1use actix_web::{
4    error::{ParseError, PayloadError},
5    http::StatusCode,
6    ResponseError,
7};
8use derive_more::{Display, Error, From};
9
10#[derive(Debug, Display, From, Error)]
12#[non_exhaustive]
13pub enum Error {
14    #[display(fmt = "Could not find Content-Type header")]
16    ContentTypeMissing,
17
18    #[display(fmt = "Could not parse Content-Type header")]
20    ContentTypeParse,
21
22    #[display(fmt = "Parsed Content-Type did not have "multipart" top-level media type")]
29    ContentTypeIncompatible,
30
31    #[display(fmt = "Multipart boundary is not found")]
33    BoundaryMissing,
34
35    #[display(fmt = "Content-Disposition header was not found when parsing a \"form-data\" field")]
43    ContentDispositionMissing,
44
45    #[display(fmt = "Content-Disposition header was not found when parsing a \"form-data\" field")]
52    ContentDispositionNameMissing,
53
54    #[display(fmt = "Nested multipart is not supported")]
56    Nested,
57
58    #[display(fmt = "Multipart stream is incomplete")]
60    Incomplete,
61
62    #[display(fmt = "Error during field parsing")]
64    Parse(ParseError),
65
66    #[display(fmt = "Payload error")]
68    Payload(PayloadError),
69
70    #[display(fmt = "Stream is not consumed")]
72    NotConsumed,
73
74    #[display(fmt = "An error occurred processing field: {name}")]
76    Field {
77        name: String,
78        source: actix_web::Error,
79    },
80
81    #[display(fmt = "Duplicate field found: {_0}")]
83    #[from(ignore)]
84    DuplicateField(#[error(not(source))] String),
85
86    #[display(fmt = "Required field is missing: {_0}")]
88    #[from(ignore)]
89    MissingField(#[error(not(source))] String),
90
91    #[display(fmt = "Unknown field: {_0}")]
93    #[from(ignore)]
94    UnknownField(#[error(not(source))] String),
95}
96
97impl ResponseError for Error {
99    fn status_code(&self) -> StatusCode {
100        match &self {
101            Error::Field { source, .. } => source.as_response_error().status_code(),
102            Error::ContentTypeIncompatible => StatusCode::UNSUPPORTED_MEDIA_TYPE,
103            _ => StatusCode::BAD_REQUEST,
104        }
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_multipart_error() {
114        let resp = Error::BoundaryMissing.error_response();
115        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
116    }
117}