#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("stream read failed: {0}")]
StreamReadFailed(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("invalid boundary")]
InvalidBoundary,
#[error("invalid multipart format")]
InvalidFormat,
#[error("incomplete multipart stream")]
IncompleteStream,
#[error("part headers exceed the buffer limit of {limit} bytes")]
HeaderSizeExceeded {
limit: usize,
},
#[error("content follows the closing delimiter of the taken part")]
StreamPartNotLast,
#[error("multipart stream has already been taken")]
StreamAlreadyTaken,
#[error("incomplete streaming part")]
IncompleteStreamPart,
}
impl Error {
#[must_use]
pub fn stream_read_failed(source: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> Self {
Self::StreamReadFailed(source.into())
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::panic, clippy::unreachable, clippy::unwrap_used)]
mod tests {
use std::error::Error as _;
use super::*;
#[test]
fn display_messages_are_stable() {
assert_eq!(Error::InvalidBoundary.to_string(), "invalid boundary");
assert_eq!(Error::InvalidFormat.to_string(), "invalid multipart format");
assert_eq!(Error::IncompleteStream.to_string(), "incomplete multipart stream");
assert_eq!(Error::IncompleteStreamPart.to_string(), "incomplete streaming part");
assert_eq!(
Error::StreamPartNotLast.to_string(),
"content follows the closing delimiter of the taken part"
);
assert_eq!(Error::StreamAlreadyTaken.to_string(), "multipart stream has already been taken");
assert_eq!(
Error::HeaderSizeExceeded { limit: 8192 }.to_string(),
"part headers exceed the buffer limit of 8192 bytes"
);
assert_eq!(
Error::StreamReadFailed(Box::new(std::io::Error::other("boom"))).to_string(),
"stream read failed: boom"
);
}
#[test]
fn only_stream_failures_expose_a_source() {
let err = Error::StreamReadFailed(Box::new(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "eof")));
let kind = err
.source()
.and_then(|source| source.downcast_ref::<std::io::Error>())
.map(std::io::Error::kind);
assert_eq!(kind, Some(std::io::ErrorKind::UnexpectedEof));
for err in [
Error::InvalidBoundary,
Error::InvalidFormat,
Error::IncompleteStream,
Error::IncompleteStreamPart,
Error::StreamPartNotLast,
Error::StreamAlreadyTaken,
Error::HeaderSizeExceeded { limit: 1 },
] {
assert!(err.source().is_none());
}
}
#[test]
fn error_is_send_sync_static() {
fn assert_bounds<T: std::error::Error + Send + Sync + 'static>() {}
assert_bounds::<Error>();
}
}