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,
27 pub cols: u16,
29 pub pixel_width: u16,
31 pub pixel_height: u16,
33}
34
35pub trait PtyMaster: Send + 'static {
37 fn try_clone_reader(&mut self) -> io::Result<Box<dyn Read + Send>>;
39 fn take_writer(&mut self) -> io::Result<Box<dyn Write + Send>>;
41 fn resize(&self, size: PtySize) -> io::Result<()>;
43 fn get_size(&self) -> io::Result<PtySize>;
48 #[cfg(unix)]
52 fn process_group_leader(&self) -> Option<i32>;
53 #[cfg(unix)]
55 fn as_raw_fd(&self) -> Option<std::os::fd::RawFd>;
56}
57
58pub trait PtyChild: Send + 'static {
60 fn pid(&self) -> u32;
62 fn try_wait(&mut self) -> io::Result<Option<u32>>;
67 fn wait(&mut self) -> io::Result<u32>;
69 fn kill(&mut self) -> io::Result<()>;
71 #[cfg(windows)]
76 fn as_raw_handle(&self) -> Option<std::os::windows::io::RawHandle>;
77}
78
79pub trait PtySlave: Send + 'static {
81 type Child: PtyChild;
83 fn spawn(
85 self,
86 argv: &[OsString],
87 cwd: Option<&Path>,
88 env: Option<&[(OsString, OsString)]>,
89 ) -> io::Result<Self::Child>;
90}
91
92pub trait PtyBackend {
94 type Master: PtyMaster;
96 type Slave: PtySlave;
98 fn openpty(size: PtySize) -> io::Result<(Self::Master, Self::Slave)>;
100}
101
102#[cfg(windows)]
103mod conpty {
104 use super::*;
105 use crate::pty::conpty_passthrough;
106
107 pub(crate) struct ConPtyBackend;
108
109 impl PtyBackend for ConPtyBackend {
110 type Master = conpty_passthrough::ConPtyMaster;
111 type Slave = conpty_passthrough::ConPtySlave;
112
113 fn openpty(size: PtySize) -> io::Result<(Self::Master, Self::Slave)> {
114 let pair = conpty_passthrough::openpty(conpty_passthrough::PtySize {
115 rows: size.rows,
116 cols: size.cols,
117 pixel_width: size.pixel_width,
118 pixel_height: size.pixel_height,
119 })?;
120 Ok((pair.master, pair.slave))
121 }
122 }
123
124 impl PtyMaster for conpty_passthrough::ConPtyMaster {
125 fn try_clone_reader(&mut self) -> io::Result<Box<dyn Read + Send>> {
126 conpty_passthrough::ConPtyMaster::try_clone_reader(self)
127 }
128 fn take_writer(&mut self) -> io::Result<Box<dyn Write + Send>> {
129 conpty_passthrough::ConPtyMaster::take_writer(self)
130 }
131 fn resize(&self, size: PtySize) -> io::Result<()> {
132 conpty_passthrough::ConPtyMaster::resize(
133 self,
134 conpty_passthrough::PtySize {
135 rows: size.rows,
136 cols: size.cols,
137 pixel_width: size.pixel_width,
138 pixel_height: size.pixel_height,
139 },
140 )
141 }
142 fn get_size(&self) -> io::Result<PtySize> {
143 let s = conpty_passthrough::ConPtyMaster::get_size(self);
144 Ok(PtySize {
145 rows: s.rows,
146 cols: s.cols,
147 pixel_width: s.pixel_width,
148 pixel_height: s.pixel_height,
149 })
150 }
151 }
152
153 impl PtySlave for conpty_passthrough::ConPtySlave {
154 type Child = conpty_passthrough::child::ConPtyChild;
155 fn spawn(
156 self,
157 argv: &[OsString],
158 cwd: Option<&Path>,
159 env: Option<&[(OsString, OsString)]>,
160 ) -> io::Result<Self::Child> {
161 conpty_passthrough::ConPtySlave::spawn(self, argv, cwd, env)
162 }
163 }
164
165 impl PtyChild for conpty_passthrough::child::ConPtyChild {
166 fn pid(&self) -> u32 {
167 conpty_passthrough::child::ConPtyChild::pid(self)
168 }
169 fn try_wait(&mut self) -> io::Result<Option<u32>> {
170 conpty_passthrough::child::ConPtyChild::try_wait(self)
171 }
172 fn wait(&mut self) -> io::Result<u32> {
173 conpty_passthrough::child::ConPtyChild::wait(self)
174 }
175 fn kill(&mut self) -> io::Result<()> {
176 conpty_passthrough::child::ConPtyChild::kill(self)
177 }
178 fn as_raw_handle(&self) -> Option<std::os::windows::io::RawHandle> {
179 Some(conpty_passthrough::child::ConPtyChild::as_raw_handle(self))
180 }
181 }
182}
183
184#[cfg(unix)]
185mod unix {
186 use super::*;
187 use portable_pty::{
188 native_pty_system, Child as PortableChild, CommandBuilder, MasterPty,
189 PtySize as PortPtySize, SlavePty,
190 };
191
192 pub(crate) struct PortablePtyBackend;
193
194 pub(crate) struct PortablePtyMaster(Box<dyn MasterPty + Send>);
195 pub(crate) struct PortablePtySlave(Box<dyn SlavePty + Send>);
196 pub(crate) struct PortablePtyChild(Box<dyn PortableChild + Send + Sync>);
197
198 impl PtyBackend for PortablePtyBackend {
199 type Master = PortablePtyMaster;
200 type Slave = PortablePtySlave;
201
202 fn openpty(size: PtySize) -> io::Result<(Self::Master, Self::Slave)> {
203 let sys = native_pty_system();
204 let pair = sys
205 .openpty(PortPtySize {
206 rows: size.rows,
207 cols: size.cols,
208 pixel_width: size.pixel_width,
209 pixel_height: size.pixel_height,
210 })
211 .map_err(io::Error::other)?;
212 Ok((PortablePtyMaster(pair.master), PortablePtySlave(pair.slave)))
213 }
214 }
215
216 impl PtyMaster for PortablePtyMaster {
217 fn try_clone_reader(&mut self) -> io::Result<Box<dyn Read + Send>> {
218 self.0.try_clone_reader().map_err(io::Error::other)
219 }
220 fn take_writer(&mut self) -> io::Result<Box<dyn Write + Send>> {
221 self.0.take_writer().map_err(io::Error::other)
222 }
223 fn resize(&self, size: PtySize) -> io::Result<()> {
224 self.0
225 .resize(PortPtySize {
226 rows: size.rows,
227 cols: size.cols,
228 pixel_width: size.pixel_width,
229 pixel_height: size.pixel_height,
230 })
231 .map_err(io::Error::other)
232 }
233 fn get_size(&self) -> io::Result<PtySize> {
234 let s = self.0.get_size().map_err(io::Error::other)?;
235 Ok(PtySize {
236 rows: s.rows,
237 cols: s.cols,
238 pixel_width: s.pixel_width,
239 pixel_height: s.pixel_height,
240 })
241 }
242 fn process_group_leader(&self) -> Option<i32> {
243 self.0.process_group_leader()
244 }
245 fn as_raw_fd(&self) -> Option<std::os::fd::RawFd> {
246 self.0.as_raw_fd()
247 }
248 }
249
250 impl PtySlave for PortablePtySlave {
251 type Child = PortablePtyChild;
252 fn spawn(
253 self,
254 argv: &[OsString],
255 cwd: Option<&Path>,
256 env: Option<&[(OsString, OsString)]>,
257 ) -> io::Result<Self::Child> {
258 if argv.is_empty() {
259 return Err(io::Error::other(
260 "portable-pty spawn requires non-empty argv",
261 ));
262 }
263 let mut cmd = CommandBuilder::new(&argv[0]);
264 for arg in &argv[1..] {
265 cmd.arg(arg);
266 }
267 if let Some(cwd) = cwd {
268 cmd.cwd(cwd);
269 }
270 if let Some(env) = env {
271 cmd.env_clear();
272 for (k, v) in env {
273 cmd.env(k, v);
274 }
275 }
276 let child = self.0.spawn_command(cmd).map_err(io::Error::other)?;
277 Ok(PortablePtyChild(child))
278 }
279 }
280
281 impl PtyChild for PortablePtyChild {
282 fn pid(&self) -> u32 {
283 self.0.process_id().unwrap_or(0)
284 }
285 fn try_wait(&mut self) -> io::Result<Option<u32>> {
286 match self.0.try_wait()? {
287 Some(status) => Ok(Some(portable_pty_exit_code(status))),
288 None => Ok(None),
289 }
290 }
291 fn wait(&mut self) -> io::Result<u32> {
292 let status = self.0.wait()?;
293 Ok(portable_pty_exit_code(status))
294 }
295 fn kill(&mut self) -> io::Result<()> {
296 self.0.kill()
297 }
298 }
299
300 fn portable_pty_exit_code(status: portable_pty::ExitStatus) -> u32 {
304 status.exit_code()
309 }
310}
311
312#[cfg(windows)]
315pub(crate) type Backend = conpty::ConPtyBackend;
316#[cfg(unix)]
317pub(crate) type Backend = unix::PortablePtyBackend;
318
319#[cfg(windows)]
323#[allow(dead_code)]
324mod unix_compat {
325 use super::*;
326 use portable_pty::{
327 native_pty_system, Child as PortableChild, CommandBuilder, MasterPty,
328 PtySize as PortPtySize, SlavePty,
329 };
330
331 pub(crate) struct PortablePtyBackend;
332 pub(crate) struct PortablePtyMaster(Box<dyn MasterPty + Send>);
333 pub(crate) struct PortablePtySlave(Box<dyn SlavePty + Send>);
334 pub(crate) struct PortablePtyChild(Box<dyn PortableChild + Send + Sync>);
335
336 impl PtyBackend for PortablePtyBackend {
337 type Master = PortablePtyMaster;
338 type Slave = PortablePtySlave;
339 fn openpty(size: PtySize) -> io::Result<(Self::Master, Self::Slave)> {
340 let sys = native_pty_system();
341 let pair = sys
342 .openpty(PortPtySize {
343 rows: size.rows,
344 cols: size.cols,
345 pixel_width: size.pixel_width,
346 pixel_height: size.pixel_height,
347 })
348 .map_err(io::Error::other)?;
349 Ok((PortablePtyMaster(pair.master), PortablePtySlave(pair.slave)))
350 }
351 }
352
353 impl PtyMaster for PortablePtyMaster {
354 fn try_clone_reader(&mut self) -> io::Result<Box<dyn Read + Send>> {
355 self.0.try_clone_reader().map_err(io::Error::other)
356 }
357 fn take_writer(&mut self) -> io::Result<Box<dyn Write + Send>> {
358 self.0.take_writer().map_err(io::Error::other)
359 }
360 fn resize(&self, size: PtySize) -> io::Result<()> {
361 self.0
362 .resize(PortPtySize {
363 rows: size.rows,
364 cols: size.cols,
365 pixel_width: size.pixel_width,
366 pixel_height: size.pixel_height,
367 })
368 .map_err(io::Error::other)
369 }
370 fn get_size(&self) -> io::Result<PtySize> {
371 let s = self.0.get_size().map_err(io::Error::other)?;
372 Ok(PtySize {
373 rows: s.rows,
374 cols: s.cols,
375 pixel_width: s.pixel_width,
376 pixel_height: s.pixel_height,
377 })
378 }
379 }
380
381 impl PtySlave for PortablePtySlave {
382 type Child = PortablePtyChild;
383 fn spawn(
384 self,
385 argv: &[OsString],
386 cwd: Option<&Path>,
387 env: Option<&[(OsString, OsString)]>,
388 ) -> io::Result<Self::Child> {
389 if argv.is_empty() {
390 return Err(io::Error::other(
391 "portable-pty spawn requires non-empty argv",
392 ));
393 }
394 let mut cmd = CommandBuilder::new(&argv[0]);
395 for arg in &argv[1..] {
396 cmd.arg(arg);
397 }
398 if let Some(cwd) = cwd {
399 cmd.cwd(cwd);
400 }
401 if let Some(env) = env {
402 cmd.env_clear();
403 for (k, v) in env {
404 cmd.env(k, v);
405 }
406 }
407 let child = self.0.spawn_command(cmd).map_err(io::Error::other)?;
408 Ok(PortablePtyChild(child))
409 }
410 }
411
412 impl PtyChild for PortablePtyChild {
413 fn pid(&self) -> u32 {
414 self.0.process_id().unwrap_or(0)
415 }
416 fn try_wait(&mut self) -> io::Result<Option<u32>> {
417 match self.0.try_wait()? {
418 Some(status) => Ok(Some(status.exit_code())),
419 None => Ok(None),
420 }
421 }
422 fn wait(&mut self) -> io::Result<u32> {
423 let status = self.0.wait()?;
424 Ok(status.exit_code())
425 }
426 fn kill(&mut self) -> io::Result<()> {
427 self.0.kill()
428 }
429 fn as_raw_handle(&self) -> Option<std::os::windows::io::RawHandle> {
430 self.0.as_raw_handle()
431 }
432 }
433}