1pub type Result<T> = std::result::Result<T, Error>;
5
6#[derive(Debug, Clone, thiserror::Error)]
8pub enum Error {
9 #[error("Invalid JSON syntax at position {position}: {message}")]
11 InvalidJson {
12 position: usize,
14 message: String,
16 },
17
18 #[error("Invalid frame format: {0}")]
20 InvalidFrame(String),
21
22 #[error("Schema validation failed: {0}")]
24 SchemaValidation(String),
25
26 #[error("Semantic type mismatch: expected {expected}, got {actual}")]
28 SemanticTypeMismatch {
29 expected: String,
31 actual: String,
33 },
34
35 #[error("Buffer error: {0}")]
37 Buffer(String),
38
39 #[error("Memory allocation failed: {0}")]
41 Memory(String),
42
43 #[error("I/O error: {0}")]
45 Io(String),
46
47 #[error("Connection failed: {0}")]
49 ConnectionFailed(String),
50
51 #[error("Client error: {0}")]
53 ClientError(String),
54
55 #[error("Invalid session: {0}")]
57 InvalidSession(String),
58
59 #[error("Invalid URL: {0}")]
61 InvalidUrl(String),
62
63 #[error("Serialization error: {0}")]
65 Serialization(String),
66
67 #[error("UTF-8 conversion failed: {0}")]
69 Utf8(String),
70
71 #[error("Security error: {0}")]
73 SecurityError(String),
74
75 #[error("Compression error: {0}")]
77 CompressionError(String),
78
79 #[error("{0}")]
81 Other(String),
82}
83
84impl Error {
85 pub fn invalid_json(position: usize, message: impl Into<String>) -> Self {
87 Self::InvalidJson {
88 position,
89 message: message.into(),
90 }
91 }
92
93 pub fn invalid_frame(message: impl Into<String>) -> Self {
95 Self::InvalidFrame(message.into())
96 }
97
98 pub fn schema_validation(message: impl Into<String>) -> Self {
100 Self::SchemaValidation(message.into())
101 }
102
103 pub fn semantic_type_mismatch(expected: impl Into<String>, actual: impl Into<String>) -> Self {
105 Self::SemanticTypeMismatch {
106 expected: expected.into(),
107 actual: actual.into(),
108 }
109 }
110
111 pub fn buffer(message: impl Into<String>) -> Self {
113 Self::Buffer(message.into())
114 }
115
116 pub fn memory(message: impl Into<String>) -> Self {
118 Self::Memory(message.into())
119 }
120
121 pub fn connection_failed(message: impl Into<String>) -> Self {
123 Self::ConnectionFailed(message.into())
124 }
125
126 pub fn client_error(message: impl Into<String>) -> Self {
128 Self::ClientError(message.into())
129 }
130
131 pub fn invalid_session(message: impl Into<String>) -> Self {
133 Self::InvalidSession(message.into())
134 }
135
136 pub fn invalid_url(message: impl Into<String>) -> Self {
138 Self::InvalidUrl(message.into())
139 }
140
141 pub fn serialization(message: impl Into<String>) -> Self {
143 Self::Serialization(message.into())
144 }
145
146 pub fn utf8(message: impl Into<String>) -> Self {
148 Self::Utf8(message.into())
149 }
150
151 pub fn security_error(message: impl Into<String>) -> Self {
153 Self::SecurityError(message.into())
154 }
155
156 pub fn compression_error(message: impl Into<String>) -> Self {
158 Self::CompressionError(message.into())
159 }
160
161 pub fn other(message: impl Into<String>) -> Self {
163 Self::Other(message.into())
164 }
165
166 pub fn is_json_error(&self) -> bool {
168 matches!(self, Self::InvalidJson { .. } | Self::Serialization(_))
169 }
170
171 pub fn is_network_error(&self) -> bool {
173 matches!(self, Self::ConnectionFailed(_) | Self::Io(_))
174 }
175
176 pub fn is_validation_error(&self) -> bool {
178 matches!(
179 self,
180 Self::SchemaValidation(_) | Self::SemanticTypeMismatch { .. } | Self::InvalidFrame(_)
181 )
182 }
183
184 pub fn is_security_error(&self) -> bool {
186 matches!(self, Self::SecurityError(_))
187 }
188
189 pub fn category(&self) -> &'static str {
191 match self {
192 Self::InvalidJson { .. } | Self::Serialization(_) => "json",
193 Self::InvalidFrame(_)
194 | Self::SchemaValidation(_)
195 | Self::SemanticTypeMismatch { .. } => "validation",
196 Self::Buffer(_) | Self::Memory(_) => "memory",
197 Self::Io(_) | Self::ConnectionFailed(_) => "network",
198 Self::ClientError(_) | Self::InvalidSession(_) | Self::InvalidUrl(_) => "client",
199 Self::Utf8(_) => "encoding",
200 Self::SecurityError(_) => "security",
201 Self::CompressionError(_) => "compression",
202 Self::Other(_) => "other",
203 }
204 }
205}
206
207impl From<std::io::Error> for Error {
208 fn from(err: std::io::Error) -> Self {
209 Error::Io(err.to_string())
210 }
211}
212
213impl From<std::str::Utf8Error> for Error {
214 fn from(err: std::str::Utf8Error) -> Self {
215 Error::Utf8(err.to_string())
216 }
217}
218
219impl From<serde_json::Error> for Error {
220 fn from(err: serde_json::Error) -> Self {
221 Error::Serialization(err.to_string())
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn test_error_creation_constructors() {
231 let json_err = Error::invalid_json(42, "unexpected token");
232 if let Error::InvalidJson { position, message } = json_err {
233 assert_eq!(position, 42);
234 assert_eq!(message, "unexpected token");
235 } else {
236 panic!("Expected InvalidJson error");
237 }
238
239 let frame_err = Error::invalid_frame("malformed frame");
240 assert!(matches!(frame_err, Error::InvalidFrame(_)));
241
242 let schema_err = Error::schema_validation("required field missing");
243 assert!(matches!(schema_err, Error::SchemaValidation(_)));
244
245 let type_err = Error::semantic_type_mismatch("string", "number");
246 if let Error::SemanticTypeMismatch { expected, actual } = type_err {
247 assert_eq!(expected, "string");
248 assert_eq!(actual, "number");
249 } else {
250 panic!("Expected SemanticTypeMismatch error");
251 }
252 }
253
254 #[test]
255 fn test_all_error_constructors() {
256 assert!(matches!(Error::buffer("overflow"), Error::Buffer(_)));
257 assert!(matches!(
258 Error::memory("allocation failed"),
259 Error::Memory(_)
260 ));
261 assert!(matches!(
262 Error::connection_failed("timeout"),
263 Error::ConnectionFailed(_)
264 ));
265 assert!(matches!(
266 Error::client_error("bad request"),
267 Error::ClientError(_)
268 ));
269 assert!(matches!(
270 Error::invalid_session("expired"),
271 Error::InvalidSession(_)
272 ));
273 assert!(matches!(
274 Error::invalid_url("malformed"),
275 Error::InvalidUrl(_)
276 ));
277 assert!(matches!(
278 Error::serialization("json error"),
279 Error::Serialization(_)
280 ));
281 assert!(matches!(Error::utf8("invalid utf8"), Error::Utf8(_)));
282 assert!(matches!(Error::other("unknown"), Error::Other(_)));
283 }
284
285 #[test]
286 fn test_error_classification() {
287 let json_err = Error::invalid_json(0, "test");
288 assert!(json_err.is_json_error());
289 assert!(!json_err.is_network_error());
290 assert!(!json_err.is_validation_error());
291
292 let serialization_err = Error::serialization("test");
293 assert!(serialization_err.is_json_error());
294
295 let network_err = Error::connection_failed("test");
296 assert!(network_err.is_network_error());
297 assert!(!network_err.is_json_error());
298
299 let io_err = Error::Io("test".to_string());
300 assert!(io_err.is_network_error());
301
302 let validation_err = Error::schema_validation("test");
303 assert!(validation_err.is_validation_error());
304 assert!(!validation_err.is_json_error());
305
306 let frame_err = Error::invalid_frame("test");
307 assert!(frame_err.is_validation_error());
308
309 let type_err = Error::semantic_type_mismatch("a", "b");
310 assert!(type_err.is_validation_error());
311 }
312
313 #[test]
314 fn test_error_categories() {
315 assert_eq!(Error::invalid_json(0, "test").category(), "json");
316 assert_eq!(Error::serialization("test").category(), "json");
317 assert_eq!(Error::invalid_frame("test").category(), "validation");
318 assert_eq!(Error::schema_validation("test").category(), "validation");
319 assert_eq!(
320 Error::semantic_type_mismatch("a", "b").category(),
321 "validation"
322 );
323 assert_eq!(Error::buffer("test").category(), "memory");
324 assert_eq!(Error::memory("test").category(), "memory");
325 assert_eq!(Error::Io("test".to_string()).category(), "network");
326 assert_eq!(Error::connection_failed("test").category(), "network");
327 assert_eq!(Error::client_error("test").category(), "client");
328 assert_eq!(Error::invalid_session("test").category(), "client");
329 assert_eq!(Error::invalid_url("test").category(), "client");
330 assert_eq!(Error::utf8("test").category(), "encoding");
331 assert_eq!(Error::other("test").category(), "other");
332 }
333
334 #[test]
335 fn test_error_display() {
336 let json_err = Error::invalid_json(42, "unexpected token");
337 assert_eq!(
338 json_err.to_string(),
339 "Invalid JSON syntax at position 42: unexpected token"
340 );
341
342 let type_err = Error::semantic_type_mismatch("string", "number");
343 assert_eq!(
344 type_err.to_string(),
345 "Semantic type mismatch: expected string, got number"
346 );
347
348 let frame_err = Error::invalid_frame("malformed");
349 assert_eq!(frame_err.to_string(), "Invalid frame format: malformed");
350 }
351
352 #[test]
353 fn test_from_std_io_error() {
354 let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
355 let pjs_err = Error::from(io_err);
356
357 assert!(matches!(pjs_err, Error::Io(_)));
358 assert!(pjs_err.is_network_error());
359 assert_eq!(pjs_err.category(), "network");
360 }
361
362 #[test]
363 fn test_from_utf8_error() {
364 let mut invalid_utf8 = vec![0xF0, 0x28, 0x8C, 0xBC]; invalid_utf8[1] = 0x28; let utf8_err = std::str::from_utf8(&invalid_utf8).unwrap_err();
369 let pjs_err = Error::from(utf8_err);
370
371 assert!(matches!(pjs_err, Error::Utf8(_)));
372 assert_eq!(pjs_err.category(), "encoding");
373 }
374
375 #[test]
376 fn test_from_serde_json_error() {
377 let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
378 let pjs_err = Error::from(json_err);
379
380 assert!(matches!(pjs_err, Error::Serialization(_)));
381 assert!(pjs_err.is_json_error());
382 assert_eq!(pjs_err.category(), "json");
383 }
384
385 #[test]
386 fn test_error_debug_format() {
387 let err = Error::invalid_json(10, "syntax error");
388 let debug_str = format!("{:?}", err);
389 assert!(debug_str.contains("InvalidJson"));
390 assert!(debug_str.contains("10"));
391 assert!(debug_str.contains("syntax error"));
392 }
393
394 #[test]
395 fn test_error_clone() {
396 let original = Error::semantic_type_mismatch("expected", "actual");
397 let cloned = original.clone();
398
399 assert_eq!(original.category(), cloned.category());
400 assert_eq!(original.to_string(), cloned.to_string());
401 }
402
403 #[test]
404 fn test_result_type_alias() {
405 fn returns_result() -> Result<i32> {
406 Ok(42)
407 }
408
409 fn returns_error() -> Result<i32> {
410 Err(Error::other("test error"))
411 }
412
413 assert_eq!(returns_result().unwrap(), 42);
414 assert!(returns_error().is_err());
415 }
416}