1#[derive(Debug, thiserror::Error)]
10#[non_exhaustive]
11pub enum Error {
12 #[error("stream read failed: {0}")]
14 StreamReadFailed(#[source] Box<dyn std::error::Error + Send + Sync>),
15 #[error("invalid boundary")]
17 InvalidBoundary,
18 #[error("invalid multipart format")]
20 InvalidFormat,
21 #[error("incomplete multipart stream")]
23 IncompleteStream,
24 #[error("part headers exceed the buffer limit of {limit} bytes")]
26 HeaderSizeExceeded {
27 limit: usize,
29 },
30 #[error("content follows the closing delimiter of the taken part")]
34 StreamPartNotLast,
35 #[error("multipart stream has already been taken")]
37 StreamAlreadyTaken,
38 #[error("incomplete streaming part")]
40 IncompleteStreamPart,
41}
42
43impl Error {
44 #[must_use]
49 pub fn stream_read_failed(source: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> Self {
50 Self::StreamReadFailed(source.into())
51 }
52}
53
54#[cfg(test)]
55#[allow(clippy::expect_used, clippy::panic, clippy::unreachable, clippy::unwrap_used)]
56mod tests {
57 use std::error::Error as _;
58
59 use super::*;
60
61 #[test]
62 fn display_messages_are_stable() {
63 assert_eq!(Error::InvalidBoundary.to_string(), "invalid boundary");
64 assert_eq!(Error::InvalidFormat.to_string(), "invalid multipart format");
65 assert_eq!(Error::IncompleteStream.to_string(), "incomplete multipart stream");
66 assert_eq!(Error::IncompleteStreamPart.to_string(), "incomplete streaming part");
67 assert_eq!(
68 Error::StreamPartNotLast.to_string(),
69 "content follows the closing delimiter of the taken part"
70 );
71 assert_eq!(Error::StreamAlreadyTaken.to_string(), "multipart stream has already been taken");
72 assert_eq!(
73 Error::HeaderSizeExceeded { limit: 8192 }.to_string(),
74 "part headers exceed the buffer limit of 8192 bytes"
75 );
76 assert_eq!(
77 Error::StreamReadFailed(Box::new(std::io::Error::other("boom"))).to_string(),
78 "stream read failed: boom"
79 );
80 }
81
82 #[test]
83 fn only_stream_failures_expose_a_source() {
84 let err = Error::StreamReadFailed(Box::new(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "eof")));
85 let kind = err
86 .source()
87 .and_then(|source| source.downcast_ref::<std::io::Error>())
88 .map(std::io::Error::kind);
89 assert_eq!(kind, Some(std::io::ErrorKind::UnexpectedEof));
90
91 for err in [
92 Error::InvalidBoundary,
93 Error::InvalidFormat,
94 Error::IncompleteStream,
95 Error::IncompleteStreamPart,
96 Error::StreamPartNotLast,
97 Error::StreamAlreadyTaken,
98 Error::HeaderSizeExceeded { limit: 1 },
99 ] {
100 assert!(err.source().is_none());
101 }
102 }
103
104 #[test]
105 fn error_is_send_sync_static() {
106 fn assert_bounds<T: std::error::Error + Send + Sync + 'static>() {}
107 assert_bounds::<Error>();
108 }
109}