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