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