1use std::{
6 fmt::Display,
7 io::{self, Read, Stdin, Stdout, Write},
8 os::fd::{AsRawFd, RawFd},
9 u8,
10};
11
12#[derive(Debug)]
14pub enum Error {
15 Io(io::Error),
16 Errno(nix::errno::Errno),
17}
18
19impl From<io::Error> for Error {
20 fn from(e: io::Error) -> Self {
21 Error::Io(e)
22 }
23}
24
25impl From<nix::errno::Errno> for Error {
26 fn from(e: nix::errno::Errno) -> Self {
27 Error::Errno(e)
28 }
29}
30
31impl Display for Error {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 match self {
34 Error::Io(e) => write!(f, "IO error: {}", e),
35 Error::Errno(e) => write!(f, "UNIX error: {}", e),
36 }
37 }
38}
39
40pub struct TermManager {
42 stdin: Stdin,
43 stdout: Stdout,
44 fd: RawFd,
45 original_termios: libc::termios,
46}
47
48impl TermManager {
49 pub fn new() -> Result<TermManager, Error> {
51 let stdin = io::stdin();
52 let stdout = io::stdout();
53 let fd = stdin.as_raw_fd();
54 let original_termios = enable_raw_mode(fd)?;
55
56 Ok(TermManager {
57 stdin,
58 stdout,
59 fd,
60 original_termios,
61 })
62 }
63
64 pub fn get_stdin(&self) -> &Stdin {
65 &self.stdin
66 }
67
68 pub fn get_stout(&self) -> &Stdout {
69 &self.stdout
70 }
71
72 pub fn flush(&mut self) -> Result<(), Error> {
74 match self.stdout.flush() {
75 Ok(_) => Ok(()),
76 Err(e) => Err(Error::Io(e)),
77 }
78 }
79
80 pub fn write(&mut self, data: &[u8]) -> Result<(), Error> {
82 match self.stdout.write(data) {
83 Ok(r) => {
84 if r <= 0 && data.len() <= 0 {
85 let msg = format!("only wrote {} of {} bytes", r, data.len());
86 return Err(Error::Io(io::Error::new(io::ErrorKind::WriteZero, msg)));
87 }
88
89 Ok(())
90 }
91 Err(e) => Err(Error::Io(e)),
92 }
93 }
94
95 pub fn read(&mut self, buf: &mut [u8; 1]) -> Result<usize, Error> {
97 match self.stdin.read(buf) {
98 Ok(0) => {
99 return Err(Error::Io(io::Error::new(
100 io::ErrorKind::WriteZero,
101 "read 0 bytes from stdin",
102 )));
103 }
104 Ok(bytes_read) => Ok(bytes_read),
105 Err(e) => Err(Error::Io(e)),
106 }
107 }
108}
109
110impl Drop for TermManager {
111 fn drop(&mut self) {
112 disable_raw_mode(self.fd, self.original_termios).unwrap();
113 }
114}
115
116fn enable_raw_mode(fd: RawFd) -> Result<libc::termios, Error> {
118 let original_termios = get_termios(fd)?;
119 let mut raw = original_termios;
120 raw.c_lflag &= !(libc::ICANON | libc::ECHO);
121 raw.c_cc[libc::VMIN] = 1;
122 raw.c_cc[libc::VTIME] = 0;
123 set_termios(fd, &raw)?;
124 Ok(original_termios)
125}
126
127fn disable_raw_mode(fd: RawFd, original_termios: libc::termios) -> Result<(), Error> {
129 set_termios(fd, &original_termios)?;
130 Ok(())
131}
132
133fn get_termios(fd: RawFd) -> Result<libc::termios, Error> {
135 let mut termios = std::mem::MaybeUninit::uninit();
136 let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) };
137 if res != 0 {
138 return Err(Error::Io(io::Error::last_os_error()));
139 }
140 Ok(unsafe { termios.assume_init() })
141}
142
143fn set_termios(fd: RawFd, termios: &libc::termios) -> Result<(), Error> {
145 let res = unsafe { libc::tcsetattr(fd, libc::TCSANOW, termios) };
146 if res != 0 {
147 return Err(Error::Io(io::Error::last_os_error()));
148 }
149 Ok(())
150}