use base_scheduler::Cancelled;
use std::{any::Any, error, fmt, sync::Arc};
#[derive(Clone, Debug)]
pub enum BodyError {
Cancelled,
Closed,
Abandoned,
InvalidFrame,
ChunkTooLarge { length: usize, limit: usize },
Failed(Arc<dyn error::Error + Send + Sync>),
}
impl BodyError {
pub fn new<E: error::Error + Send + Sync + 'static>(error: E) -> Self {
let mut error = Some(error);
if let Some(body) = (&mut error as &mut dyn Any).downcast_mut::<Option<Self>>() {
return body.take().unwrap();
}
Self::Failed(Arc::new(error.unwrap()))
}
}
impl From<Cancelled> for BodyError {
fn from(_: Cancelled) -> Self {
Self::Cancelled
}
}
impl From<std::io::Error> for BodyError {
fn from(error: std::io::Error) -> Self {
Self::new(error)
}
}
impl fmt::Display for BodyError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Cancelled => formatter.write_str("body operation cancelled"),
Self::Closed => formatter.write_str("body output is closed"),
Self::Abandoned => formatter.write_str("body abandoned before completion"),
Self::InvalidFrame => {
formatter.write_str("body data or headers after trailing headers")
}
Self::ChunkTooLarge { length, limit } => write!(
formatter,
"body frame has {length} bytes, exceeding queue limit {limit}"
),
Self::Failed(_) => formatter.write_str("body failed"),
}
}
}
impl error::Error for BodyError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::Failed(error) => Some(error.as_ref()),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::{error::Error as _, io};
#[test]
fn failed_reports_the_producer_error_as_its_source() {
let error = BodyError::new(io::Error::from(io::ErrorKind::TimedOut));
assert_eq!(error.to_string(), "body failed");
assert_eq!(error.source().unwrap().to_string(), "timed out");
}
#[test]
fn wrapping_a_body_error_returns_it_unchanged() {
let error = BodyError::new(BodyError::new(io::Error::other("producer failed")));
assert_eq!(error.to_string(), "body failed");
assert_eq!(error.source().unwrap().to_string(), "producer failed");
assert_eq!(
BodyError::new(BodyError::Closed).to_string(),
"body output is closed"
);
}
}