comparison rust/chg/src/uihandler.rs @ 44750:c794d0da5fb2

rust-chg: reimplement uihandler by using async-trait and tokio-0.2 We no longer have to consume self and arguments. Differential Revision: https://phab.mercurial-scm.org/D8444
author Yuya Nishihara <yuya@tcha.org>
date Fri, 10 Apr 2020 22:23:10 +0900
parents e9e44e61042b
children 4b0185841058
comparison
equal deleted inserted replaced
44749:cb5822e6e545 44750:c794d0da5fb2
1 // Copyright 2018 Yuya Nishihara <yuya@tcha.org> 1 // Copyright 2018 Yuya Nishihara <yuya@tcha.org>
2 // 2 //
3 // This software may be used and distributed according to the terms of the 3 // This software may be used and distributed according to the terms of the
4 // GNU General Public License version 2 or any later version. 4 // GNU General Public License version 2 or any later version.
5 5
6 use futures::future::IntoFuture; 6 use async_trait::async_trait;
7 use futures::Future;
8 use std::io; 7 use std::io;
9 use std::os::unix::io::AsRawFd; 8 use std::os::unix::io::AsRawFd;
10 use std::os::unix::process::ExitStatusExt; 9 use std::os::unix::process::ExitStatusExt;
11 use std::process::Stdio; 10 use std::process::Stdio;
12 use tokio; 11 use tokio;
14 13
15 use crate::message::CommandSpec; 14 use crate::message::CommandSpec;
16 use crate::procutil; 15 use crate::procutil;
17 16
18 /// Callback to process shell command requests received from server. 17 /// Callback to process shell command requests received from server.
19 pub trait SystemHandler: Sized { 18 #[async_trait]
19 pub trait SystemHandler {
20 type PagerStdin: AsRawFd; 20 type PagerStdin: AsRawFd;
21 type SpawnPagerResult: IntoFuture<Item = (Self, Self::PagerStdin), Error = io::Error>;
22 type RunSystemResult: IntoFuture<Item = (Self, i32), Error = io::Error>;
23 21
24 /// Handles pager command request. 22 /// Handles pager command request.
25 /// 23 ///
26 /// Returns the pipe to be attached to the server if the pager is spawned. 24 /// Returns the pipe to be attached to the server if the pager is spawned.
27 fn spawn_pager(self, spec: CommandSpec) -> Self::SpawnPagerResult; 25 async fn spawn_pager(&mut self, spec: &CommandSpec) -> io::Result<Self::PagerStdin>;
28 26
29 /// Handles system command request. 27 /// Handles system command request.
30 /// 28 ///
31 /// Returns command exit code (positive) or signal number (negative). 29 /// Returns command exit code (positive) or signal number (negative).
32 fn run_system(self, spec: CommandSpec) -> Self::RunSystemResult; 30 async fn run_system(&mut self, spec: &CommandSpec) -> io::Result<i32>;
33 } 31 }
34 32
35 /// Default cHg implementation to process requests received from server. 33 /// Default cHg implementation to process requests received from server.
36 pub struct ChgUiHandler {} 34 pub struct ChgUiHandler {}
37 35
39 pub fn new() -> ChgUiHandler { 37 pub fn new() -> ChgUiHandler {
40 ChgUiHandler {} 38 ChgUiHandler {}
41 } 39 }
42 } 40 }
43 41
42 #[async_trait]
44 impl SystemHandler for ChgUiHandler { 43 impl SystemHandler for ChgUiHandler {
45 type PagerStdin = ChildStdin; 44 type PagerStdin = ChildStdin;
46 type SpawnPagerResult = io::Result<(Self, Self::PagerStdin)>;
47 type RunSystemResult = Box<dyn Future<Item = (Self, i32), Error = io::Error> + Send>;
48 45
49 fn spawn_pager(self, spec: CommandSpec) -> Self::SpawnPagerResult { 46 async fn spawn_pager(&mut self, spec: &CommandSpec) -> io::Result<Self::PagerStdin> {
50 let mut pager = new_shell_command(&spec).stdin(Stdio::piped()).spawn()?; 47 let mut pager = new_shell_command(&spec).stdin(Stdio::piped()).spawn()?;
51 let pin = pager.stdin.take().unwrap(); 48 let pin = pager.stdin.take().unwrap();
52 procutil::set_blocking_fd(pin.as_raw_fd())?; 49 procutil::set_blocking_fd(pin.as_raw_fd())?;
53 // TODO: if pager exits, notify the server with SIGPIPE immediately. 50 // TODO: if pager exits, notify the server with SIGPIPE immediately.
54 // otherwise the server won't get SIGPIPE if it does not write 51 // otherwise the server won't get SIGPIPE if it does not write
55 // anything. (issue5278) 52 // anything. (issue5278)
56 // kill(peerpid, SIGPIPE); 53 // kill(peerpid, SIGPIPE);
57 tokio::spawn(pager.map(|_| ()).map_err(|_| ())); // just ignore errors 54 tokio::spawn(async { pager.await }); // just ignore errors
58 Ok((self, pin)) 55 Ok(pin)
59 } 56 }
60 57
61 fn run_system(self, spec: CommandSpec) -> Self::RunSystemResult { 58 async fn run_system(&mut self, spec: &CommandSpec) -> io::Result<i32> {
62 let fut = new_shell_command(&spec) 59 let status = new_shell_command(&spec).spawn()?.await?;
63 .spawn() 60 // TODO: unindent
64 .into_future() 61 {
65 .flatten() 62 {
66 .map(|status| {
67 let code = status 63 let code = status
68 .code() 64 .code()
69 .or_else(|| status.signal().map(|n| -n)) 65 .or_else(|| status.signal().map(|n| -n))
70 .expect("either exit code or signal should be set"); 66 .expect("either exit code or signal should be set");
71 (self, code) 67 Ok(code)
72 }); 68 }
73 Box::new(fut) 69 }
74 } 70 }
75 } 71 }
76 72
77 fn new_shell_command(spec: &CommandSpec) -> Command { 73 fn new_shell_command(spec: &CommandSpec) -> Command {
78 let mut builder = Command::new("/bin/sh"); 74 let mut builder = Command::new("/bin/sh");