1use std::ffi::OsString;
17use std::io::{self, Read, Write};
18use std::path::Path;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct PtySize {
25 pub rows: u16,
26 pub cols: u16,
27 pub pixel_width: u16,
28 pub pixel_height: u16,
29}
30
31pub trait PtyMaster: Send + 'static {
32 fn try_clone_reader(&mut self) -> io::Result<Box<dyn Read + Send>>;
33 fn take_writer(&mut self) -> io::Result<Box<dyn Write + Send>>;
34 fn resize(&self, size: PtySize) -> io::Result<()>;
35 fn get_size(&self) -> io::Result<PtySize>;
40 #[cfg(unix)]
44 fn process_group_leader(&self) -> Option<i32>;
45}
46
47pub trait PtyChild: Send + 'static {
48 fn pid(&self) -> u32;
49 fn try_wait(&mut self) -> io::Result<Option<u32>>;
54 fn wait(&mut self) -> io::Result<u32>;
56 fn kill(&mut self) -> io::Result<()>;
57 #[cfg(windows)]
62 fn as_raw_handle(&self) -> Option<std::os::windows::io::RawHandle>;
63}
64
65pub trait PtySlave: Send + 'static {
66 type Child: PtyChild;
67 fn spawn(
68 self,
69 argv: &[OsString],
70 cwd: Option<&Path>,
71 env: Option<&[(OsString, OsString)]>,
72 ) -> io::Result<Self::Child>;
73}
74
75pub trait PtyBackend {
76 type Master: PtyMaster;
77 type Slave: PtySlave;
78 fn openpty(size: PtySize) -> io::Result<(Self::Master, Self::Slave)>;
79}
80
81#[cfg(windows)]
82mod conpty {
83 use super::*;
84 use crate::pty::conpty_passthrough;
85
86 pub(crate) struct ConPtyBackend;
87
88 impl PtyBackend for ConPtyBackend {
89 type Master = conpty_passthrough::ConPtyMaster;
90 type Slave = conpty_passthrough::ConPtySlave;
91
92 fn openpty(size: PtySize) -> io::Result<(Self::Master, Self::Slave)> {
93 let pair = conpty_passthrough::openpty(conpty_passthrough::PtySize {
94 rows: size.rows,
95 cols: size.cols,
96 pixel_width: size.pixel_width,
97 pixel_height: size.pixel_height,
98 })?;
99 Ok((pair.master, pair.slave))
100 }
101 }
102
103 impl PtyMaster for conpty_passthrough::ConPtyMaster {
104 fn try_clone_reader(&mut self) -> io::Result<Box<dyn Read + Send>> {
105 conpty_passthrough::ConPtyMaster::try_clone_reader(self)
106 }
107 fn take_writer(&mut self) -> io::Result<Box<dyn Write + Send>> {
108 conpty_passthrough::ConPtyMaster::take_writer(self)
109 }
110 fn resize(&self, size: PtySize) -> io::Result<()> {
111 conpty_passthrough::ConPtyMaster::resize(
112 self,
113 conpty_passthrough::PtySize {
114 rows: size.rows,
115 cols: size.cols,
116 pixel_width: size.pixel_width,
117 pixel_height: size.pixel_height,
118 },
119 )
120 }
121 fn get_size(&self) -> io::Result<PtySize> {
122 let s = conpty_passthrough::ConPtyMaster::get_size(self);
123 Ok(PtySize {
124 rows: s.rows,
125 cols: s.cols,
126 pixel_width: s.pixel_width,
127 pixel_height: s.pixel_height,
128 })
129 }
130 }
131
132 impl PtySlave for conpty_passthrough::ConPtySlave {
133 type Child = conpty_passthrough::child::ConPtyChild;
134 fn spawn(
135 self,
136 argv: &[OsString],
137 cwd: Option<&Path>,
138 env: Option<&[(OsString, OsString)]>,
139 ) -> io::Result<Self::Child> {
140 conpty_passthrough::ConPtySlave::spawn(self, argv, cwd, env)
141 }
142 }
143
144 impl PtyChild for conpty_passthrough::child::ConPtyChild {
145 fn pid(&self) -> u32 {
146 conpty_passthrough::child::ConPtyChild::pid(self)
147 }
148 fn try_wait(&mut self) -> io::Result<Option<u32>> {
149 conpty_passthrough::child::ConPtyChild::try_wait(self)
150 }
151 fn wait(&mut self) -> io::Result<u32> {
152 conpty_passthrough::child::ConPtyChild::wait(self)
153 }
154 fn kill(&mut self) -> io::Result<()> {
155 conpty_passthrough::child::ConPtyChild::kill(self)
156 }
157 fn as_raw_handle(&self) -> Option<std::os::windows::io::RawHandle> {
158 Some(conpty_passthrough::child::ConPtyChild::as_raw_handle(self))
159 }
160 }
161}
162
163#[cfg(unix)]
164mod unix {
165 use super::*;
166 use portable_pty::{
167 Child as PortableChild, CommandBuilder, MasterPty, PtySize as PortPtySize, SlavePty,
168 native_pty_system,
169 };
170
171 pub(crate) struct PortablePtyBackend;
172
173 pub(crate) struct PortablePtyMaster(Box<dyn MasterPty + Send>);
174 pub(crate) struct PortablePtySlave(Box<dyn SlavePty + Send>);
175 pub(crate) struct PortablePtyChild(Box<dyn PortableChild + Send + Sync>);
176
177 impl PtyBackend for PortablePtyBackend {
178 type Master = PortablePtyMaster;
179 type Slave = PortablePtySlave;
180
181 fn openpty(size: PtySize) -> io::Result<(Self::Master, Self::Slave)> {
182 let sys = native_pty_system();
183 let pair = sys
184 .openpty(PortPtySize {
185 rows: size.rows,
186 cols: size.cols,
187 pixel_width: size.pixel_width,
188 pixel_height: size.pixel_height,
189 })
190 .map_err(io::Error::other)?;
191 Ok((PortablePtyMaster(pair.master), PortablePtySlave(pair.slave)))
192 }
193 }
194
195 impl PtyMaster for PortablePtyMaster {
196 fn try_clone_reader(&mut self) -> io::Result<Box<dyn Read + Send>> {
197 self.0.try_clone_reader().map_err(io::Error::other)
198 }
199 fn take_writer(&mut self) -> io::Result<Box<dyn Write + Send>> {
200 self.0.take_writer().map_err(io::Error::other)
201 }
202 fn resize(&self, size: PtySize) -> io::Result<()> {
203 self.0
204 .resize(PortPtySize {
205 rows: size.rows,
206 cols: size.cols,
207 pixel_width: size.pixel_width,
208 pixel_height: size.pixel_height,
209 })
210 .map_err(io::Error::other)
211 }
212 fn get_size(&self) -> io::Result<PtySize> {
213 let s = self.0.get_size().map_err(io::Error::other)?;
214 Ok(PtySize {
215 rows: s.rows,
216 cols: s.cols,
217 pixel_width: s.pixel_width,
218 pixel_height: s.pixel_height,
219 })
220 }
221 fn process_group_leader(&self) -> Option<i32> {
222 self.0.process_group_leader()
223 }
224 }
225
226 impl PtySlave for PortablePtySlave {
227 type Child = PortablePtyChild;
228 fn spawn(
229 self,
230 argv: &[OsString],
231 cwd: Option<&Path>,
232 env: Option<&[(OsString, OsString)]>,
233 ) -> io::Result<Self::Child> {
234 if argv.is_empty() {
235 return Err(io::Error::other("portable-pty spawn requires non-empty argv"));
236 }
237 let mut cmd = CommandBuilder::new(&argv[0]);
238 for arg in &argv[1..] {
239 cmd.arg(arg);
240 }
241 if let Some(cwd) = cwd {
242 cmd.cwd(cwd);
243 }
244 if let Some(env) = env {
245 cmd.env_clear();
246 for (k, v) in env {
247 cmd.env(k, v);
248 }
249 }
250 let child = self.0.spawn_command(cmd).map_err(io::Error::other)?;
251 Ok(PortablePtyChild(child))
252 }
253 }
254
255 impl PtyChild for PortablePtyChild {
256 fn pid(&self) -> u32 {
257 self.0.process_id().unwrap_or(0)
258 }
259 fn try_wait(&mut self) -> io::Result<Option<u32>> {
260 match self.0.try_wait()? {
261 Some(status) => Ok(Some(portable_pty_exit_code(status))),
262 None => Ok(None),
263 }
264 }
265 fn wait(&mut self) -> io::Result<u32> {
266 let status = self.0.wait()?;
267 Ok(portable_pty_exit_code(status))
268 }
269 fn kill(&mut self) -> io::Result<()> {
270 self.0.kill()
271 }
272 }
273
274 fn portable_pty_exit_code(status: portable_pty::ExitStatus) -> u32 {
278 status.exit_code()
283 }
284}
285
286#[cfg(windows)]
289pub(crate) type Backend = conpty::ConPtyBackend;
290#[cfg(unix)]
291pub(crate) type Backend = unix::PortablePtyBackend;
292
293#[cfg(windows)]
297mod unix_compat {
298 use super::*;
299 use portable_pty::{
300 Child as PortableChild, CommandBuilder, MasterPty, PtySize as PortPtySize, SlavePty,
301 native_pty_system,
302 };
303
304 pub(crate) struct PortablePtyBackend;
305 pub(crate) struct PortablePtyMaster(Box<dyn MasterPty + Send>);
306 pub(crate) struct PortablePtySlave(Box<dyn SlavePty + Send>);
307 pub(crate) struct PortablePtyChild(Box<dyn PortableChild + Send + Sync>);
308
309 impl PtyBackend for PortablePtyBackend {
310 type Master = PortablePtyMaster;
311 type Slave = PortablePtySlave;
312 fn openpty(size: PtySize) -> io::Result<(Self::Master, Self::Slave)> {
313 let sys = native_pty_system();
314 let pair = sys
315 .openpty(PortPtySize {
316 rows: size.rows,
317 cols: size.cols,
318 pixel_width: size.pixel_width,
319 pixel_height: size.pixel_height,
320 })
321 .map_err(io::Error::other)?;
322 Ok((PortablePtyMaster(pair.master), PortablePtySlave(pair.slave)))
323 }
324 }
325
326 impl PtyMaster for PortablePtyMaster {
327 fn try_clone_reader(&mut self) -> io::Result<Box<dyn Read + Send>> {
328 self.0.try_clone_reader().map_err(io::Error::other)
329 }
330 fn take_writer(&mut self) -> io::Result<Box<dyn Write + Send>> {
331 self.0.take_writer().map_err(io::Error::other)
332 }
333 fn resize(&self, size: PtySize) -> io::Result<()> {
334 self.0
335 .resize(PortPtySize {
336 rows: size.rows,
337 cols: size.cols,
338 pixel_width: size.pixel_width,
339 pixel_height: size.pixel_height,
340 })
341 .map_err(io::Error::other)
342 }
343 fn get_size(&self) -> io::Result<PtySize> {
344 let s = self.0.get_size().map_err(io::Error::other)?;
345 Ok(PtySize {
346 rows: s.rows,
347 cols: s.cols,
348 pixel_width: s.pixel_width,
349 pixel_height: s.pixel_height,
350 })
351 }
352 }
353
354 impl PtySlave for PortablePtySlave {
355 type Child = PortablePtyChild;
356 fn spawn(
357 self,
358 argv: &[OsString],
359 cwd: Option<&Path>,
360 env: Option<&[(OsString, OsString)]>,
361 ) -> io::Result<Self::Child> {
362 if argv.is_empty() {
363 return Err(io::Error::other("portable-pty spawn requires non-empty argv"));
364 }
365 let mut cmd = CommandBuilder::new(&argv[0]);
366 for arg in &argv[1..] {
367 cmd.arg(arg);
368 }
369 if let Some(cwd) = cwd {
370 cmd.cwd(cwd);
371 }
372 if let Some(env) = env {
373 cmd.env_clear();
374 for (k, v) in env {
375 cmd.env(k, v);
376 }
377 }
378 let child = self.0.spawn_command(cmd).map_err(io::Error::other)?;
379 Ok(PortablePtyChild(child))
380 }
381 }
382
383 impl PtyChild for PortablePtyChild {
384 fn pid(&self) -> u32 {
385 self.0.process_id().unwrap_or(0)
386 }
387 fn try_wait(&mut self) -> io::Result<Option<u32>> {
388 match self.0.try_wait()? {
389 Some(status) => Ok(Some(status.exit_code())),
390 None => Ok(None),
391 }
392 }
393 fn wait(&mut self) -> io::Result<u32> {
394 let status = self.0.wait()?;
395 Ok(status.exit_code())
396 }
397 fn kill(&mut self) -> io::Result<()> {
398 self.0.kill()
399 }
400 fn as_raw_handle(&self) -> Option<std::os::windows::io::RawHandle> {
401 self.0.as_raw_handle()
402 }
403 }
404}