hermit/
io.rs

1use alloc::string::String;
2use alloc::vec::Vec;
3use core::{fmt, result};
4
5use num_enum::{IntoPrimitive, TryFromPrimitive};
6
7// TODO: Integrate with src/errno.rs ?
8#[allow(clippy::upper_case_acronyms)]
9#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
10#[repr(i32)]
11pub enum Error {
12	ENOENT = crate::errno::ENOENT,
13	ENOSYS = crate::errno::ENOSYS,
14	EIO = crate::errno::EIO,
15	EBADF = crate::errno::EBADF,
16	EISDIR = crate::errno::EISDIR,
17	EINVAL = crate::errno::EINVAL,
18	ETIME = crate::errno::ETIME,
19	EAGAIN = crate::errno::EAGAIN,
20	EFAULT = crate::errno::EFAULT,
21	ENOBUFS = crate::errno::ENOBUFS,
22	ENOTCONN = crate::errno::ENOTCONN,
23	ENOTDIR = crate::errno::ENOTDIR,
24	EMFILE = crate::errno::EMFILE,
25	EEXIST = crate::errno::EEXIST,
26	EADDRINUSE = crate::errno::EADDRINUSE,
27	EOVERFLOW = crate::errno::EOVERFLOW,
28	ENOTSOCK = crate::errno::ENOTSOCK,
29}
30
31pub type Result<T> = result::Result<T, Error>;
32
33/// The Read trait allows for reading bytes from a source.
34///
35/// The Read trait is derived from Rust's std library.
36pub trait Read {
37	fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
38
39	/// Read all bytes until EOF in this source, placing them into buf.
40	fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
41		let start_len = buf.len();
42
43		loop {
44			let mut probe = [0u8; 512];
45
46			match self.read(&mut probe) {
47				Ok(0) => return Ok(buf.len() - start_len),
48				Ok(n) => {
49					buf.extend_from_slice(&probe[..n]);
50				}
51				Err(e) => return Err(e),
52			}
53		}
54	}
55
56	/// Read all bytes until EOF in this source, appending them to `buf`.
57	///
58	/// If successful, this function returns the number of bytes which were read
59	/// and appended to `buf`.
60	fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
61		unsafe { self.read_to_end(buf.as_mut_vec()) }
62	}
63}
64
65/// The Write trait allows for reading bytes from a source.
66///
67/// The Write trait is derived from Rust's std library.
68pub trait Write {
69	fn write(&mut self, buf: &[u8]) -> Result<usize>;
70
71	/// Attempts to write an entire buffer into this writer.
72	fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
73		while !buf.is_empty() {
74			match self.write(buf) {
75				Ok(0) => {
76					return Err(Error::EIO);
77				}
78				Ok(n) => buf = &buf[n..],
79				Err(e) => return Err(e),
80			}
81		}
82
83		Ok(())
84	}
85
86	/// Writes a formatted string into this writer, returning any error encountered.
87	fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> {
88		// Create a shim which translates a Write to a fmt::Write and saves
89		// off I/O errors. instead of discarding them
90		struct Adapter<'a, T: ?Sized> {
91			inner: &'a mut T,
92			error: Result<()>,
93		}
94
95		impl<T: Write + ?Sized> fmt::Write for Adapter<'_, T> {
96			fn write_str(&mut self, s: &str) -> fmt::Result {
97				match self.inner.write_all(s.as_bytes()) {
98					Ok(()) => Ok(()),
99					Err(e) => {
100						self.error = Err(e);
101						Err(fmt::Error)
102					}
103				}
104			}
105		}
106
107		let mut output = Adapter {
108			inner: self,
109			error: Ok(()),
110		};
111		match fmt::write(&mut output, fmt) {
112			Ok(()) => Ok(()),
113			Err(..) => {
114				// check if the error came from the underlying `Write` or not
115				if output.error.is_err() {
116					output.error
117				} else {
118					Err(Error::EINVAL)
119				}
120			}
121		}
122	}
123}