use std::{
error::Error,
fmt::{self, Debug, Display, Formatter, Write},
io,
};
#[derive(Debug)]
pub struct ConversionError<S, E = NoDetails> {
pub details: E,
pub cause: Option<io::Error>,
pub source: Option<S>,
}
impl<S, E: Default> ConversionError<S, E> {
pub fn from_source(source: S) -> Self {
Self { details: Default::default(), cause: None, source: Some(source) }
}
pub fn from_cause(cause: io::Error) -> Self {
Self { details: Default::default(), cause: Some(cause), source: None }
}
pub fn from_source_and_cause(source: S, cause: io::Error) -> Self {
Self { details: Default::default(), cause: Some(cause), source: Some(source) }
}
}
impl<S, E> ConversionError<S, E> {
pub fn from_source_and_details(source: S, details: E) -> Self {
Self { details, cause: None, source: Some(source) }
}
pub fn from_cause_and_details(cause: io::Error, details: E) -> Self {
Self { details, cause: Some(cause), source: None }
}
pub fn map_source<Sb>(self, f: impl FnOnce(S) -> Sb) -> ConversionError<Sb, E> {
ConversionError { details: self.details, cause: self.cause, source: self.source.map(f) }
}
pub fn try_map_source<Sb>(self, f: impl FnOnce(S) -> Option<Sb>) -> ConversionError<Sb, E> {
ConversionError {
details: self.details,
cause: self.cause,
source: self.source.and_then(f),
}
}
}
impl<S, E: Display> ConversionError<S, E> {
pub fn to_io_error(&self) -> io::Error { io::Error::other(self.to_string()) }
}
impl<S, E: Display> From<ConversionError<S, E>> for io::Error {
fn from(e: ConversionError<S, E>) -> Self { e.to_io_error() }
}
impl<S, E: Default> Default for ConversionError<S, E> {
fn default() -> Self { Self { details: Default::default(), cause: None, source: None } }
}
impl<S, E: Display> Display for ConversionError<S, E> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut snp = FormatSnooper::new(f);
write!(snp, "{}", &self.details)?;
if let Some(e) = &self.cause {
if snp.anything_written() {
f.write_str(": ")?;
}
Display::fmt(e, f)?;
}
Ok(())
}
}
impl<S: Debug, E: Error + 'static> Error for ConversionError<S, E> {
#[inline]
#[allow(clippy::as_conversions)]
fn source(&self) -> Option<&(dyn Error + 'static)> { self.cause.as_ref().map(|r| r as &_) }
}
struct FormatSnooper<'a, 'b> {
formatter: &'b mut Formatter<'a>,
anything_written: bool,
}
impl<'a, 'b> FormatSnooper<'a, 'b> {
fn new(formatter: &'b mut Formatter<'a>) -> Self {
Self { formatter, anything_written: false }
}
fn anything_written(&self) -> bool { self.anything_written }
}
impl Write for FormatSnooper<'_, '_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
if !s.is_empty() {
self.anything_written = true;
self.formatter.write_str(s)
} else {
Ok(())
}
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct NoDetails;
impl Display for NoDetails {
fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
Ok(()) }
}
#[cfg(windows)]
#[cfg_attr(feature = "doc_cfg", doc(cfg(windows)))]
pub type FromHandleError<E = NoDetails> = ConversionError<std::os::windows::io::OwnedHandle, E>;
#[cfg(unix)]
#[cfg_attr(feature = "doc_cfg", doc(cfg(unix)))]
pub type FromFdError<E = NoDetails> = ConversionError<std::os::unix::io::OwnedFd, E>;
#[derive(Debug)]
pub struct ReuniteError<R, S> {
pub rh: R,
pub sh: S,
}
impl<R, S> ReuniteError<R, S> {
#[inline]
pub fn map_halves<NR: From<R>, NS: From<S>>(
self,
fr: impl FnOnce(R) -> NR,
fs: impl FnOnce(S) -> NS,
) -> ReuniteError<NR, NS> {
let Self { rh, sh } = self;
ReuniteError { rh: fr(rh), sh: fs(sh) }
}
#[inline]
pub fn convert_halves<NR: From<R>, NS: From<S>>(self) -> ReuniteError<NR, NS> {
self.map_halves(From::from, From::from)
}
}
impl<R, S> Display for ReuniteError<R, S> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("attempt to reunite stream halves that come from different streams")
}
}
impl<R: Debug, S: Debug> Error for ReuniteError<R, S> {}
pub type ReuniteResult<T, R, S> = Result<T, ReuniteError<R, S>>;