1 // Copyright 2018 Yuya Nishihara <yuya@tcha.org>
2 //
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.
5 
6 //! Functions to run Mercurial command in cHg-aware command server.
7 
8 use bytes::Bytes;
9 use std::io;
10 use std::os::unix::io::AsRawFd;
11 use tokio_hglib::codec::ChannelMessage;
12 use tokio_hglib::{Connection, Protocol};
13 
14 use crate::attachio;
15 use crate::message::{self, CommandType};
16 use crate::uihandler::SystemHandler;
17 
18 /// Runs the given Mercurial command in cHg-aware command server, and
19 /// fetches the result code.
20 ///
21 /// This is a subset of tokio-hglib's `run_command()` with the additional
22 /// SystemRequest support.
run_command( proto: &mut Protocol<impl Connection + AsRawFd>, handler: &mut impl SystemHandler, packed_args: impl Into<Bytes>, ) -> io::Result<i32>23 pub async fn run_command(
24     proto: &mut Protocol<impl Connection + AsRawFd>,
25     handler: &mut impl SystemHandler,
26     packed_args: impl Into<Bytes>,
27 ) -> io::Result<i32> {
28     proto
29         .send_command_with_args("runcommand", packed_args)
30         .await?;
31     loop {
32         match proto.fetch_response().await? {
33             ChannelMessage::Data(b'r', data) => {
34                 return message::parse_result_code(data);
35             }
36             ChannelMessage::Data(..) => {
37                 // just ignores data sent to optional channel
38             }
39             ChannelMessage::InputRequest(..)
40             | ChannelMessage::LineRequest(..) => {
41                 return Err(io::Error::new(
42                     io::ErrorKind::InvalidData,
43                     "unsupported request",
44                 ));
45             }
46             ChannelMessage::SystemRequest(data) => {
47                 let (cmd_type, cmd_spec) = message::parse_command_spec(data)?;
48                 match cmd_type {
49                     CommandType::Pager => {
50                         // server spins new command loop while pager request is
51                         // in progress, which can be terminated by "" command.
52                         let pin = handler.spawn_pager(&cmd_spec).await?;
53                         attachio::attach_io(proto, &io::stdin(), &pin, &pin)
54                             .await?;
55                         proto.send_command("").await?; // terminator
56                     }
57                     CommandType::System => {
58                         let code = handler.run_system(&cmd_spec).await?;
59                         let data = message::pack_result_code(code);
60                         proto.send_data(data).await?;
61                     }
62                 }
63             }
64         }
65     }
66 }
67