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 { 
44            name: name.into() 
45        }
46    }
47    
48    /// Create an invalid header value error
49    pub fn invalid_header_value<T: Into<String>>(value: T) -> Self {
50        ParseError::InvalidHeaderValue { 
51            value: value.into() 
52        }
53    }
54    
55    /// Create a header to string error
56    pub fn header_to_str_error() -> Self {
57        ParseError::HeaderToStrError
58    }
59    
60    /// Create an invalid status code error
61    pub fn invalid_status_code(code: u16) -> Self {
62        ParseError::InvalidStatusCode { code }
63    }
64    
65    /// Create a JSON rejection error
66    pub fn json_rejection<T: Into<String>>(message: T) -> Self {
67        ParseError::JsonRejection { 
68            message: message.into() 
69        }
70    }
71}
72
73// Convert from Axum error types to framework-native errors
74impl From<axum::http::method::InvalidMethod> for ParseError {
75    fn from(err: axum::http::method::InvalidMethod) -> Self {
76        ParseError::InvalidMethod { 
77            method: err.to_string() 
78        }
79    }
80}
81
82impl From<axum::http::header::InvalidHeaderName> for ParseError {
83    fn from(err: axum::http::header::InvalidHeaderName) -> Self {
84        ParseError::InvalidHeaderName { 
85            name: err.to_string() 
86        }
87    }
88}
89
90impl From<axum::http::header::InvalidHeaderValue> for ParseError {
91    fn from(err: axum::http::header::InvalidHeaderValue) -> Self {
92        ParseError::InvalidHeaderValue { 
93            value: err.to_string() 
94        }
95    }
96}
97
98impl From<axum::http::header::ToStrError> for ParseError {
99    fn from(_: axum::http::header::ToStrError) -> Self {
100        ParseError::HeaderToStrError
101    }
102}
103
104// Note: From<InvalidStatusCode> implementation removed because it hardcodes code: 0
105// which produces incorrect error messages. Use ParseError::invalid_status_code(actual_code) instead.
106
107impl From<axum::extract::rejection::JsonRejection> for ParseError {
108    fn from(err: axum::extract::rejection::JsonRejection) -> Self {
109        ParseError::JsonRejection { 
110            message: err.to_string() 
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_parse_error_creation() {
121        let error = ParseError::invalid_method("INVALID");
122        assert!(matches!(error, ParseError::InvalidMethod { .. }));
123        assert_eq!(error.to_string(), "Invalid HTTP method: INVALID");
124    }
125
126    #[test]
127    fn test_header_errors() {
128        let name_error = ParseError::invalid_header_name("bad name");
129        let value_error = ParseError::invalid_header_value("bad\x00value");
130        let str_error = ParseError::header_to_str_error();
131        
132        assert!(matches!(name_error, ParseError::InvalidHeaderName { .. }));
133        assert!(matches!(value_error, ParseError::InvalidHeaderValue { .. }));
134        assert!(matches!(str_error, ParseError::HeaderToStrError));
135    }
136
137    #[test]
138    fn test_status_code_error() {
139        let error = ParseError::invalid_status_code(999);
140        assert!(matches!(error, ParseError::InvalidStatusCode { code: 999 }));
141    }
142
143    #[test]
144    fn test_json_error() {
145        let error = ParseError::json_rejection("Invalid JSON syntax");
146        assert!(matches!(error, ParseError::JsonRejection { .. }));
147    }
148
149    #[test]
150    fn test_axum_conversions() {
151        // Test that we can convert from Axum errors
152        let axum_method_err = axum::http::Method::from_bytes(b"INVALID METHOD WITH SPACES").unwrap_err();
153        let parse_err: ParseError = axum_method_err.into();
154        assert!(matches!(parse_err, ParseError::InvalidMethod { .. }));
155    }
156}