1use std::io;
4use std::path::Path;
5use std::process::Stdio;
6use tokio::process::Command;
7
8use crate::codec::ChannelMessage;
9use crate::connection::{Connection, PipeConnection};
10use crate::message::{self, ServerSpec};
11use crate::protocol::Protocol;
12use crate::runcommand::{self, UiHandler};
13
14#[derive(Debug)]
16pub struct Client<C> {
17 proto: Protocol<C>,
18 spec: ServerSpec,
19}
20
21impl<C> Client<C> {
22 fn new(proto: Protocol<C>, spec: ServerSpec) -> Self {
23 Self { proto, spec }
24 }
25
26 pub fn server_spec(&self) -> &ServerSpec {
28 &self.spec
29 }
30
31 pub fn borrow_protocol_mut(&mut self) -> &mut Protocol<C> {
35 &mut self.proto
36 }
37}
38
39impl<C> Client<C>
40where
41 C: Connection,
42{
43 pub async fn run_command(
49 &mut self,
50 handler: &mut impl UiHandler,
51 args: impl IntoIterator<Item = impl AsRef<[u8]>>,
52 ) -> io::Result<i32> {
53 runcommand::run_command(
54 self.borrow_protocol_mut(),
55 handler,
56 message::pack_args(args),
57 )
58 .await
59 }
60}
61
62pub type PipeClient = Client<PipeConnection>;
64
65impl PipeClient {
66 pub async fn spawn_at(dir: impl AsRef<Path>) -> io::Result<Self> {
68 let mut command = Command::new("hg");
69 command
70 .args(&[
71 "serve",
72 "--cmdserver",
73 "pipe",
74 "--config",
75 "ui.interactive=True",
76 ])
77 .current_dir(dir)
78 .env("HGPLAIN", "1");
79 PipeClient::spawn_with(&mut command).await
80 }
81
82 pub async fn spawn_with(command: &mut Command) -> io::Result<Self> {
84 let child = command
85 .stdin(Stdio::piped())
86 .stdout(Stdio::piped())
87 .spawn()?;
88 let mut proto = Protocol::new(PipeConnection::new(child));
89 let spec = read_hello(&mut proto).await?;
90 Ok(Self::new(proto, spec))
91 }
92
93 }
95
96#[cfg(unix)]
97pub use self::unix::UnixClient;
98
99#[cfg(unix)]
100mod unix {
101 use std::ffi::OsStr;
102 use std::os::unix::io::{AsRawFd, RawFd};
103 use tokio::net::UnixStream;
104
105 use super::*;
106 use crate::connection::UnixConnection;
107
108 impl<C> Client<C>
109 where
110 C: Connection,
111 {
112 pub async fn run_command_os(
114 &mut self,
115 handler: &mut impl UiHandler,
116 args: impl IntoIterator<Item = impl AsRef<OsStr>>,
117 ) -> io::Result<i32> {
118 runcommand::run_command(
119 self.borrow_protocol_mut(),
120 handler,
121 message::pack_args_os(args),
122 )
123 .await
124 }
125 }
126
127 impl<C> AsRawFd for Client<C>
128 where
129 C: AsRawFd,
130 {
131 fn as_raw_fd(&self) -> RawFd {
132 self.proto.as_raw_fd()
133 }
134 }
135
136 pub type UnixClient = Client<UnixConnection>;
138
139 impl UnixClient {
140 pub async fn connect(path: impl AsRef<Path>) -> io::Result<Self> {
142 let stream = UnixStream::connect(path).await?;
143 let mut proto = Protocol::new(UnixConnection::new(stream));
144 let spec = read_hello(&mut proto).await?;
145 Ok(Self::new(proto, spec))
146 }
147 }
148}
149
150async fn read_hello(proto: &mut Protocol<impl Connection>) -> io::Result<ServerSpec> {
151 match proto.fetch_response().await? {
152 ChannelMessage::Data(b'o', data) => message::parse_hello(data),
153 _ => Err(io::Error::new(
154 io::ErrorKind::InvalidData,
155 "no hello message received",
156 )),
157 }
158}