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("{0}")]
73 Other(String),
74}
75
76impl Error {
77 pub fn invalid_json(position: usize, message: impl Into<String>) -> Self {
79 Self::InvalidJson {
80 position,
81 message: message.into(),
82 }
83 }
84
85 pub fn invalid_frame(message: impl Into<String>) -> Self {
87 Self::InvalidFrame(message.into())
88 }
89
90 pub fn schema_validation(message: impl Into<String>) -> Self {
92 Self::SchemaValidation(message.into())
93 }
94
95 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 pub fn buffer(message: impl Into<String>) -> Self {
105 Self::Buffer(message.into())
106 }
107
108 pub fn memory(message: impl Into<String>) -> Self {
110 Self::Memory(message.into())
111 }
112
113 pub fn connection_failed(message: impl Into<String>) -> Self {
115 Self::ConnectionFailed(message.into())
116 }
117
118 pub fn client_error(message: impl Into<String>) -> Self {
120 Self::ClientError(message.into())
121 }
122
123 pub fn invalid_session(message: impl Into<String>) -> Self {
125 Self::InvalidSession(message.into())
126 }
127
128 pub fn invalid_url(message: impl Into<String>) -> Self {
130 Self::InvalidUrl(message.into())
131 }
132
133 pub fn serialization(message: impl Into<String>) -> Self {
135 Self::Serialization(message.into())
136 }
137
138 pub fn utf8(message: impl Into<String>) -> Self {
140 Self::Utf8(message.into())
141 }
142
143 pub fn other(message: impl Into<String>) -> Self {
145 Self::Other(message.into())
146 }
147
148 pub fn is_json_error(&self) -> bool {
150 matches!(self, Self::InvalidJson { .. } | Self::Serialization(_))
151 }
152
153 pub fn is_network_error(&self) -> bool {
155 matches!(self, Self::ConnectionFailed(_) | Self::Io(_))
156 }
157
158 pub fn is_validation_error(&self) -> bool {
160 matches!(
161 self,
162 Self::SchemaValidation(_) | Self::SemanticTypeMismatch { .. } | Self::InvalidFrame(_)
163 )
164 }
165
166 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 let mut invalid_utf8 = vec![0xF0, 0x28, 0x8C, 0xBC]; invalid_utf8[1] = 0x28; 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}