1pub mod error;
22mod unix;
23
24pub use error::PtyError;
25use std::error::Error;
26use std::os::fd::{FromRawFd, AsRawFd, RawFd};
27use crate::unix::window::WindowSize;
28
29pub struct Pty {
33 pid: RawFd
34}
35
36impl Pty {
37 pub fn spawn<F, G>(on_read: F, on_death: G) -> Result<Pty, Box<dyn Error>>
41 where
42 F: FnMut(RawFd, Result<String, Box<dyn Error>>) + Send + 'static,
43 G: FnMut(RawFd) + Send + 'static
44 {
45 let master = unix::pty::spawn()?;
46 unix::pty::poll(master, on_read, on_death)?;
47
48 Ok(Pty { pid: master })
49 }
50
51 pub fn write(&self, s: &str) -> Result<(), Box<dyn Error>> {
53 unix::pty::write(self.pid, s.as_bytes())
54 }
55
56 pub fn resize(&self, window_size: WindowSize) -> Result<(), Box<dyn Error>> {
58 unix::pty::resize(self.pid, window_size)
59 }
60
61 pub fn kill(&self) {
63 unix::pty::kill(self.pid)
64 }
65}
66
67impl FromRawFd for Pty {
68 unsafe fn from_raw_fd(fd: RawFd) -> Self {
69 Pty { pid: fd }
70 }
71}
72
73impl AsRawFd for Pty {
74 fn as_raw_fd(&self) -> RawFd {
75 self.pid
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use std::time::Duration;
82 use std::sync::{Arc, Mutex};
83 use super::*;
84
85 #[test]
86 fn spawn() -> Result<(), Box<dyn Error>> {
87 let read_buf = Arc::new(Mutex::new(String::new()));
88 let die_buf = Arc::new(Mutex::new(String::new()));
89
90 let (read_buf_async, die_buf_async) = (read_buf.clone(), die_buf.clone());
91
92 let pty = Pty::spawn(move |_fd, res| {
94 read_buf_async.lock().unwrap().push_str(res.unwrap().as_str());
95 }, move |fd| {
96 die_buf_async.lock().unwrap().push_str(format!("{fd} dead").as_str());
97 })?;
98 std::thread::sleep(Duration::from_millis(100));
99
100 let pty = unsafe { Pty::from_raw_fd(pty.as_raw_fd()) };
102 pty.write("echo 'Hello, World'\r")?;
104 std::thread::sleep(Duration::from_millis(100));
105
106 pty.kill();
107 std::thread::sleep(Duration::from_millis(100));
108
109 assert!(read_buf.lock().unwrap().contains("echo 'Hello, World'"));
112 assert_eq!(die_buf.lock().unwrap().as_str(), format!("{} dead", pty.as_raw_fd()).as_str());
113
114 Ok(())
115 }
116}