origin_studio/
io.rs

1//! Traits, helpers, and type definitions for core I/O functionality.
2
3#[cfg(feature = "thread")]
4use crate::thread::{ReentrantMutex, ReentrantMutexGuard};
5use core::fmt::{self, Arguments};
6#[cfg(not(feature = "thread"))]
7use core::marker::PhantomData;
8
9pub type Error = rustix::io::Errno;
10
11pub type Result<T> = core::result::Result<T, Error>;
12
13pub trait Read {
14    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
15
16    fn is_read_vectored(&self) -> bool {
17        false
18    }
19}
20
21pub trait Write {
22    fn write(&mut self, buf: &[u8]) -> Result<usize>;
23
24    fn is_write_vectored(&self) -> bool {
25        false
26    }
27
28    fn flush(&mut self) -> Result<()>;
29
30    fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
31        while !buf.is_empty() {
32            match self.write(buf) {
33                Ok(n) => buf = &buf[n..],
34                Err(Error::INTR) => {}
35                Err(err) => return Err(err),
36            }
37        }
38        Ok(())
39    }
40
41    fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result<()> {
42        // Create a shim which translates a Write to a fmt::Write and saves
43        // off I/O errors. instead of discarding them
44        struct Adapter<'a, T: ?Sized + 'a> {
45            inner: &'a mut T,
46            error: Result<()>,
47        }
48
49        impl<T: Write + ?Sized> fmt::Write for Adapter<'_, T> {
50            fn write_str(&mut self, s: &str) -> fmt::Result {
51                match self.inner.write_all(s.as_bytes()) {
52                    Ok(()) => Ok(()),
53                    Err(e) => {
54                        self.error = Err(e);
55                        Err(fmt::Error)
56                    }
57                }
58            }
59        }
60
61        let mut output = Adapter {
62            inner: self,
63            error: Ok(()),
64        };
65        match fmt::write(&mut output, fmt) {
66            Ok(()) => Ok(()),
67            Err(..) => {
68                // check if the error came from the underlying `Write` or not
69                if output.error.is_err() {
70                    output.error
71                } else {
72                    Err(Error::IO)
73                }
74            }
75        }
76    }
77}
78
79pub struct StdoutLock<'a> {
80    #[cfg(feature = "thread")]
81    _lock: ReentrantMutexGuard<'a, ()>,
82
83    #[cfg(not(feature = "thread"))]
84    _phantom: PhantomData<&'a ()>,
85}
86
87#[cfg(feature = "thread")]
88static STDOUT_LOCK: ReentrantMutex<()> = ReentrantMutex::new(());
89
90pub struct Stdout(());
91
92pub fn stdout() -> Stdout {
93    Stdout(())
94}
95
96impl Stdout {
97    pub fn lock(&self) -> StdoutLock<'static> {
98        StdoutLock {
99            #[cfg(feature = "thread")]
100            _lock: STDOUT_LOCK.lock(),
101
102            #[cfg(not(feature = "thread"))]
103            _phantom: PhantomData,
104        }
105    }
106}
107
108impl Write for Stdout {
109    #[cfg_attr(feature = "std", allow(unused_unsafe))]
110    fn write(&mut self, buf: &[u8]) -> Result<usize> {
111        rustix::io::write(unsafe { rustix::stdio::stdout() }, buf)
112    }
113
114    fn flush(&mut self) -> Result<()> {
115        Ok(())
116    }
117}
118
119impl<'a> Write for StdoutLock<'a> {
120    fn write(&mut self, buf: &[u8]) -> Result<usize> {
121        Stdout(()).write(buf)
122    }
123
124    fn flush(&mut self) -> Result<()> {
125        Stdout(()).flush()
126    }
127}
128
129impl core::fmt::Write for Stdout {
130    fn write_str(&mut self, s: &str) -> core::fmt::Result {
131        match self.write_all(s.as_bytes()) {
132            Ok(_) => Ok(()),
133            Err(err) => panic!("failed printing to stdout: {:?}", err),
134        }
135    }
136}
137
138impl<'a> core::fmt::Write for StdoutLock<'a> {
139    fn write_str(&mut self, s: &str) -> core::fmt::Result {
140        Stdout(()).write_str(s)
141    }
142}
143
144pub struct StderrLock<'a> {
145    #[cfg(feature = "thread")]
146    _lock: ReentrantMutexGuard<'a, ()>,
147
148    #[cfg(not(feature = "thread"))]
149    _phantom: PhantomData<&'a ()>,
150}
151
152#[cfg(feature = "thread")]
153static STDERR_LOCK: ReentrantMutex<()> = ReentrantMutex::new(());
154
155pub struct Stderr(());
156
157pub fn stderr() -> Stderr {
158    Stderr(())
159}
160
161impl Stderr {
162    pub fn lock(&self) -> StderrLock<'static> {
163        StderrLock {
164            #[cfg(feature = "thread")]
165            _lock: STDERR_LOCK.lock(),
166
167            #[cfg(not(feature = "thread"))]
168            _phantom: PhantomData,
169        }
170    }
171}
172
173impl Write for Stderr {
174    #[cfg_attr(feature = "std", allow(unused_unsafe))]
175    fn write(&mut self, buf: &[u8]) -> Result<usize> {
176        rustix::io::write(unsafe { rustix::stdio::stderr() }, buf)
177    }
178
179    fn flush(&mut self) -> Result<()> {
180        Ok(())
181    }
182}
183
184impl<'a> Write for StderrLock<'a> {
185    fn write(&mut self, buf: &[u8]) -> Result<usize> {
186        Stderr(()).write(buf)
187    }
188
189    fn flush(&mut self) -> Result<()> {
190        Stderr(()).flush()
191    }
192}
193
194impl core::fmt::Write for Stderr {
195    fn write_str(&mut self, s: &str) -> core::fmt::Result {
196        match self.write_all(s.as_bytes()) {
197            Ok(_) => Ok(()),
198            Err(err) => panic!("failed printing to stderr: {:?}", err),
199        }
200    }
201}
202
203impl<'a> core::fmt::Write for StderrLock<'a> {
204    fn write_str(&mut self, s: &str) -> core::fmt::Result {
205        Stderr(()).write_str(s)
206    }
207}
208
209pub trait Seek {
210    fn seek(&mut self, pos: SeekFrom) -> Result<u64>;
211
212    fn rewind(&mut self) -> Result<()> {
213        self.seek(SeekFrom::Start(0))?;
214        Ok(())
215    }
216
217    fn stream_position(&mut self) -> Result<u64> {
218        self.seek(SeekFrom::Current(0))
219    }
220}
221
222#[derive(Copy, Clone, Eq, PartialEq, Debug)]
223pub enum SeekFrom {
224    Start(u64),
225    End(i64),
226    Current(i64),
227}