pjson_rs/
error.rs

1//! Error types for PJS operations
2
3/// Result type alias for PJS operations
4pub type Result<T> = std::result::Result<T, Error>;
5
6/// Main error type for PJS operations
7#[derive(Debug, Clone, thiserror::Error)]
8pub enum Error {
9    /// Invalid JSON syntax
10    #[error("Invalid JSON syntax at position {position}: {message}")]
11    InvalidJson {
12        /// Position in the input where error occurred
13        position: usize,
14        /// Error description
15        message: String,
16    },
17
18    /// Frame format error
19    #[error("Invalid frame format: {0}")]
20    InvalidFrame(String),
21
22    /// Schema validation error
23    #[error("Schema validation failed: {0}")]
24    SchemaValidation(String),
25
26    /// Semantic type mismatch
27    #[error("Semantic type mismatch: expected {expected}, got {actual}")]
28    SemanticTypeMismatch {
29        /// Expected semantic type
30        expected: String,
31        /// Actual semantic type
32        actual: String,
33    },
34
35    /// Buffer overflow or underflow
36    #[error("Buffer error: {0}")]
37    Buffer(String),
38
39    /// Memory allocation error
40    #[error("Memory allocation failed: {0}")]
41    Memory(String),
42
43    /// I/O error
44    #[error("I/O error: {0}")]
45    Io(String),
46
47    /// Connection failed error
48    #[error("Connection failed: {0}")]
49    ConnectionFailed(String),
50
51    /// Client error
52    #[error("Client error: {0}")]
53    ClientError(String),
54
55    /// Invalid session error
56    #[error("Invalid session: {0}")]
57    InvalidSession(String),
58
59    /// Invalid URL error
60    #[error("Invalid URL: {0}")]
61    InvalidUrl(String),
62
63    /// Serialization error
64    #[error("Serialization error: {0}")]
65    Serialization(String),
66
67    /// UTF-8 conversion error
68    #[error("UTF-8 conversion failed: {0}")]
69    Utf8(String),
70
71    /// Generic error for other cases
72    #[error("{0}")]
73    Other(String),
74}
75
76impl Error {
77    /// Create an invalid JSON error
78    pub fn invalid_json(position: usize, message: impl Into<String>) -> Self {
79        Self::InvalidJson {
80            position,
81            message: message.into(),
82        }
83    }
84
85    /// Create an invalid frame error
86    pub fn invalid_frame(message: impl Into<String>) -> Self {
87        Self::InvalidFrame(message.into())
88    }
89
90    /// Create a schema validation error
91    pub fn schema_validation(message: impl Into<String>) -> Self {
92        Self::SchemaValidation(message.into())
93    }
94
95    /// Create a semantic type mismatch error
96    pub fn semantic_type_mismatch(expected: impl Into<String>, actual: impl Into<String>) -> Self {
97        Self::SemanticTypeMismatch {
98            expected: expected.into(),
99            actual: actual.into(),
100        }
101    }
102
103    /// Create a buffer error
104    pub fn buffer(message: impl Into<String>) -> Self {
105        Self::Buffer(message.into())
106    }
107
108    /// Create a memory error
109    pub fn memory(message: impl Into<String>) -> Self {
110        Self::Memory(message.into())
111    }
112
113    /// Create a connection failed error
114    pub fn connection_failed(message: impl Into<String>) -> Self {
115        Self::ConnectionFailed(message.into())
116    }
117
118    /// Create a client error
119    pub fn client_error(message: impl Into<String>) -> Self {
120        Self::ClientError(message.into())
121    }
122
123    /// Create an invalid session error
124    pub fn invalid_session(message: impl Into<String>) -> Self {
125        Self::InvalidSession(message.into())
126    }
127
128    /// Create an invalid URL error
129    pub fn invalid_url(message: impl Into<String>) -> Self {
130        Self::InvalidUrl(message.into())
131    }
132
133    /// Create a serialization error
134    pub fn serialization(message: impl Into<String>) -> Self {
135        Self::Serialization(message.into())
136    }
137
138    /// Create a UTF-8 error
139    pub fn utf8(message: impl Into<String>) -> Self {
140        Self::Utf8(message.into())
141    }
142
143    /// Create a generic error
144    pub fn other(message: impl Into<String>) -> Self {
145        Self::Other(message.into())
146    }
147
148    /// Check if the error is related to JSON parsing
149    pub fn is_json_error(&self) -> bool {
150        matches!(self, Self::InvalidJson { .. } | Self::Serialization(_))
151    }
152
153    /// Check if the error is a network-related error
154    pub fn is_network_error(&self) -> bool {
155        matches!(self, Self::ConnectionFailed(_) | Self::Io(_))
156    }
157
158    /// Check if the error is a validation error
159    pub fn is_validation_error(&self) -> bool {
160        matches!(
161            self,
162            Self::SchemaValidation(_) | Self::SemanticTypeMismatch { .. } | Self::InvalidFrame(_)
163        )
164    }
165
166    /// Get error category as string
167    pub fn category(&self) -> &'static str {
168        match self {
169            Self::InvalidJson { .. } | Self::Serialization(_) => "json",
170            Self::InvalidFrame(_) | Self::SchemaValidation(_) | Self::SemanticTypeMismatch { .. } => "validation",
171            Self::Buffer(_) | Self::Memory(_) => "memory",
172            Self::Io(_) | Self::ConnectionFailed(_) => "network",
173            Self::ClientError(_) | Self::InvalidSession(_) | Self::InvalidUrl(_) => "client",
174            Self::Utf8(_) => "encoding",
175            Self::Other(_) => "other",
176        }
177    }
178}
179
180impl From<std::io::Error> for Error {
181    fn from(err: std::io::Error) -> Self {
182        Error::Io(err.to_string())
183    }
184}
185
186impl From<std::str::Utf8Error> for Error {
187    fn from(err: std::str::Utf8Error) -> Self {
188        Error::Utf8(err.to_string())
189    }
190}
191
192impl From<serde_json::Error> for Error {
193    fn from(err: serde_json::Error) -> Self {
194        Error::Serialization(err.to_string())
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn test_error_creation_constructors() {
204        let json_err = Error::invalid_json(42, "unexpected token");
205        if let Error::InvalidJson { position, message } = json_err {
206            assert_eq!(position, 42);
207            assert_eq!(message, "unexpected token");
208        } else {
209            panic!("Expected InvalidJson error");
210        }
211
212        let frame_err = Error::invalid_frame("malformed frame");
213        assert!(matches!(frame_err, Error::InvalidFrame(_)));
214
215        let schema_err = Error::schema_validation("required field missing");
216        assert!(matches!(schema_err, Error::SchemaValidation(_)));
217
218        let type_err = Error::semantic_type_mismatch("string", "number");
219        if let Error::SemanticTypeMismatch { expected, actual } = type_err {
220            assert_eq!(expected, "string");
221            assert_eq!(actual, "number");
222        } else {
223            panic!("Expected SemanticTypeMismatch error");
224        }
225    }
226
227    #[test]
228    fn test_all_error_constructors() {
229        assert!(matches!(Error::buffer("overflow"), Error::Buffer(_)));
230        assert!(matches!(Error::memory("allocation failed"), Error::Memory(_)));
231        assert!(matches!(Error::connection_failed("timeout"), Error::ConnectionFailed(_)));
232        assert!(matches!(Error::client_error("bad request"), Error::ClientError(_)));
233        assert!(matches!(Error::invalid_session("expired"), Error::InvalidSession(_)));
234        assert!(matches!(Error::invalid_url("malformed"), Error::InvalidUrl(_)));
235        assert!(matches!(Error::serialization("json error"), Error::Serialization(_)));
236        assert!(matches!(Error::utf8("invalid utf8"), Error::Utf8(_)));
237        assert!(matches!(Error::other("unknown"), Error::Other(_)));
238    }
239
240    #[test]
241    fn test_error_classification() {
242        let json_err = Error::invalid_json(0, "test");
243        assert!(json_err.is_json_error());
244        assert!(!json_err.is_network_error());
245        assert!(!json_err.is_validation_error());
246
247        let serialization_err = Error::serialization("test");
248        assert!(serialization_err.is_json_error());
249
250        let network_err = Error::connection_failed("test");
251        assert!(network_err.is_network_error());
252        assert!(!network_err.is_json_error());
253
254        let io_err = Error::Io("test".to_string());
255        assert!(io_err.is_network_error());
256
257        let validation_err = Error::schema_validation("test");
258        assert!(validation_err.is_validation_error());
259        assert!(!validation_err.is_json_error());
260
261        let frame_err = Error::invalid_frame("test");
262        assert!(frame_err.is_validation_error());
263
264        let type_err = Error::semantic_type_mismatch("a", "b");
265        assert!(type_err.is_validation_error());
266    }
267
268    #[test]
269    fn test_error_categories() {
270        assert_eq!(Error::invalid_json(0, "test").category(), "json");
271        assert_eq!(Error::serialization("test").category(), "json");
272        assert_eq!(Error::invalid_frame("test").category(), "validation");
273        assert_eq!(Error::schema_validation("test").category(), "validation");
274        assert_eq!(Error::semantic_type_mismatch("a", "b").category(), "validation");
275        assert_eq!(Error::buffer("test").category(), "memory");
276        assert_eq!(Error::memory("test").category(), "memory");
277        assert_eq!(Error::Io("test".to_string()).category(), "network");
278        assert_eq!(Error::connection_failed("test").category(), "network");
279        assert_eq!(Error::client_error("test").category(), "client");
280        assert_eq!(Error::invalid_session("test").category(), "client");
281        assert_eq!(Error::invalid_url("test").category(), "client");
282        assert_eq!(Error::utf8("test").category(), "encoding");
283        assert_eq!(Error::other("test").category(), "other");
284    }
285
286    #[test]
287    fn test_error_display() {
288        let json_err = Error::invalid_json(42, "unexpected token");
289        assert_eq!(
290            json_err.to_string(),
291            "Invalid JSON syntax at position 42: unexpected token"
292        );
293
294        let type_err = Error::semantic_type_mismatch("string", "number");
295        assert_eq!(
296            type_err.to_string(),
297            "Semantic type mismatch: expected string, got number"
298        );
299
300        let frame_err = Error::invalid_frame("malformed");
301        assert_eq!(frame_err.to_string(), "Invalid frame format: malformed");
302    }
303
304    #[test]
305    fn test_from_std_io_error() {
306        let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
307        let pjs_err = Error::from(io_err);
308        
309        assert!(matches!(pjs_err, Error::Io(_)));
310        assert!(pjs_err.is_network_error());
311        assert_eq!(pjs_err.category(), "network");
312    }
313
314    #[test]
315    fn test_from_utf8_error() {
316        // Create invalid UTF-8 dynamically to avoid compiler warning
317        let mut invalid_utf8 = vec![0xF0, 0x28, 0x8C, 0xBC]; // Invalid UTF-8 sequence
318        invalid_utf8[1] = 0x28; // Make it definitely invalid
319        
320        let utf8_err = std::str::from_utf8(&invalid_utf8).unwrap_err();
321        let pjs_err = Error::from(utf8_err);
322        
323        assert!(matches!(pjs_err, Error::Utf8(_)));
324        assert_eq!(pjs_err.category(), "encoding");
325    }
326
327    #[test]
328    fn test_from_serde_json_error() {
329        let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
330        let pjs_err = Error::from(json_err);
331        
332        assert!(matches!(pjs_err, Error::Serialization(_)));
333        assert!(pjs_err.is_json_error());
334        assert_eq!(pjs_err.category(), "json");
335    }
336
337    #[test]
338    fn test_error_debug_format() {
339        let err = Error::invalid_json(10, "syntax error");
340        let debug_str = format!("{:?}", err);
341        assert!(debug_str.contains("InvalidJson"));
342        assert!(debug_str.contains("10"));
343        assert!(debug_str.contains("syntax error"));
344    }
345
346    #[test]
347    fn test_error_clone() {
348        let original = Error::semantic_type_mismatch("expected", "actual");
349        let cloned = original.clone();
350        
351        assert_eq!(original.category(), cloned.category());
352        assert_eq!(original.to_string(), cloned.to_string());
353    }
354
355    #[test]
356    fn test_result_type_alias() {
357        fn returns_result() -> Result<i32> {
358            Ok(42)
359        }
360        
361        fn returns_error() -> Result<i32> {
362            Err(Error::other("test error"))
363        }
364        
365        assert_eq!(returns_result().unwrap(), 42);
366        assert!(returns_error().is_err());
367    }
368}