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)]
297#[allow(dead_code)]
298mod unix_compat {
299 use super::*;
300 use portable_pty::{
301 Child as PortableChild, CommandBuilder, MasterPty, PtySize as PortPtySize, SlavePty,
302 native_pty_system,
303 };
304
305 pub(crate) struct PortablePtyBackend;
306 pub(crate) struct PortablePtyMaster(Box<dyn MasterPty + Send>);
307 pub(crate) struct PortablePtySlave(Box<dyn SlavePty + Send>);
308 pub(crate) struct PortablePtyChild(Box<dyn PortableChild + Send + Sync>);
309
310 impl PtyBackend for PortablePtyBackend {
311 type Master = PortablePtyMaster;
312 type Slave = PortablePtySlave;
313 fn openpty(size: PtySize) -> io::Result<(Self::Master, Self::Slave)> {
314 let sys = native_pty_system();
315 let pair = sys
316 .openpty(PortPtySize {
317 rows: size.rows,
318 cols: size.cols,
319 pixel_width: size.pixel_width,
320 pixel_height: size.pixel_height,
321 })
322 .map_err(io::Error::other)?;
323 Ok((PortablePtyMaster(pair.master), PortablePtySlave(pair.slave)))
324 }
325 }
326
327 impl PtyMaster for PortablePtyMaster {
328 fn try_clone_reader(&mut self) -> io::Result<Box<dyn Read + Send>> {
329 self.0.try_clone_reader().map_err(io::Error::other)
330 }
331 fn take_writer(&mut self) -> io::Result<Box<dyn Write + Send>> {
332 self.0.take_writer().map_err(io::Error::other)
333 }
334 fn resize(&self, size: PtySize) -> io::Result<()> {
335 self.0
336 .resize(PortPtySize {
337 rows: size.rows,
338 cols: size.cols,
339 pixel_width: size.pixel_width,
340 pixel_height: size.pixel_height,
341 })
342 .map_err(io::Error::other)
343 }
344 fn get_size(&self) -> io::Result<PtySize> {
345 let s = self.0.get_size().map_err(io::Error::other)?;
346 Ok(PtySize {
347 rows: s.rows,
348 cols: s.cols,
349 pixel_width: s.pixel_width,
350 pixel_height: s.pixel_height,
351 })
352 }
353 }
354
355 impl PtySlave for PortablePtySlave {
356 type Child = PortablePtyChild;
357 fn spawn(
358 self,
359 argv: &[OsString],
360 cwd: Option<&Path>,
361 env: Option<&[(OsString, OsString)]>,
362 ) -> io::Result<Self::Child> {
363 if argv.is_empty() {
364 return Err(io::Error::other("portable-pty spawn requires non-empty argv"));
365 }
366 let mut cmd = CommandBuilder::new(&argv[0]);
367 for arg in &argv[1..] {
368 cmd.arg(arg);
369 }
370 if let Some(cwd) = cwd {
371 cmd.cwd(cwd);
372 }
373 if let Some(env) = env {
374 cmd.env_clear();
375 for (k, v) in env {
376 cmd.env(k, v);
377 }
378 }
379 let child = self.0.spawn_command(cmd).map_err(io::Error::other)?;
380 Ok(PortablePtyChild(child))
381 }
382 }
383
384 impl PtyChild for PortablePtyChild {
385 fn pid(&self) -> u32 {
386 self.0.process_id().unwrap_or(0)
387 }
388 fn try_wait(&mut self) -> io::Result<Option<u32>> {
389 match self.0.try_wait()? {
390 Some(status) => Ok(Some(status.exit_code())),
391 None => Ok(None),
392 }
393 }
394 fn wait(&mut self) -> io::Result<u32> {
395 let status = self.0.wait()?;
396 Ok(status.exit_code())
397 }
398 fn kill(&mut self) -> io::Result<()> {
399 self.0.kill()
400 }
401 fn as_raw_handle(&self) -> Option<std::os::windows::io::RawHandle> {
402 self.0.as_raw_handle()
403 }
404 }
405}