Mercurial > hg
changeset 39974:a9c5fc436fd5
rust-chg: add callback to handle pager and shell command requests
This could be inlined into the ChgRunCommand state to be introduced by
the next patch, but it seemed good to separate any user interactions from
the IPC code.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Mon, 24 Sep 2018 18:18:35 +0900 |
parents | ba447b83cd56 |
children | 571d8eb39095 |
files | rust/chg/src/lib.rs rust/chg/src/uihandler.rs |
diffstat | 2 files changed, 87 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/rust/chg/src/lib.rs Sat Sep 29 21:59:07 2018 +0900 +++ b/rust/chg/src/lib.rs Mon Sep 24 18:18:35 2018 +0900 @@ -9,7 +9,11 @@ extern crate libc; extern crate tokio; extern crate tokio_hglib; +extern crate tokio_process; pub mod attachio; pub mod message; pub mod procutil; +mod uihandler; + +pub use uihandler::{ChgUiHandler, SystemHandler};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/chg/src/uihandler.rs Mon Sep 24 18:18:35 2018 +0900 @@ -0,0 +1,83 @@ +// Copyright 2018 Yuya Nishihara <yuya@tcha.org> +// +// This software may be used and distributed according to the terms of the +// GNU General Public License version 2 or any later version. + +use futures::Future; +use futures::future::IntoFuture; +use std::io; +use std::os::unix::io::AsRawFd; +use std::os::unix::process::ExitStatusExt; +use std::process::{Command, Stdio}; +use tokio; +use tokio_process::{ChildStdin, CommandExt}; + +use super::message::CommandSpec; +use super::procutil; + +/// Callback to process shell command requests received from server. +pub trait SystemHandler: Sized { + type PagerStdin: AsRawFd; + type SpawnPagerResult: IntoFuture<Item = (Self, Self::PagerStdin), Error = io::Error>; + type RunSystemResult: IntoFuture<Item = (Self, i32), Error = io::Error>; + + /// Handles pager command request. + /// + /// Returns the pipe to be attached to the server if the pager is spawned. + fn spawn_pager(self, spec: CommandSpec) -> Self::SpawnPagerResult; + + /// Handles system command request. + /// + /// Returns command exit code (positive) or signal number (negative). + fn run_system(self, spec: CommandSpec) -> Self::RunSystemResult; +} + +/// Default cHg implementation to process requests received from server. +pub struct ChgUiHandler { +} + +impl ChgUiHandler { + pub fn new() -> ChgUiHandler { + ChgUiHandler {} + } +} + +impl SystemHandler for ChgUiHandler { + type PagerStdin = ChildStdin; + type SpawnPagerResult = io::Result<(Self, Self::PagerStdin)>; + type RunSystemResult = Box<dyn Future<Item = (Self, i32), Error = io::Error> + Send>; + + fn spawn_pager(self, spec: CommandSpec) -> Self::SpawnPagerResult { + let mut pager = new_shell_command(&spec) + .stdin(Stdio::piped()) + .spawn_async()?; + let pin = pager.stdin().take().unwrap(); + procutil::set_blocking_fd(pin.as_raw_fd())?; + tokio::spawn(pager.map(|_| ()).map_err(|_| ())); // just ignore errors + Ok((self, pin)) + } + + fn run_system(self, spec: CommandSpec) -> Self::RunSystemResult { + let fut = new_shell_command(&spec) + .spawn_async() + .into_future() + .flatten() + .map(|status| { + let code = status.code().or_else(|| status.signal().map(|n| -n)) + .expect("either exit code or signal should be set"); + (self, code) + }); + Box::new(fut) + } +} + +fn new_shell_command(spec: &CommandSpec) -> Command { + let mut builder = Command::new("/bin/sh"); + builder + .arg("-c") + .arg(&spec.command) + .current_dir(&spec.current_dir) + .env_clear() + .envs(spec.envs.iter().cloned()); + builder + }