# HG changeset patch # User Yuya Nishihara # Date 1537780715 -32400 # Node ID a9c5fc436fd57e3241d077b36dc60799460e3263 # Parent ba447b83cd5664bc2f7dfa73a5767e89453fe1e4 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. diff -r ba447b83cd56 -r a9c5fc436fd5 rust/chg/src/lib.rs --- 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}; diff -r ba447b83cd56 -r a9c5fc436fd5 rust/chg/src/uihandler.rs --- /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 +// +// 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; + type RunSystemResult: IntoFuture; + + /// 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 + 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 + }