volo_http/error/
server.rs

1//! Generic error types for server
2
3use std::{error::Error, fmt};
4
5use http::StatusCode;
6
7use crate::{response::Response, server::IntoResponse};
8
9/// [`Error`]s when extracting something from a [`Body`](crate::body::Body)
10#[derive(Debug)]
11#[non_exhaustive]
12pub enum ExtractBodyError {
13    /// Generic extracting errors when pulling [`Body`](crate::body::Body) or checking
14    /// `Content-Type` of request
15    Generic(GenericRejectionError),
16    /// The [`Body`](crate::body::Body) cannot be extracted as a [`String`] or
17    /// [`FastStr`](faststr::FastStr)
18    String(simdutf8::basic::Utf8Error),
19    /// The [`Body`](crate::body::Body) cannot be extracted as a json object
20    #[cfg(feature = "json")]
21    Json(crate::utils::json::Error),
22    /// The [`Body`](crate::body::Body) cannot be extracted as a form object
23    #[cfg(feature = "form")]
24    Form(serde_urlencoded::de::Error),
25}
26
27impl fmt::Display for ExtractBodyError {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        f.write_str("failed to extract ")?;
30        match self {
31            Self::Generic(e) => write!(f, "data: {e}"),
32            Self::String(e) => write!(f, "string: {e}"),
33            #[cfg(feature = "json")]
34            Self::Json(e) => write!(f, "json: {e}"),
35            #[cfg(feature = "form")]
36            Self::Form(e) => write!(f, "form: {e}"),
37        }
38    }
39}
40
41impl Error for ExtractBodyError {
42    fn source(&self) -> Option<&(dyn Error + 'static)> {
43        match self {
44            Self::Generic(e) => Some(e),
45            Self::String(e) => Some(e),
46            #[cfg(feature = "json")]
47            Self::Json(e) => Some(e),
48            #[cfg(feature = "form")]
49            Self::Form(e) => Some(e),
50        }
51    }
52}
53
54impl IntoResponse for ExtractBodyError {
55    fn into_response(self) -> Response {
56        let status = match self {
57            Self::Generic(e) => e.to_status_code(),
58            Self::String(_) => StatusCode::UNSUPPORTED_MEDIA_TYPE,
59            #[cfg(feature = "json")]
60            Self::Json(_) => StatusCode::BAD_REQUEST,
61            #[cfg(feature = "form")]
62            Self::Form(_) => StatusCode::BAD_REQUEST,
63        };
64
65        status.into_response()
66    }
67}
68
69/// Generic rejection [`Error`]s
70#[derive(Debug)]
71#[non_exhaustive]
72pub enum GenericRejectionError {
73    /// Failed to collect the [`Body`](crate::body::Body)
74    BodyCollectionError,
75    /// The `Content-Type` is invalid for the extractor
76    InvalidContentType,
77}
78
79impl fmt::Display for GenericRejectionError {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        match self {
82            Self::BodyCollectionError => write!(f, "failed to collect the response body"),
83            Self::InvalidContentType => write!(f, "invalid content type"),
84        }
85    }
86}
87
88impl Error for GenericRejectionError {}
89
90impl GenericRejectionError {
91    /// Convert the [`GenericRejectionError`] to the corresponding [`StatusCode`]
92    pub fn to_status_code(&self) -> StatusCode {
93        match self {
94            Self::BodyCollectionError => StatusCode::INTERNAL_SERVER_ERROR,
95            Self::InvalidContentType => StatusCode::UNSUPPORTED_MEDIA_TYPE,
96        }
97    }
98}
99
100impl IntoResponse for GenericRejectionError {
101    fn into_response(self) -> Response {
102        self.to_status_code().into_response()
103    }
104}
105
106/// Create a generic [`ExtractBodyError`] with [`GenericRejectionError::BodyCollectionError`]
107pub fn body_collection_error() -> ExtractBodyError {
108    ExtractBodyError::Generic(GenericRejectionError::BodyCollectionError)
109}
110
111/// Create a generic [`ExtractBodyError`] with [`GenericRejectionError::InvalidContentType`]
112pub fn invalid_content_type() -> ExtractBodyError {
113    ExtractBodyError::Generic(GenericRejectionError::InvalidContentType)
114}