elif_http/errors/
parse_error.rs

1//! Framework-native parsing error types
2//!
3//! These errors replace Axum error type exposures in public APIs.
4//! They represent parsing/validation failures that occur during request processing.
5
6use thiserror::Error;
7
8/// Result type for parsing operations
9pub type ParseResult<T> = Result<T, ParseError>;
10
11/// Framework-native parsing errors (replaces Axum error exposures)
12#[derive(Error, Debug)]
13pub enum ParseError {
14    #[error("Invalid HTTP method: {method}")]
15    InvalidMethod { method: String },
16
17    #[error("Invalid header name: {name}")]
18    InvalidHeaderName { name: String },
19
20    #[error("Invalid header value: {value}")]
21    InvalidHeaderValue { value: String },
22
23    #[error("Header value contains non-ASCII characters")]
24    HeaderToStrError,
25
26    #[error("Invalid status code: {code}")]
27    InvalidStatusCode { code: u16 },
28
29    #[error("JSON parsing failed: {message}")]
30    JsonRejection { message: String },
31}
32
33impl ParseError {
34    /// Create an invalid method error
35    pub fn invalid_method<T: Into<String>>(method: T) -> Self {
36        ParseError::InvalidMethod {
37            method: method.into(),
38        }
39    }
40
41    /// Create an invalid header name error
42    pub fn invalid_header_name<T: Into<String>>(name: T) -> Self {
43        ParseError::InvalidHeaderName { name: name.into() }
44    }
45
46    /// Create an invalid header value error
47    pub fn invalid_header_value<T: Into<String>>(value: T) -> Self {
48        ParseError::InvalidHeaderValue {
49            value: value.into(),
50        }
51    }
52
53    /// Create a header to string error
54    pub fn header_to_str_error() -> Self {
55        ParseError::HeaderToStrError
56    }
57
58    /// Create an invalid status code error
59    pub fn invalid_status_code(code: u16) -> Self {
60        ParseError::InvalidStatusCode { code }
61    }
62
63    /// Create a JSON rejection error
64    pub fn json_rejection<T: Into<String>>(message: T) -> Self {
65        ParseError::JsonRejection {
66            message: message.into(),
67        }
68    }
69}
70
71// Convert from Axum error types to framework-native errors
72impl From<axum::http::method::InvalidMethod> for ParseError {
73    fn from(err: axum::http::method::InvalidMethod) -> Self {
74        ParseError::InvalidMethod {
75            method: err.to_string(),
76        }
77    }
78}
79
80impl From<axum::http::header::InvalidHeaderName> for ParseError {
81    fn from(err: axum::http::header::InvalidHeaderName) -> Self {
82        ParseError::InvalidHeaderName {
83            name: err.to_string(),
84        }
85    }
86}
87
88impl From<axum::http::header::InvalidHeaderValue> for ParseError {
89    fn from(err: axum::http::header::InvalidHeaderValue) -> Self {
90        ParseError::InvalidHeaderValue {
91            value: err.to_string(),
92        }
93    }
94}
95
96impl From<axum::http::header::ToStrError> for ParseError {
97    fn from(_: axum::http::header::ToStrError) -> Self {
98        ParseError::HeaderToStrError
99    }
100}
101
102// Note: From<InvalidStatusCode> implementation removed because it hardcodes code: 0
103// which produces incorrect error messages. Use ParseError::invalid_status_code(actual_code) instead.
104
105impl From<axum::extract::rejection::JsonRejection> for ParseError {
106    fn from(err: axum::extract::rejection::JsonRejection) -> Self {
107        ParseError::JsonRejection {
108            message: err.to_string(),
109        }
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_parse_error_creation() {
119        let error = ParseError::invalid_method("INVALID");
120        assert!(matches!(error, ParseError::InvalidMethod { .. }));
121        assert_eq!(error.to_string(), "Invalid HTTP method: INVALID");
122    }
123
124    #[test]
125    fn test_header_errors() {
126        let name_error = ParseError::invalid_header_name("bad name");
127        let value_error = ParseError::invalid_header_value("bad\x00value");
128        let str_error = ParseError::header_to_str_error();
129
130        assert!(matches!(name_error, ParseError::InvalidHeaderName { .. }));
131        assert!(matches!(value_error, ParseError::InvalidHeaderValue { .. }));
132        assert!(matches!(str_error, ParseError::HeaderToStrError));
133    }
134
135    #[test]
136    fn test_status_code_error() {
137        let error = ParseError::invalid_status_code(999);
138        assert!(matches!(error, ParseError::InvalidStatusCode { code: 999 }));
139    }
140
141    #[test]
142    fn test_json_error() {
143        let error = ParseError::json_rejection("Invalid JSON syntax");
144        assert!(matches!(error, ParseError::JsonRejection { .. }));
145    }
146
147    #[test]
148    fn test_axum_conversions() {
149        // Test that we can convert from Axum errors
150        let axum_method_err =
151            axum::http::Method::from_bytes(b"INVALID METHOD WITH SPACES").unwrap_err();
152        let parse_err: ParseError = axum_method_err.into();
153        assert!(matches!(parse_err, ParseError::InvalidMethod { .. }));
154    }
155}