mirror of
https://github.com/rosenpass/rosenpass.git
synced 2026-02-28 14:33:37 -08:00
chore: Documentation and unit tests for rosenpass_util::io
This commit is contained in:
355
util/src/io.rs
355
util/src/io.rs
@@ -1,8 +1,262 @@
|
|||||||
|
//! Helpers for performing IO
|
||||||
|
//!
|
||||||
|
//! # IO Error handling helpers tutorial
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use std::io::ErrorKind as EK;
|
||||||
|
//!
|
||||||
|
//! // It can be a bit hard to use IO errors in match statements
|
||||||
|
//!
|
||||||
|
//! fn io_placeholder() -> std::io::Result<()> {
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! loop {
|
||||||
|
//! match io_placeholder() {
|
||||||
|
//! Ok(()) => break,
|
||||||
|
//! // All errors are unreachable; just here for demo purposes
|
||||||
|
//! Err(e) if e.kind() == EK::Interrupted => continue,
|
||||||
|
//! Err(e) if e.kind() == EK::WouldBlock => {
|
||||||
|
//! panic!("This particular function is not designed to be used in nonblocking code!");
|
||||||
|
//! }
|
||||||
|
//! Err(e) => Err(e)?,
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // For this reason this module contains various helper functions to make
|
||||||
|
//! // matching on error kinds a bit less repetitive. [IoResultKindHintExt::io_err_kind_hint]
|
||||||
|
//! // provides the basic functionality for use mostly with std::io::Result
|
||||||
|
//!
|
||||||
|
//! use rosenpass_util::io::IoResultKindHintExt;
|
||||||
|
//!
|
||||||
|
//! loop {
|
||||||
|
//! match io_placeholder().io_err_kind_hint() {
|
||||||
|
//! Ok(()) => break,
|
||||||
|
//! // All errors are unreachable; just here for demo purposes
|
||||||
|
//! Err((_, EK::Interrupted)) => continue,
|
||||||
|
//! Err((_, EK::WouldBlock)) => {
|
||||||
|
//! // Unreachable, just here for explanation purposes
|
||||||
|
//! panic!("This particular function is not designed to be used in nonblocking code!");
|
||||||
|
//! }
|
||||||
|
//! Err((e, _)) => Err(e)?,
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // The trait can be customized; firstly, you can use IoErrorKind
|
||||||
|
//! // for error types that can be fully represented as std::io::ErrorKind
|
||||||
|
//!
|
||||||
|
//! use rosenpass_util::io::IoErrorKind;
|
||||||
|
//!
|
||||||
|
//! #[derive(thiserror::Error, Debug, PartialEq, Eq)]
|
||||||
|
//! enum MyErrno {
|
||||||
|
//! #[error("Got interrupted")]
|
||||||
|
//! Interrupted,
|
||||||
|
//! #[error("In nonblocking mode")]
|
||||||
|
//! WouldBlock,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl IoErrorKind for MyErrno {
|
||||||
|
//! fn io_error_kind(&self) -> std::io::ErrorKind {
|
||||||
|
//! use MyErrno as ME;
|
||||||
|
//! match self {
|
||||||
|
//! ME::Interrupted => EK::Interrupted,
|
||||||
|
//! ME::WouldBlock => EK::WouldBlock,
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! EK::Interrupted,
|
||||||
|
//! std::io::Error::new(EK::Interrupted, "artificially interrupted").io_error_kind()
|
||||||
|
//! );
|
||||||
|
//! assert_eq!(EK::Interrupted, MyErrno::Interrupted.io_error_kind());
|
||||||
|
//! assert_eq!(EK::WouldBlock, MyErrno::WouldBlock.io_error_kind());
|
||||||
|
//!
|
||||||
|
//! // And when an error can not fully be represented as an std::io::ErrorKind,
|
||||||
|
//! // you can still use [TryIoErrorKind]
|
||||||
|
//!
|
||||||
|
//! use rosenpass_util::io::TryIoErrorKind;
|
||||||
|
//!
|
||||||
|
//! #[derive(thiserror::Error, Debug, PartialEq, Eq)]
|
||||||
|
//! enum MyErrnoOrBlue {
|
||||||
|
//! #[error("Got interrupted")]
|
||||||
|
//! Interrupted,
|
||||||
|
//! #[error("In nonblocking mode")]
|
||||||
|
//! WouldBlock,
|
||||||
|
//! #[error("I am feeling blue")]
|
||||||
|
//! FeelingBlue,
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl TryIoErrorKind for MyErrnoOrBlue {
|
||||||
|
//! fn try_io_error_kind(&self) -> Option<std::io::ErrorKind> {
|
||||||
|
//! use MyErrnoOrBlue as ME;
|
||||||
|
//! match self {
|
||||||
|
//! ME::Interrupted => Some(EK::Interrupted),
|
||||||
|
//! ME::WouldBlock => Some(EK::WouldBlock),
|
||||||
|
//! ME::FeelingBlue => None,
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! Some(EK::Interrupted),
|
||||||
|
//! MyErrnoOrBlue::Interrupted.try_io_error_kind()
|
||||||
|
//! );
|
||||||
|
//! assert_eq!(
|
||||||
|
//! Some(EK::WouldBlock),
|
||||||
|
//! MyErrnoOrBlue::WouldBlock.try_io_error_kind()
|
||||||
|
//! );
|
||||||
|
//! assert_eq!(None, MyErrnoOrBlue::FeelingBlue.try_io_error_kind());
|
||||||
|
//!
|
||||||
|
//! // TryIoErrorKind is automatically implemented for all types that implement
|
||||||
|
//! // IoErrorKind
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! Some(EK::Interrupted),
|
||||||
|
//! std::io::Error::new(EK::Interrupted, "artificially interrupted").try_io_error_kind()
|
||||||
|
//! );
|
||||||
|
//! assert_eq!(
|
||||||
|
//! Some(EK::Interrupted),
|
||||||
|
//! MyErrno::Interrupted.try_io_error_kind()
|
||||||
|
//! );
|
||||||
|
//! assert_eq!(
|
||||||
|
//! Some(EK::WouldBlock),
|
||||||
|
//! MyErrno::WouldBlock.try_io_error_kind()
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! // By implementing IoErrorKind, we can automatically make use of IoResultKindHintExt<T>
|
||||||
|
//! // with our custom error type
|
||||||
|
//!
|
||||||
|
//! //use rosenpass_util::io::IoResultKindHintExt;
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! Ok::<_, MyErrno>(42).io_err_kind_hint(),
|
||||||
|
//! Ok(42));
|
||||||
|
//! assert!(matches!(
|
||||||
|
//! Err::<(), _>(std::io::Error::new(EK::Interrupted, "artificially interrupted")).io_err_kind_hint(),
|
||||||
|
//! Err((err, EK::Interrupted)) if format!("{err:?}") == "Custom { kind: Interrupted, error: \"artificially interrupted\" }"));
|
||||||
|
//! assert_eq!(
|
||||||
|
//! Err::<(), _>(MyErrno::Interrupted).io_err_kind_hint(),
|
||||||
|
//! Err((MyErrno::Interrupted, EK::Interrupted)));
|
||||||
|
//!
|
||||||
|
//! // Correspondingly, TryIoResultKindHintExt can be used for Results with Errors
|
||||||
|
//! // that implement TryIoErrorKind
|
||||||
|
//!
|
||||||
|
//! use crate::rosenpass_util::io::TryIoResultKindHintExt;
|
||||||
|
//!
|
||||||
|
//! assert_eq!(
|
||||||
|
//! Ok::<_, MyErrnoOrBlue>(42).try_io_err_kind_hint(),
|
||||||
|
//! Ok(42));
|
||||||
|
//! assert_eq!(
|
||||||
|
//! Err::<(), _>(MyErrnoOrBlue::Interrupted).try_io_err_kind_hint(),
|
||||||
|
//! Err((MyErrnoOrBlue::Interrupted, Some(EK::Interrupted))));
|
||||||
|
//! assert_eq!(
|
||||||
|
//! Err::<(), _>(MyErrnoOrBlue::FeelingBlue).try_io_err_kind_hint(),
|
||||||
|
//! Err((MyErrnoOrBlue::FeelingBlue, None)));
|
||||||
|
//!
|
||||||
|
//! // SubstituteForIoErrorKindExt serves as a helper to handle specific ErrorKinds
|
||||||
|
//! // using a method chaining style. It works on anything that implements TryIoErrorKind.
|
||||||
|
//!
|
||||||
|
//! use rosenpass_util::io::SubstituteForIoErrorKindExt;
|
||||||
|
//!
|
||||||
|
//! assert_eq!(Ok(42),
|
||||||
|
//! Err(MyErrnoOrBlue::Interrupted)
|
||||||
|
//! .substitute_for_ioerr_kind_with(EK::Interrupted, || 42));
|
||||||
|
//!
|
||||||
|
//! assert_eq!(Err(MyErrnoOrBlue::WouldBlock),
|
||||||
|
//! Err(MyErrnoOrBlue::WouldBlock)
|
||||||
|
//! .substitute_for_ioerr_kind_with(EK::Interrupted, || 42));
|
||||||
|
//!
|
||||||
|
//! // The other functions in SubstituteForIoErrorKindExt are mostly just wrappers,
|
||||||
|
//! // getting the same job done with minor convenience
|
||||||
|
//!
|
||||||
|
//! // Plain Ok() value instead of function
|
||||||
|
//! assert_eq!(Ok(42),
|
||||||
|
//! Err(MyErrnoOrBlue::Interrupted)
|
||||||
|
//! .substitute_for_ioerr_kind(EK::Interrupted, 42));
|
||||||
|
//! assert_eq!(Err(MyErrnoOrBlue::WouldBlock),
|
||||||
|
//! Err(MyErrnoOrBlue::WouldBlock)
|
||||||
|
//! .substitute_for_ioerr_kind(EK::Interrupted, 42));
|
||||||
|
//!
|
||||||
|
//! // For specific errors
|
||||||
|
//! assert_eq!(Ok(42),
|
||||||
|
//! Err(MyErrnoOrBlue::Interrupted)
|
||||||
|
//! .substitute_for_ioerr_interrupted_with(|| 42)
|
||||||
|
//! .substitute_for_ioerr_wouldblock_with(|| 23));
|
||||||
|
//! assert_eq!(Ok(23),
|
||||||
|
//! Err(MyErrnoOrBlue::WouldBlock)
|
||||||
|
//! .substitute_for_ioerr_interrupted_with(|| 42)
|
||||||
|
//! .substitute_for_ioerr_wouldblock_with(|| 23));
|
||||||
|
//! assert_eq!(Err(MyErrnoOrBlue::FeelingBlue),
|
||||||
|
//! Err(MyErrnoOrBlue::FeelingBlue)
|
||||||
|
//! .substitute_for_ioerr_interrupted_with(|| 42)
|
||||||
|
//! .substitute_for_ioerr_wouldblock_with(|| 23));
|
||||||
|
//!
|
||||||
|
//! // And for specific errors without the function call
|
||||||
|
//! assert_eq!(Ok(42),
|
||||||
|
//! Err(MyErrnoOrBlue::Interrupted)
|
||||||
|
//! .substitute_for_ioerr_interrupted(42)
|
||||||
|
//! .substitute_for_ioerr_wouldblock(23));
|
||||||
|
//! assert_eq!(Ok(23),
|
||||||
|
//! Err(MyErrnoOrBlue::WouldBlock)
|
||||||
|
//! .substitute_for_ioerr_interrupted(42)
|
||||||
|
//! .substitute_for_ioerr_wouldblock(23));
|
||||||
|
//! assert_eq!(Err(MyErrnoOrBlue::FeelingBlue),
|
||||||
|
//! Err(MyErrnoOrBlue::FeelingBlue)
|
||||||
|
//! .substitute_for_ioerr_interrupted(42)
|
||||||
|
//! .substitute_for_ioerr_wouldblock(23));
|
||||||
|
//!
|
||||||
|
//! // handle_interrupted automates the process of handling ErrorKind::Interrupted
|
||||||
|
//! // in cases where the action should simply be rerun; it can handle any error type
|
||||||
|
//! // that implements TryIoErrorKind. It lets other errors and Ok(_) pass through.
|
||||||
|
//!
|
||||||
|
//! use rosenpass_util::io::handle_interrupted;
|
||||||
|
//!
|
||||||
|
//! let mut ctr = 0u32;
|
||||||
|
//! let mut simulate_io = || -> Result<u32, MyErrnoOrBlue> {
|
||||||
|
//! let r = match ctr % 6 {
|
||||||
|
//! 1 => Ok(42),
|
||||||
|
//! 3 => Err(MyErrnoOrBlue::FeelingBlue),
|
||||||
|
//! 5 => Err(MyErrnoOrBlue::WouldBlock),
|
||||||
|
//! _ => Err(MyErrnoOrBlue::Interrupted),
|
||||||
|
//! };
|
||||||
|
//! ctr += 1;
|
||||||
|
//! r
|
||||||
|
//! };
|
||||||
|
//!
|
||||||
|
//! assert_eq!(Ok(Some(42)), handle_interrupted(&mut simulate_io));
|
||||||
|
//! assert_eq!(Err(MyErrnoOrBlue::FeelingBlue), handle_interrupted(&mut simulate_io));
|
||||||
|
//! assert_eq!(Err(MyErrnoOrBlue::WouldBlock), handle_interrupted(&mut simulate_io));
|
||||||
|
//! // never returns None
|
||||||
|
//!
|
||||||
|
//! // nonblocking_handle_io_errors performs the same job, except that
|
||||||
|
//! // WouldBlock is substituted with Ok(None)
|
||||||
|
//!
|
||||||
|
//! use rosenpass_util::io::nonblocking_handle_io_errors;
|
||||||
|
//!
|
||||||
|
//! assert_eq!(Ok(Some(42)), nonblocking_handle_io_errors(&mut simulate_io));
|
||||||
|
//! assert_eq!(Err(MyErrnoOrBlue::FeelingBlue), nonblocking_handle_io_errors(&mut simulate_io));
|
||||||
|
//! assert_eq!(Ok(None), nonblocking_handle_io_errors(&mut simulate_io));
|
||||||
|
//!
|
||||||
|
//! Ok::<_, anyhow::Error>(())
|
||||||
|
//! ```
|
||||||
|
|
||||||
use std::{borrow::Borrow, io};
|
use std::{borrow::Borrow, io};
|
||||||
|
|
||||||
use anyhow::ensure;
|
use anyhow::ensure;
|
||||||
|
use zerocopy::AsBytes;
|
||||||
|
|
||||||
|
/// Generic trait for accessing [std::io::Error::kind]
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
pub trait IoErrorKind {
|
pub trait IoErrorKind {
|
||||||
|
/// Conversion to [std::io::Error::kind]
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
fn io_error_kind(&self) -> io::ErrorKind;
|
fn io_error_kind(&self) -> io::ErrorKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,7 +266,17 @@ impl<T: Borrow<io::Error>> IoErrorKind for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generic trait for accessing [std::io::Error::kind] where it may not be present
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
pub trait TryIoErrorKind {
|
pub trait TryIoErrorKind {
|
||||||
|
/// Conversion to [std::io::Error::kind] where it may not be present
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
fn try_io_error_kind(&self) -> Option<io::ErrorKind>;
|
fn try_io_error_kind(&self) -> Option<io::ErrorKind>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,8 +286,19 @@ impl<T: IoErrorKind> TryIoErrorKind for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper for accessing [std::io::Error::kind] in Results
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
pub trait IoResultKindHintExt<T>: Sized {
|
pub trait IoResultKindHintExt<T>: Sized {
|
||||||
|
// Error trait including the ErrorKind hint
|
||||||
type Error;
|
type Error;
|
||||||
|
/// Helper for accessing [std::io::Error::kind] in Results
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
fn io_err_kind_hint(self) -> Result<T, (Self::Error, io::ErrorKind)>;
|
fn io_err_kind_hint(self) -> Result<T, (Self::Error, io::ErrorKind)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,8 +312,19 @@ impl<T, E: IoErrorKind> IoResultKindHintExt<T> for Result<T, E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper for accessing [std::io::Error::kind] in Results where it may not be present
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
pub trait TryIoResultKindHintExt<T>: Sized {
|
pub trait TryIoResultKindHintExt<T>: Sized {
|
||||||
|
// Error trait including the ErrorKind hint
|
||||||
type Error;
|
type Error;
|
||||||
|
/// Helper for accessing [std::io::Error::kind] in Results where it may not be present
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
fn try_io_err_kind_hint(self) -> Result<T, (Self::Error, Option<io::ErrorKind>)>;
|
fn try_io_err_kind_hint(self) -> Result<T, (Self::Error, Option<io::ErrorKind>)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,17 +338,41 @@ impl<T, E: TryIoErrorKind> TryIoResultKindHintExt<T> for Result<T, E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper for working with IO results using a method chaining style
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
pub trait SubstituteForIoErrorKindExt<T>: Sized {
|
pub trait SubstituteForIoErrorKindExt<T>: Sized {
|
||||||
|
/// Error type produced by methods in this trait
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
|
/// Substitute errors with a certain [std::io::ErrorKind] by a value produced by a function
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
fn substitute_for_ioerr_kind_with<F: FnOnce() -> T>(
|
fn substitute_for_ioerr_kind_with<F: FnOnce() -> T>(
|
||||||
self,
|
self,
|
||||||
kind: io::ErrorKind,
|
kind: io::ErrorKind,
|
||||||
f: F,
|
f: F,
|
||||||
) -> Result<T, Self::Error>;
|
) -> Result<T, Self::Error>;
|
||||||
|
|
||||||
|
/// Substitute errors with a certain [std::io::ErrorKind] by a value
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
fn substitute_for_ioerr_kind(self, kind: io::ErrorKind, v: T) -> Result<T, Self::Error> {
|
fn substitute_for_ioerr_kind(self, kind: io::ErrorKind, v: T) -> Result<T, Self::Error> {
|
||||||
self.substitute_for_ioerr_kind_with(kind, || v)
|
self.substitute_for_ioerr_kind_with(kind, || v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Substitute errors with [std::io::ErrorKind] [std::io::ErrorKind::Interrupted] by a value
|
||||||
|
/// produced by a function
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
fn substitute_for_ioerr_interrupted_with<F: FnOnce() -> T>(
|
fn substitute_for_ioerr_interrupted_with<F: FnOnce() -> T>(
|
||||||
self,
|
self,
|
||||||
f: F,
|
f: F,
|
||||||
@@ -70,10 +380,21 @@ pub trait SubstituteForIoErrorKindExt<T>: Sized {
|
|||||||
self.substitute_for_ioerr_kind_with(io::ErrorKind::Interrupted, f)
|
self.substitute_for_ioerr_kind_with(io::ErrorKind::Interrupted, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Substitute errors with [std::io::ErrorKind] [std::io::ErrorKind::Interrupted] by a value
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
fn substitute_for_ioerr_interrupted(self, v: T) -> Result<T, Self::Error> {
|
fn substitute_for_ioerr_interrupted(self, v: T) -> Result<T, Self::Error> {
|
||||||
self.substitute_for_ioerr_interrupted_with(|| v)
|
self.substitute_for_ioerr_interrupted_with(|| v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Substitute errors with [std::io::ErrorKind] [std::io::ErrorKind::WouldBlock] by a value
|
||||||
|
/// produced by a function
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
fn substitute_for_ioerr_wouldblock_with<F: FnOnce() -> T>(
|
fn substitute_for_ioerr_wouldblock_with<F: FnOnce() -> T>(
|
||||||
self,
|
self,
|
||||||
f: F,
|
f: F,
|
||||||
@@ -81,6 +402,11 @@ pub trait SubstituteForIoErrorKindExt<T>: Sized {
|
|||||||
self.substitute_for_ioerr_kind_with(io::ErrorKind::WouldBlock, f)
|
self.substitute_for_ioerr_kind_with(io::ErrorKind::WouldBlock, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Substitute errors with [std::io::ErrorKind] [std::io::ErrorKind::WouldBlock] by a value
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
fn substitute_for_ioerr_wouldblock(self, v: T) -> Result<T, Self::Error> {
|
fn substitute_for_ioerr_wouldblock(self, v: T) -> Result<T, Self::Error> {
|
||||||
self.substitute_for_ioerr_wouldblock_with(|| v)
|
self.substitute_for_ioerr_wouldblock_with(|| v)
|
||||||
}
|
}
|
||||||
@@ -107,6 +433,10 @@ impl<T, E: TryIoErrorKind> SubstituteForIoErrorKindExt<T> for Result<T, E> {
|
|||||||
/// - If there is no error (i.e. on `Ok(r)`), the function will return `Ok(Some(r))`
|
/// - If there is no error (i.e. on `Ok(r)`), the function will return `Ok(Some(r))`
|
||||||
/// - `Interrupted` is handled internally, by retrying the IO operation
|
/// - `Interrupted` is handled internally, by retrying the IO operation
|
||||||
/// - Other errors are returned as is
|
/// - Other errors are returned as is
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
pub fn handle_interrupted<R, E, F>(mut iofn: F) -> Result<Option<R>, E>
|
pub fn handle_interrupted<R, E, F>(mut iofn: F) -> Result<Option<R>, E>
|
||||||
where
|
where
|
||||||
E: TryIoErrorKind,
|
E: TryIoErrorKind,
|
||||||
@@ -128,6 +458,10 @@ where
|
|||||||
/// - `Interrupted` is handled internally, by retrying the IO operation
|
/// - `Interrupted` is handled internally, by retrying the IO operation
|
||||||
/// - `WouldBlock` is handled by returning `Ok(None)`,
|
/// - `WouldBlock` is handled by returning `Ok(None)`,
|
||||||
/// - Other errors are returned as is
|
/// - Other errors are returned as is
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// See [tutorial in the module](self).
|
||||||
pub fn nonblocking_handle_io_errors<R, E, F>(mut iofn: F) -> Result<Option<R>, E>
|
pub fn nonblocking_handle_io_errors<R, E, F>(mut iofn: F) -> Result<Option<R>, E>
|
||||||
where
|
where
|
||||||
E: TryIoErrorKind,
|
E: TryIoErrorKind,
|
||||||
@@ -144,6 +478,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [std:io::Read] extension trait for call with [nonblocking_handle_io_errors] applied
|
||||||
pub trait ReadNonblockingWithBoringErrorsHandledExt {
|
pub trait ReadNonblockingWithBoringErrorsHandledExt {
|
||||||
/// Convenience wrapper using [nonblocking_handle_io_errors] with [std::io::Read]
|
/// Convenience wrapper using [nonblocking_handle_io_errors] with [std::io::Read]
|
||||||
fn read_nonblocking_with_boring_errors_handled(
|
fn read_nonblocking_with_boring_errors_handled(
|
||||||
@@ -161,7 +496,27 @@ impl<T: io::Read> ReadNonblockingWithBoringErrorsHandledExt for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extension trait for [std::io::Read] providing the ability to read
|
||||||
|
/// a buffer exactly
|
||||||
pub trait ReadExt {
|
pub trait ReadExt {
|
||||||
|
/// Version of [std::io::Read::read_exact] that throws if there
|
||||||
|
/// is extra data in the stream to be read
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rosenpass_util::io::ReadExt;
|
||||||
|
///
|
||||||
|
/// let mut buf = [0u8; 4];
|
||||||
|
///
|
||||||
|
/// // Over or underlong buffer yields error
|
||||||
|
/// assert!(b"12345".as_slice().read_exact_til_end(&mut buf).is_err());
|
||||||
|
/// assert!(b"123".as_slice().read_exact_til_end(&mut buf).is_err());
|
||||||
|
///
|
||||||
|
/// // Buffer of precisely the right length leads to successful read
|
||||||
|
/// assert!(b"1234".as_slice().read_exact_til_end(&mut buf).is_ok());
|
||||||
|
/// assert_eq!(b"1234", &buf);
|
||||||
|
/// ```
|
||||||
fn read_exact_til_end(&mut self, buf: &mut [u8]) -> anyhow::Result<()>;
|
fn read_exact_til_end(&mut self, buf: &mut [u8]) -> anyhow::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user