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 {
let msg = self.to_string();
io::Error::new(io::ErrorKind::Other, msg)
}
}
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>>;