Skip to main content

http_streams_core/
error.rs

1//! The error type shared by every streaming format, in both directions.
2
3use std::fmt;
4
5type BoxedError = Box<dyn std::error::Error + Send + Sync>;
6
7/// The error that may occur while encoding or decoding a streamed HTTP body.
8pub struct StreamError {
9    kind: StreamErrorKind,
10    source: Option<BoxedError>,
11    message: Option<String>,
12}
13
14impl StreamError {
15    /// Create a new instance of an error.
16    ///
17    /// Public so that formats implemented outside this crate can report failures the same way
18    /// the built-in ones do.
19    pub fn new(kind: StreamErrorKind, source: Option<BoxedError>, message: Option<String>) -> Self {
20        Self {
21            kind,
22            source,
23            message,
24        }
25    }
26
27    /// The kind of error that occurred.
28    pub fn kind(&self) -> StreamErrorKind {
29        self.kind
30    }
31
32    /// The actual error that occurred.
33    pub fn source(&self) -> Option<&BoxedError> {
34        self.source.as_ref()
35    }
36
37    /// The message associated with the error.
38    pub fn message(&self) -> Option<&str> {
39        self.message.as_deref()
40    }
41
42    /// Takes the error apart, giving up ownership of its cause.
43    ///
44    /// Exists so a binding crate can wrap its own error type to pass it through this crate's
45    /// pipeline and then recover the *original* on the way out, by downcasting the returned
46    /// source. Without that, a round trip would leave the caller's error nested inside a
47    /// `StreamError` inside their own error type, changing what their callbacks see.
48    pub fn into_parts(self) -> (StreamErrorKind, Option<BoxedError>, Option<String>) {
49        (self.kind, self.source, self.message)
50    }
51
52    /// A codec error carrying `source` as its cause.
53    pub fn codec(source: impl Into<BoxedError>) -> Self {
54        Self::new(StreamErrorKind::CodecError, Some(source.into()), None)
55    }
56
57    /// An I/O error carrying `source` as its cause.
58    pub fn io(source: impl Into<BoxedError>) -> Self {
59        Self::new(StreamErrorKind::InputOutputError, Some(source.into()), None)
60    }
61}
62
63/// The kind of error that occurred.
64///
65/// Variant names are inherited from `reqwest-streams`, whose public `StreamBodyKind` is a
66/// renamed re-export of this type: renaming them would break downstream `match` arms.
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68#[non_exhaustive]
69pub enum StreamErrorKind {
70    /// An error occured while encoding or decoding a frame or format.
71    CodecError,
72
73    /// An error occured while reading or writing the stream.
74    InputOutputError,
75
76    /// The maximum length of a single object was exceeded.
77    MaxLenReachedError,
78
79    /// The maximum length of the whole body was exceeded.
80    ///
81    /// Only reachable on the receiving side, where the peer is not trusted.
82    MaxBodyLenReachedError,
83}
84
85impl StreamErrorKind {
86    /// A short, stable name for this kind, reported as the `error_kind` tracing field so that
87    /// errors can be aggregated without parsing their [`Display`] output.
88    ///
89    /// [`Display`]: fmt::Display
90    pub fn as_str(&self) -> &'static str {
91        match self {
92            StreamErrorKind::CodecError => "codec",
93            StreamErrorKind::InputOutputError => "io",
94            StreamErrorKind::MaxLenReachedError => "max_len",
95            StreamErrorKind::MaxBodyLenReachedError => "max_body_len",
96        }
97    }
98}
99
100impl fmt::Debug for StreamError {
101    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        let mut builder = f.debug_struct("StreamError");
103
104        builder.field("kind", &self.kind);
105
106        if let Some(ref source) = self.source {
107            builder.field("source", source);
108        }
109
110        if let Some(ref message) = self.message {
111            builder.field("message", message);
112        }
113
114        builder.finish()
115    }
116}
117
118impl fmt::Display for StreamError {
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        match self.kind {
121            StreamErrorKind::CodecError => f.write_str("Frame/codec error")?,
122            StreamErrorKind::InputOutputError => f.write_str("I/O error")?,
123            StreamErrorKind::MaxLenReachedError => f.write_str("Max object length reached")?,
124            StreamErrorKind::MaxBodyLenReachedError => f.write_str("Max body length reached")?,
125        };
126
127        if let Some(message) = &self.message {
128            write!(f, ": {}", message)?;
129        }
130
131        if let Some(e) = &self.source {
132            write!(f, ": {}", e)?;
133        }
134
135        Ok(())
136    }
137}
138
139impl std::error::Error for StreamError {}
140
141impl From<std::io::Error> for StreamError {
142    fn from(err: std::io::Error) -> Self {
143        StreamError::new(StreamErrorKind::InputOutputError, Some(Box::new(err)), None)
144    }
145}