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("{0}")]
77 Other(String),
78}
79
80impl Error {
81 pub fn invalid_json(position: usize, message: impl Into<String>) -> Self {
83 Self::InvalidJson {
84 position,
85 message: message.into(),
86 }
87 }
88
89 pub fn invalid_frame(message: impl Into<String>) -> Self {
91 Self::InvalidFrame(message.into())
92 }
93
94 pub fn schema_validation(message: impl Into<String>) -> Self {
96 Self::SchemaValidation(message.into())
97 }
98
99 pub fn semantic_type_mismatch(expected: impl Into<String>, actual: impl Into<String>) -> Self {
101 Self::SemanticTypeMismatch {
102 expected: expected.into(),
103 actual: actual.into(),
104 }
105 }
106
107 pub fn buffer(message: impl Into<String>) -> Self {
109 Self::Buffer(message.into())
110 }
111
112 pub fn memory(message: impl Into<String>) -> Self {
114 Self::Memory(message.into())
115 }
116
117 pub fn connection_failed(message: impl Into<String>) -> Self {
119 Self::ConnectionFailed(message.into())
120 }
121
122 pub fn client_error(message: impl Into<String>) -> Self {
124 Self::ClientError(message.into())
125 }
126
127 pub fn invalid_session(message: impl Into<String>) -> Self {
129 Self::InvalidSession(message.into())
130 }
131
132 pub fn invalid_url(message: impl Into<String>) -> Self {
134 Self::InvalidUrl(message.into())
135 }
136
137 pub fn serialization(message: impl Into<String>) -> Self {
139 Self::Serialization(message.into())
140 }
141
142 pub fn utf8(message: impl Into<String>) -> Self {
144 Self::Utf8(message.into())
145 }
146
147 pub fn security_error(message: impl Into<String>) -> Self {
149 Self::SecurityError(message.into())
150 }
151
152 pub fn other(message: impl Into<String>) -> Self {
154 Self::Other(message.into())
155 }
156
157 pub fn is_json_error(&self) -> bool {
159 matches!(self, Self::InvalidJson { .. } | Self::Serialization(_))
160 }
161
162 pub fn is_network_error(&self) -> bool {
164 matches!(self, Self::ConnectionFailed(_) | Self::Io(_))
165 }
166
167 pub fn is_validation_error(&self) -> bool {
169 matches!(
170 self,
171 Self::SchemaValidation(_) | Self::SemanticTypeMismatch { .. } | Self::InvalidFrame(_)
172 )
173 }
174
175 pub fn is_security_error(&self) -> bool {
177 matches!(self, Self::SecurityError(_))
178 }
179
180 pub fn category(&self) -> &'static str {
182 match self {
183 Self::InvalidJson { .. } | Self::Serialization(_) => "json",
184 Self::InvalidFrame(_)
185 | Self::SchemaValidation(_)
186 | Self::SemanticTypeMismatch { .. } => "validation",
187 Self::Buffer(_) | Self::Memory(_) => "memory",
188 Self::Io(_) | Self::ConnectionFailed(_) => "network",
189 Self::ClientError(_) | Self::InvalidSession(_) | Self::InvalidUrl(_) => "client",
190 Self::Utf8(_) => "encoding",
191 Self::SecurityError(_) => "security",
192 Self::Other(_) => "other",
193 }
194 }
195}
196
197impl From<std::io::Error> for Error {
198 fn from(err: std::io::Error) -> Self {
199 Error::Io(err.to_string())
200 }
201}
202
203impl From<std::str::Utf8Error> for Error {
204 fn from(err: std::str::Utf8Error) -> Self {
205 Error::Utf8(err.to_string())
206 }
207}
208
209impl From<serde_json::Error> for Error {
210 fn from(err: serde_json::Error) -> Self {
211 Error::Serialization(err.to_string())
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn test_error_creation_constructors() {
221 let json_err = Error::invalid_json(42, "unexpected token");
222 if let Error::InvalidJson { position, message } = json_err {
223 assert_eq!(position, 42);
224 assert_eq!(message, "unexpected token");
225 } else {
226 panic!("Expected InvalidJson error");
227 }
228
229 let frame_err = Error::invalid_frame("malformed frame");
230 assert!(matches!(frame_err, Error::InvalidFrame(_)));
231
232 let schema_err = Error::schema_validation("required field missing");
233 assert!(matches!(schema_err, Error::SchemaValidation(_)));
234
235 let type_err = Error::semantic_type_mismatch("string", "number");
236 if let Error::SemanticTypeMismatch { expected, actual } = type_err {
237 assert_eq!(expected, "string");
238 assert_eq!(actual, "number");
239 } else {
240 panic!("Expected SemanticTypeMismatch error");
241 }
242 }
243
244 #[test]
245 fn test_all_error_constructors() {
246 assert!(matches!(Error::buffer("overflow"), Error::Buffer(_)));
247 assert!(matches!(
248 Error::memory("allocation failed"),
249 Error::Memory(_)
250 ));
251 assert!(matches!(
252 Error::connection_failed("timeout"),
253 Error::ConnectionFailed(_)
254 ));
255 assert!(matches!(
256 Error::client_error("bad request"),
257 Error::ClientError(_)
258 ));
259 assert!(matches!(
260 Error::invalid_session("expired"),
261 Error::InvalidSession(_)
262 ));
263 assert!(matches!(
264 Error::invalid_url("malformed"),
265 Error::InvalidUrl(_)
266 ));
267 assert!(matches!(
268 Error::serialization("json error"),
269 Error::Serialization(_)
270 ));
271 assert!(matches!(Error::utf8("invalid utf8"), Error::Utf8(_)));
272 assert!(matches!(Error::other("unknown"), Error::Other(_)));
273 }
274
275 #[test]
276 fn test_error_classification() {
277 let json_err = Error::invalid_json(0, "test");
278 assert!(json_err.is_json_error());
279 assert!(!json_err.is_network_error());
280 assert!(!json_err.is_validation_error());
281
282 let serialization_err = Error::serialization("test");
283 assert!(serialization_err.is_json_error());
284
285 let network_err = Error::connection_failed("test");
286 assert!(network_err.is_network_error());
287 assert!(!network_err.is_json_error());
288
289 let io_err = Error::Io("test".to_string());
290 assert!(io_err.is_network_error());
291
292 let validation_err = Error::schema_validation("test");
293 assert!(validation_err.is_validation_error());
294 assert!(!validation_err.is_json_error());
295
296 let frame_err = Error::invalid_frame("test");
297 assert!(frame_err.is_validation_error());
298
299 let type_err = Error::semantic_type_mismatch("a", "b");
300 assert!(type_err.is_validation_error());
301 }
302
303 #[test]
304 fn test_error_categories() {
305 assert_eq!(Error::invalid_json(0, "test").category(), "json");
306 assert_eq!(Error::serialization("test").category(), "json");
307 assert_eq!(Error::invalid_frame("test").category(), "validation");
308 assert_eq!(Error::schema_validation("test").category(), "validation");
309 assert_eq!(
310 Error::semantic_type_mismatch("a", "b").category(),
311 "validation"
312 );
313 assert_eq!(Error::buffer("test").category(), "memory");
314 assert_eq!(Error::memory("test").category(), "memory");
315 assert_eq!(Error::Io("test".to_string()).category(), "network");
316 assert_eq!(Error::connection_failed("test").category(), "network");
317 assert_eq!(Error::client_error("test").category(), "client");
318 assert_eq!(Error::invalid_session("test").category(), "client");
319 assert_eq!(Error::invalid_url("test").category(), "client");
320 assert_eq!(Error::utf8("test").category(), "encoding");
321 assert_eq!(Error::other("test").category(), "other");
322 }
323
324 #[test]
325 fn test_error_display() {
326 let json_err = Error::invalid_json(42, "unexpected token");
327 assert_eq!(
328 json_err.to_string(),
329 "Invalid JSON syntax at position 42: unexpected token"
330 );
331
332 let type_err = Error::semantic_type_mismatch("string", "number");
333 assert_eq!(
334 type_err.to_string(),
335 "Semantic type mismatch: expected string, got number"
336 );
337
338 let frame_err = Error::invalid_frame("malformed");
339 assert_eq!(frame_err.to_string(), "Invalid frame format: malformed");
340 }
341
342 #[test]
343 fn test_from_std_io_error() {
344 let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
345 let pjs_err = Error::from(io_err);
346
347 assert!(matches!(pjs_err, Error::Io(_)));
348 assert!(pjs_err.is_network_error());
349 assert_eq!(pjs_err.category(), "network");
350 }
351
352 #[test]
353 fn test_from_utf8_error() {
354 let mut invalid_utf8 = vec![0xF0, 0x28, 0x8C, 0xBC]; invalid_utf8[1] = 0x28; let utf8_err = std::str::from_utf8(&invalid_utf8).unwrap_err();
359 let pjs_err = Error::from(utf8_err);
360
361 assert!(matches!(pjs_err, Error::Utf8(_)));
362 assert_eq!(pjs_err.category(), "encoding");
363 }
364
365 #[test]
366 fn test_from_serde_json_error() {
367 let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
368 let pjs_err = Error::from(json_err);
369
370 assert!(matches!(pjs_err, Error::Serialization(_)));
371 assert!(pjs_err.is_json_error());
372 assert_eq!(pjs_err.category(), "json");
373 }
374
375 #[test]
376 fn test_error_debug_format() {
377 let err = Error::invalid_json(10, "syntax error");
378 let debug_str = format!("{:?}", err);
379 assert!(debug_str.contains("InvalidJson"));
380 assert!(debug_str.contains("10"));
381 assert!(debug_str.contains("syntax error"));
382 }
383
384 #[test]
385 fn test_error_clone() {
386 let original = Error::semantic_type_mismatch("expected", "actual");
387 let cloned = original.clone();
388
389 assert_eq!(original.category(), cloned.category());
390 assert_eq!(original.to_string(), cloned.to_string());
391 }
392
393 #[test]
394 fn test_result_type_alias() {
395 fn returns_result() -> Result<i32> {
396 Ok(42)
397 }
398
399 fn returns_error() -> Result<i32> {
400 Err(Error::other("test error"))
401 }
402
403 assert_eq!(returns_result().unwrap(), 42);
404 assert!(returns_error().is_err());
405 }
406}