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("Could not find Content-Type header")]
16 ContentTypeMissing,
17
18 #[display("Could not parse Content-Type header")]
20 ContentTypeParse,
21
22 #[display("Parsed Content-Type did not have 'multipart' top-level media type")]
29 ContentTypeIncompatible,
30
31 #[display("Multipart boundary is not found")]
33 BoundaryMissing,
34
35 #[display("Content-Disposition header was not found when parsing a \"form-data\" field")]
43 ContentDispositionMissing,
44
45 #[display(
52 "Content-Disposition 'name' parameter was not found when parsing a \"form-data\" field"
53 )]
54 ContentDispositionNameMissing,
55
56 #[display("Nested multipart is not supported")]
58 Nested,
59
60 #[display("Multipart stream is incomplete")]
62 Incomplete,
63
64 #[display("Error during field parsing")]
66 Parse(ParseError),
67
68 #[display("Payload error")]
70 Payload(PayloadError),
71
72 #[display("Stream is not consumed")]
74 NotConsumed,
75
76 #[display("An error occurred processing field \"{name}\"")]
78 Field {
79 name: String,
80 source: actix_web::Error,
81 },
82
83 #[display("Duplicate field found \"{_0}\"")]
85 #[from(ignore)]
86 DuplicateField(#[error(not(source))] String),
87
88 #[display("Required field is missing \"{_0}\"")]
90 #[from(ignore)]
91 MissingField(#[error(not(source))] String),
92
93 #[display("Unknown field \"{_0}\"")]
95 #[from(ignore)]
96 UnknownField(#[error(not(source))] String),
97}
98
99impl ResponseError for Error {
100 fn status_code(&self) -> StatusCode {
101 match &self {
102 Error::Field { source, .. } => source.as_response_error().status_code(),
103 Error::ContentTypeIncompatible => StatusCode::UNSUPPORTED_MEDIA_TYPE,
104 _ => StatusCode::BAD_REQUEST,
105 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use err_report::Report;
112
113 use super::*;
114
115 #[test]
116 fn test_multipart_error() {
117 let resp = Error::BoundaryMissing.error_response();
118 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
119
120 let err = Error::Payload(PayloadError::Overflow);
121 assert_eq!(
122 Report::new(&err).to_string(),
123 "Payload error: payload reached size limit",
124 );
125
126 let err = Error::Parse(ParseError::Incomplete);
127 assert_eq!(
128 Report::new(&err).to_string(),
129 "Error during field parsing: message is incomplete"
130 );
131
132 let err = Error::ContentDispositionNameMissing;
133 assert_eq!(
134 Report::new(&err).to_string(),
135 "Content-Disposition 'name' parameter was not found when parsing a \"form-data\" field"
136 );
137
138 let err = Error::Field {
139 name: "avatar".to_owned(),
140 source: actix_web::error::ErrorBadRequest("invalid file format"),
141 };
142 assert_eq!(
143 Report::new(&err).to_string(),
144 "An error occurred processing field \"avatar\": invalid file format"
145 );
146 }
147}