1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// this module is transparently re-exported by its parent `stream`

use std::error::Error;

/// A error that is raised by functions that move fallible `Source`s into
/// fallible `Sinks`.
///
/// In case this error is raised it can be matched to investigate if `Source`
/// or `Sink` failed.
///
/// # Conversion
///
/// Both variants `SourceError` and `SinkError` are public exported.
/// Consequently, `StreamError` can be constructed with `.map_err(SourceError)`
/// and `.map_err(SinkError)`.
///
/// # ToDo
///
/// For a proper use in multi-threaded environments the trait bounds should be
/// supplied with `+ Send + Sync`. However, such bounds wound make `StreamError`
/// incompatible with `sophia`'s whole error-handling. This should be resolved
/// when the error-handling is completely refactored
/// ([tracking issue](https://github.com/pchampin/sophia_rs/issues/8)).
#[derive(Debug, thiserror::Error)]
pub enum StreamError<SourceErr, SinkErr>
where
    SourceErr: 'static + Error,
    SinkErr: 'static + Error,
{
    #[error("Source failed: {0}")]
    SourceError(#[source] SourceErr),
    #[error("Sink failed: {0}")]
    SinkError(#[source] SinkErr),
}

pub use self::StreamError::*;

impl<SourceErr, SinkErr> StreamError<SourceErr, SinkErr>
where
    SourceErr: 'static + Error,
    SinkErr: 'static + Error,
{
    /// Checks if `StreamError` was raised by the `Source`.
    pub fn is_source_error(&self) -> bool {
        matches!(self, SourceError(_))
    }
    /// Checks if `StreamError` was raised by the `Sink`.
    pub fn is_sink_error(&self) -> bool {
        matches!(self, SinkError(_))
    }
    /// Converts `StreamError` into an inner error.
    pub fn inner_into<Err>(self) -> Err
    where
        SourceErr: 'static + Error + Into<Err>,
        SinkErr: 'static + Error + Into<Err>,
    {
        match self {
            SourceError(err) => err.into(),
            SinkError(err) => err.into(),
        }
    }
    /// Unwrap as the inner SourceError.
    ///
    /// # Panic
    /// Panic if self is actually a SinkError.
    pub fn unwrap_source_error(self) -> SourceErr {
        match self {
            SourceError(err) => err,
            SinkError(_) => panic!("this is a SinkError"),
        }
    }
    /// Unwrap as the inner SinkError.
    ///
    /// # Panic
    /// Panic if self is actually a SourceError.
    pub fn unwrap_sink_error(self) -> SinkErr {
        match self {
            SourceError(_) => panic!("this is a SourceError"),
            SinkError(err) => err,
        }
    }
    /// Switch source and sink error.
    pub fn reverse(self) -> StreamError<SinkErr, SourceErr> {
        match self {
            SourceError(e) => SinkError(e),
            SinkError(e) => SourceError(e),
        }
    }
}

/// Convenient type alias
pub type StreamResult<T, E1, E2> = Result<T, StreamError<E1, E2>>;

/// Extension trait for `SourceError`s.
pub trait SourceResult<T, E1, E2>
where
    E1: 'static + Error,
    E2: 'static + Error,
{
    /// Map an error to a `SourceError`.
    fn source_err(self) -> StreamResult<T, E1, E2>;
}

impl<T, E1, E2> SourceResult<T, E1, E2> for Result<T, E1>
where
    E1: 'static + Error,
    E2: 'static + Error,
{
    fn source_err(self) -> StreamResult<T, E1, E2> {
        self.map_err(SourceError)
    }
}

/// Extension trait for `SinkError`s.
pub trait SinkResult<T, E1, E2>
where
    E1: 'static + Error,
    E2: 'static + Error,
{
    /// Map an error to a `SinkError`.
    fn sink_err(self) -> StreamResult<T, E1, E2>;
}

impl<T, E1, E2> SinkResult<T, E1, E2> for Result<T, E2>
where
    E1: 'static + Error,
    E2: 'static + Error,
{
    fn sink_err(self) -> StreamResult<T, E1, E2> {
        self.map_err(SinkError)
    }
}