diff --git a/io/src/error.rs b/io/src/error.rs new file mode 100644 index 00000000..8d0a894d --- /dev/null +++ b/io/src/error.rs @@ -0,0 +1,177 @@ +#[cfg(any(feature = "alloc", feature = "std"))] +use alloc::boxed::Box; +use core::fmt::{Debug, Display, Formatter}; + +#[derive(Debug)] +pub struct Error { + kind: ErrorKind, + + #[cfg(feature = "std")] + error: Option>, + #[cfg(all(feature = "alloc", not(feature = "std")))] + error: Option>, +} + +impl Error { + #[cfg(feature = "std")] + pub fn new(kind: ErrorKind, error: E) -> Error + where + E: Into>, + { + Self { kind, error: Some(error.into()) } + } + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub fn new(kind: ErrorKind, error: E) -> Error { + Self { kind, error: Some(error.into()) } + } + + pub fn kind(&self) -> ErrorKind { self.kind } + + #[cfg(feature = "std")] + pub fn get_ref(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> { + self.error.as_deref() + } + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub fn get_ref(&self) -> Option<&(dyn Debug + Send + Sync + 'static)> { + self.error.as_deref() + } +} + +impl From for Error { + fn from(kind: ErrorKind) -> Error { + Self { + kind, + #[cfg(any(feature = "std", feature = "alloc"))] + error: None, + } + } +} + +impl Display for Error { + fn fmt(&self, fmt: &mut Formatter) -> core::result::Result<(), core::fmt::Error> { + fmt.write_fmt(format_args!("I/O Error: {}", self.kind.description()))?; + #[cfg(any(feature = "alloc", feature = "std"))] + if let Some(e) = &self.error { + fmt.write_fmt(format_args!(". {:?}", e))?; + } + Ok(()) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.error.as_ref().and_then(|e| e.as_ref().source()) + } + + #[allow(deprecated)] + fn description(&self) -> &str { + match self.error.as_ref() { + Some(e) => e.description(), + None => self.kind.description(), + } + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn std::error::Error> { + self.error.as_ref().and_then(|e| e.as_ref().cause()) + } +} + +#[cfg(feature = "std")] +impl From for Error { + fn from(o: std::io::Error) -> Error { + Self { kind: ErrorKind::from_std(o.kind()), error: o.into_inner() } + } +} + +#[cfg(feature = "std")] +impl From for std::io::Error { + fn from(o: Error) -> std::io::Error { + if let Some(err) = o.error { + std::io::Error::new(o.kind.to_std(), err) + } else { + o.kind.to_std().into() + } + } +} + +macro_rules! define_errorkind { + ($($kind: ident),*) => { + #[non_exhaustive] + #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] + /// A minimal subset of [`std::io::ErrorKind`] which is used for [`Error`]. Note that, as with + /// [`std::io`], only [`Self::Interrupted`] has defined semantics in this crate, all other + /// variants are provided here only to provide higher-fidelity conversions to and from + /// [`std::io::Error`]. + pub enum ErrorKind { + $($kind),* + } + + impl ErrorKind { + fn description(&self) -> &'static str { + match self { + $(Self::$kind => stringify!($kind)),* + } + } + + #[cfg(feature = "std")] + fn to_std(self) -> std::io::ErrorKind { + match self { + $(Self::$kind => std::io::ErrorKind::$kind),* + } + } + + #[cfg(feature = "std")] + fn from_std(o: std::io::ErrorKind) -> ErrorKind { + match o { + $(std::io::ErrorKind::$kind => ErrorKind::$kind),*, + _ => ErrorKind::Other + } + } + } + } +} + +define_errorkind!( + NotFound, + PermissionDenied, + ConnectionRefused, + ConnectionReset, + ConnectionAborted, + NotConnected, + AddrInUse, + AddrNotAvailable, + BrokenPipe, + AlreadyExists, + WouldBlock, + InvalidInput, + InvalidData, + TimedOut, + WriteZero, + Interrupted, + UnexpectedEof, + // Note: Any time we bump the MSRV any new error kinds should be added here! + Other +); + +#[cfg(all(feature = "alloc", not(feature = "std")))] +mod sealed { + use alloc::boxed::Box; + use alloc::string::String; + use core::fmt::Debug; + + pub trait IntoBoxDynDebug { + fn into(self) -> Box; + } + + impl IntoBoxDynDebug for &str { + fn into(self) -> Box { Box::new(String::from(self)) } + } + + impl IntoBoxDynDebug for String { + fn into(self) -> Box { Box::new(self) } + } +} diff --git a/io/src/lib.rs b/io/src/lib.rs index 7a84eb0a..32e717da 100644 --- a/io/src/lib.rs +++ b/io/src/lib.rs @@ -13,191 +13,29 @@ //! This traits are not one-for-one drop-ins, but are as close as possible while still implementing //! `std::io`'s traits without unnecessary complexity. +#![cfg_attr(not(feature = "std"), no_std)] // Experimental features we need. #![cfg_attr(docsrs, feature(doc_auto_cfg))] -#![cfg_attr(not(feature = "std"), no_std)] #[cfg(any(feature = "alloc", feature = "std"))] extern crate alloc; +mod error; mod macros; +#[rustfmt::skip] // Keep public re-exports separate. +pub use self::error::{Error, ErrorKind}; + /// Standard I/O stream definitions which are API-equivalent to `std`'s `io` module. See /// [`std::io`] for more info. -#[cfg(any(feature = "alloc", feature = "std"))] -use alloc::boxed::Box; use core::convert::TryInto; -use core::fmt::{Debug, Display, Formatter}; - -#[cfg(all(feature = "alloc", not(feature = "std")))] -mod sealed { - use alloc::boxed::Box; - use alloc::string::String; - use core::fmt::Debug; - pub trait IntoBoxDynDebug { - fn into(self) -> Box; - } - impl IntoBoxDynDebug for &str { - fn into(self) -> Box { Box::new(String::from(self)) } - } - impl IntoBoxDynDebug for String { - fn into(self) -> Box { Box::new(self) } - } -} - -#[derive(Debug)] -pub struct Error { - kind: ErrorKind, - - #[cfg(feature = "std")] - error: Option>, - #[cfg(all(feature = "alloc", not(feature = "std")))] - error: Option>, -} -impl Error { - #[cfg(feature = "std")] - pub fn new(kind: ErrorKind, error: E) -> Error - where - E: Into>, - { - Self { kind, error: Some(error.into()) } - } - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub fn new(kind: ErrorKind, error: E) -> Error { - Self { kind, error: Some(error.into()) } - } - - pub fn kind(&self) -> ErrorKind { self.kind } -} - -impl From for Error { - fn from(kind: ErrorKind) -> Error { - Self { - kind, - #[cfg(any(feature = "std", feature = "alloc"))] - error: None, - } - } -} - -impl Display for Error { - fn fmt(&self, fmt: &mut Formatter) -> core::result::Result<(), core::fmt::Error> { - fmt.write_fmt(format_args!("I/O Error: {}", self.kind.description()))?; - #[cfg(any(feature = "alloc", feature = "std"))] - if let Some(e) = &self.error { - fmt.write_fmt(format_args!(". {:?}", e))?; - } - Ok(()) - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - self.error.as_ref().and_then(|e| e.as_ref().source()) - } - #[allow(deprecated)] - fn description(&self) -> &str { - match self.error.as_ref() { - Some(e) => e.description(), - None => self.kind.description(), - } - } - #[allow(deprecated)] - fn cause(&self) -> Option<&dyn std::error::Error> { - self.error.as_ref().and_then(|e| e.as_ref().cause()) - } -} - -impl Error { - #[cfg(feature = "std")] - pub fn get_ref(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> { - self.error.as_deref() - } - #[cfg(all(feature = "alloc", not(feature = "std")))] - pub fn get_ref(&self) -> Option<&(dyn Debug + Send + Sync + 'static)> { self.error.as_deref() } -} - -#[cfg(feature = "std")] -impl From for Error { - fn from(o: std::io::Error) -> Error { - Self { kind: ErrorKind::from_std(o.kind()), error: o.into_inner() } - } -} - -#[cfg(feature = "std")] -impl From for std::io::Error { - fn from(o: Error) -> std::io::Error { - if let Some(err) = o.error { - std::io::Error::new(o.kind.to_std(), err) - } else { - o.kind.to_std().into() - } - } -} - -macro_rules! define_errorkind { - ($($kind: ident),*) => { - #[non_exhaustive] - #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] - /// A minimal subset of [`std::io::ErrorKind`] which is used for [`Error`]. Note that, as with - /// [`std::io`], only [`Self::Interrupted`] has defined semantics in this crate, all other - /// variants are provided here only to provide higher-fidelity conversions to and from - /// [`std::io::Error`]. - pub enum ErrorKind { - $($kind),* - } - - impl ErrorKind { - fn description(&self) -> &'static str { - match self { - $(Self::$kind => stringify!($kind)),* - } - } - #[cfg(feature = "std")] - fn to_std(self) -> std::io::ErrorKind { - match self { - $(Self::$kind => std::io::ErrorKind::$kind),* - } - } - #[cfg(feature = "std")] - fn from_std(o: std::io::ErrorKind) -> ErrorKind { - match o { - $(std::io::ErrorKind::$kind => ErrorKind::$kind),*, - _ => ErrorKind::Other - } - } - } - } -} - -define_errorkind!( - NotFound, - PermissionDenied, - ConnectionRefused, - ConnectionReset, - ConnectionAborted, - NotConnected, - AddrInUse, - AddrNotAvailable, - BrokenPipe, - AlreadyExists, - WouldBlock, - InvalidInput, - InvalidData, - TimedOut, - WriteZero, - Interrupted, - UnexpectedEof, - // Note: Any time we bump the MSRV any new error kinds should be added here! - Other -); pub type Result = core::result::Result; /// A generic trait describing an input stream. See [`std::io::Read`] for more info. pub trait Read { fn read(&mut self, buf: &mut [u8]) -> Result; + #[inline] fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> { while !buf.is_empty() { @@ -210,6 +48,7 @@ pub trait Read { } Ok(()) } + #[inline] fn take(&mut self, limit: u64) -> Take { Take { reader: self, remaining: limit } } } @@ -218,6 +57,7 @@ pub struct Take<'a, R: Read + ?Sized> { reader: &'a mut R, remaining: u64, } + impl<'a, R: Read + ?Sized> Read for Take<'a, R> { #[inline] fn read(&mut self, buf: &mut [u8]) -> Result { @@ -251,14 +91,18 @@ pub struct Cursor { inner: T, pos: u64, } + impl> Cursor { #[inline] pub fn new(inner: T) -> Self { Cursor { inner, pos: 0 } } + #[inline] pub fn position(&self) -> u64 { self.pos } + #[inline] pub fn into_inner(self) -> T { self.inner } } + impl> Read for Cursor { #[inline] fn read(&mut self, buf: &mut [u8]) -> Result { @@ -275,6 +119,7 @@ impl> Read for Cursor { /// A generic trait describing an output stream. See [`std::io::Write`] for more info. pub trait Write { fn write(&mut self, buf: &[u8]) -> Result; + fn flush(&mut self) -> Result<()>; #[inline] @@ -297,6 +142,7 @@ impl Write for W { fn write(&mut self, buf: &[u8]) -> Result { Ok(::write(self, buf)?) } + #[inline] fn flush(&mut self) -> Result<()> { Ok(::flush(self)?) } } @@ -308,6 +154,7 @@ impl Write for alloc::vec::Vec { self.extend_from_slice(buf); Ok(buf.len()) } + #[inline] fn flush(&mut self) -> Result<()> { Ok(()) } } @@ -321,29 +168,37 @@ impl<'a> Write for &'a mut [u8] { *self = &mut core::mem::take(self)[cnt..]; Ok(cnt) } + #[inline] fn flush(&mut self) -> Result<()> { Ok(()) } } /// A sink to which all writes succeed. See [`std::io::Sink`] for more info. pub struct Sink; + #[cfg(not(feature = "std"))] impl Write for Sink { #[inline] fn write(&mut self, buf: &[u8]) -> Result { Ok(buf.len()) } + #[inline] fn write_all(&mut self, _: &[u8]) -> Result<()> { Ok(()) } + #[inline] fn flush(&mut self) -> Result<()> { Ok(()) } } + #[cfg(feature = "std")] impl std::io::Write for Sink { #[inline] fn write(&mut self, buf: &[u8]) -> std::io::Result { Ok(buf.len()) } + #[inline] fn write_all(&mut self, _: &[u8]) -> std::io::Result<()> { Ok(()) } + #[inline] fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } + /// Returns a sink to which all writes succeed. See [`std::io::sink`] for more info. pub fn sink() -> Sink { Sink }