1pub type Result<T> = core::result::Result<T, Error>;
16
17#[derive(thiserror::Error, Debug)]
18pub enum Error {
19 #[error("File not found")]
20 FileNotFound,
21 #[error("File version not found")]
22 FileVersionNotFound,
23
24 #[error("Volume not found")]
25 VolumeNotFound,
26
27 #[error("File corrupt")]
28 FileCorrupt,
29
30 #[error("Done for now")]
31 DoneForNow,
32
33 #[error("Method not allowed")]
34 MethodNotAllowed,
35
36 #[error("Unexpected error")]
37 Unexpected,
38
39 #[error("I/O error: {0}")]
40 Io(std::io::Error),
41
42 #[error("rmp serde decode error: {0}")]
43 RmpSerdeDecode(String),
44
45 #[error("rmp serde encode error: {0}")]
46 RmpSerdeEncode(String),
47
48 #[error("Invalid UTF-8: {0}")]
49 FromUtf8(String),
50
51 #[error("rmp decode value read error: {0}")]
52 RmpDecodeValueRead(String),
53
54 #[error("rmp encode value write error: {0}")]
55 RmpEncodeValueWrite(String),
56
57 #[error("rmp decode num value read error: {0}")]
58 RmpDecodeNumValueRead(String),
59
60 #[error("rmp decode marker read error: {0}")]
61 RmpDecodeMarkerRead(String),
62
63 #[error("time component range error: {0}")]
64 TimeComponentRange(String),
65
66 #[error("uuid parse error: {0}")]
67 UuidParse(String),
68}
69
70impl Error {
71 pub fn other<E>(error: E) -> Error
72 where
73 E: Into<Box<dyn std::error::Error + Send + Sync>>,
74 {
75 std::io::Error::other(error).into()
76 }
77}
78
79impl PartialEq for Error {
80 fn eq(&self, other: &Self) -> bool {
81 match (self, other) {
82 (Error::FileCorrupt, Error::FileCorrupt) => true,
83 (Error::DoneForNow, Error::DoneForNow) => true,
84 (Error::MethodNotAllowed, Error::MethodNotAllowed) => true,
85 (Error::FileNotFound, Error::FileNotFound) => true,
86 (Error::FileVersionNotFound, Error::FileVersionNotFound) => true,
87 (Error::VolumeNotFound, Error::VolumeNotFound) => true,
88 (Error::Io(e1), Error::Io(e2)) => e1.kind() == e2.kind() && e1.to_string() == e2.to_string(),
89 (Error::RmpSerdeDecode(e1), Error::RmpSerdeDecode(e2)) => e1 == e2,
90 (Error::RmpSerdeEncode(e1), Error::RmpSerdeEncode(e2)) => e1 == e2,
91 (Error::RmpDecodeValueRead(e1), Error::RmpDecodeValueRead(e2)) => e1 == e2,
92 (Error::RmpEncodeValueWrite(e1), Error::RmpEncodeValueWrite(e2)) => e1 == e2,
93 (Error::RmpDecodeNumValueRead(e1), Error::RmpDecodeNumValueRead(e2)) => e1 == e2,
94 (Error::TimeComponentRange(e1), Error::TimeComponentRange(e2)) => e1 == e2,
95 (Error::UuidParse(e1), Error::UuidParse(e2)) => e1 == e2,
96 (Error::Unexpected, Error::Unexpected) => true,
97 (a, b) => a.to_string() == b.to_string(),
98 }
99 }
100}
101
102impl Clone for Error {
103 fn clone(&self) -> Self {
104 match self {
105 Error::FileNotFound => Error::FileNotFound,
106 Error::FileVersionNotFound => Error::FileVersionNotFound,
107 Error::FileCorrupt => Error::FileCorrupt,
108 Error::DoneForNow => Error::DoneForNow,
109 Error::MethodNotAllowed => Error::MethodNotAllowed,
110 Error::VolumeNotFound => Error::VolumeNotFound,
111 Error::Io(e) => Error::Io(std::io::Error::new(e.kind(), e.to_string())),
112 Error::RmpSerdeDecode(s) => Error::RmpSerdeDecode(s.clone()),
113 Error::RmpSerdeEncode(s) => Error::RmpSerdeEncode(s.clone()),
114 Error::FromUtf8(s) => Error::FromUtf8(s.clone()),
115 Error::RmpDecodeValueRead(s) => Error::RmpDecodeValueRead(s.clone()),
116 Error::RmpEncodeValueWrite(s) => Error::RmpEncodeValueWrite(s.clone()),
117 Error::RmpDecodeNumValueRead(s) => Error::RmpDecodeNumValueRead(s.clone()),
118 Error::RmpDecodeMarkerRead(s) => Error::RmpDecodeMarkerRead(s.clone()),
119 Error::TimeComponentRange(s) => Error::TimeComponentRange(s.clone()),
120 Error::UuidParse(s) => Error::UuidParse(s.clone()),
121 Error::Unexpected => Error::Unexpected,
122 }
123 }
124}
125
126impl From<std::io::Error> for Error {
127 fn from(e: std::io::Error) -> Self {
128 match e.kind() {
129 std::io::ErrorKind::UnexpectedEof => Error::Unexpected,
130 _ => Error::Io(e),
131 }
132 }
133}
134
135impl From<Error> for std::io::Error {
136 fn from(e: Error) -> Self {
137 match e {
138 Error::Unexpected => std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "Unexpected EOF"),
139 Error::Io(e) => e,
140 _ => std::io::Error::other(e.to_string()),
141 }
142 }
143}
144
145impl From<rmp_serde::decode::Error> for Error {
146 fn from(e: rmp_serde::decode::Error) -> Self {
147 Error::RmpSerdeDecode(e.to_string())
148 }
149}
150
151impl From<rmp_serde::encode::Error> for Error {
152 fn from(e: rmp_serde::encode::Error) -> Self {
153 Error::RmpSerdeEncode(e.to_string())
154 }
155}
156
157impl From<std::string::FromUtf8Error> for Error {
158 fn from(e: std::string::FromUtf8Error) -> Self {
159 Error::FromUtf8(e.to_string())
160 }
161}
162
163impl From<rmp::decode::ValueReadError> for Error {
164 fn from(e: rmp::decode::ValueReadError) -> Self {
165 Error::RmpDecodeValueRead(e.to_string())
166 }
167}
168
169impl From<rmp::encode::ValueWriteError> for Error {
170 fn from(e: rmp::encode::ValueWriteError) -> Self {
171 Error::RmpEncodeValueWrite(e.to_string())
172 }
173}
174
175impl From<rmp::decode::NumValueReadError> for Error {
176 fn from(e: rmp::decode::NumValueReadError) -> Self {
177 Error::RmpDecodeNumValueRead(e.to_string())
178 }
179}
180
181impl From<time::error::ComponentRange> for Error {
182 fn from(e: time::error::ComponentRange) -> Self {
183 Error::TimeComponentRange(e.to_string())
184 }
185}
186
187impl From<uuid::Error> for Error {
188 fn from(e: uuid::Error) -> Self {
189 Error::UuidParse(e.to_string())
190 }
191}
192
193impl From<rmp::decode::MarkerReadError> for Error {
194 fn from(e: rmp::decode::MarkerReadError) -> Self {
195 let serr = format!("{e:?}");
196 Error::RmpDecodeMarkerRead(serr)
197 }
198}
199
200pub fn is_io_eof(e: &Error) -> bool {
201 match e {
202 Error::Io(e) => e.kind() == std::io::ErrorKind::UnexpectedEof,
203 _ => false,
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use std::io::{Error as IoError, ErrorKind};
211
212 #[test]
213 fn test_filemeta_error_from_io_error() {
214 let io_error = IoError::new(ErrorKind::PermissionDenied, "permission denied");
215 let filemeta_error: Error = io_error.into();
216
217 match filemeta_error {
218 Error::Io(inner_io) => {
219 assert_eq!(inner_io.kind(), ErrorKind::PermissionDenied);
220 assert!(inner_io.to_string().contains("permission denied"));
221 }
222 _ => panic!("Expected Io variant"),
223 }
224 }
225
226 #[test]
227 fn test_filemeta_error_other_function() {
228 let custom_error = "Custom filemeta error";
229 let filemeta_error = Error::other(custom_error);
230
231 match filemeta_error {
232 Error::Io(io_error) => {
233 assert!(io_error.to_string().contains(custom_error));
234 assert_eq!(io_error.kind(), ErrorKind::Other);
235 }
236 _ => panic!("Expected Io variant"),
237 }
238 }
239
240 #[test]
241 fn test_filemeta_error_conversions() {
242 let serde_decode_err =
244 rmp_serde::decode::Error::InvalidMarkerRead(std::io::Error::new(ErrorKind::InvalidData, "invalid"));
245 let filemeta_error: Error = serde_decode_err.into();
246 assert!(matches!(filemeta_error, Error::RmpSerdeDecode(_)));
247
248 let encode_error_string = "test encode error";
250 let filemeta_error = Error::RmpSerdeEncode(encode_error_string.to_string());
251 assert!(matches!(filemeta_error, Error::RmpSerdeEncode(_)));
252
253 let utf8_err = std::string::String::from_utf8(vec![0xFF]).unwrap_err();
254 let filemeta_error: Error = utf8_err.into();
255 assert!(matches!(filemeta_error, Error::FromUtf8(_)));
256 }
257
258 #[test]
259 fn test_filemeta_error_clone() {
260 let test_cases = vec![
261 Error::FileNotFound,
262 Error::FileVersionNotFound,
263 Error::VolumeNotFound,
264 Error::FileCorrupt,
265 Error::DoneForNow,
266 Error::MethodNotAllowed,
267 Error::Unexpected,
268 Error::Io(IoError::new(ErrorKind::NotFound, "test")),
269 Error::RmpSerdeDecode("test decode error".to_string()),
270 Error::RmpSerdeEncode("test encode error".to_string()),
271 Error::FromUtf8("test utf8 error".to_string()),
272 Error::RmpDecodeValueRead("test value read error".to_string()),
273 Error::RmpEncodeValueWrite("test value write error".to_string()),
274 Error::RmpDecodeNumValueRead("test num read error".to_string()),
275 Error::RmpDecodeMarkerRead("test marker read error".to_string()),
276 Error::TimeComponentRange("test time error".to_string()),
277 Error::UuidParse("test uuid error".to_string()),
278 ];
279
280 for original_error in test_cases {
281 let cloned_error = original_error.clone();
282 assert_eq!(original_error, cloned_error);
283 }
284 }
285
286 #[test]
287 fn test_filemeta_error_partial_eq() {
288 assert_eq!(Error::FileNotFound, Error::FileNotFound);
290 assert_ne!(Error::FileNotFound, Error::FileVersionNotFound);
291
292 let io1 = Error::Io(IoError::new(ErrorKind::NotFound, "test"));
294 let io2 = Error::Io(IoError::new(ErrorKind::NotFound, "test"));
295 let io3 = Error::Io(IoError::new(ErrorKind::PermissionDenied, "test"));
296 assert_eq!(io1, io2);
297 assert_ne!(io1, io3);
298
299 let decode1 = Error::RmpSerdeDecode("error message".to_string());
301 let decode2 = Error::RmpSerdeDecode("error message".to_string());
302 let decode3 = Error::RmpSerdeDecode("different message".to_string());
303 assert_eq!(decode1, decode2);
304 assert_ne!(decode1, decode3);
305 }
306
307 #[test]
308 fn test_filemeta_error_display() {
309 let test_cases = vec![
310 (Error::FileNotFound, "File not found"),
311 (Error::FileVersionNotFound, "File version not found"),
312 (Error::VolumeNotFound, "Volume not found"),
313 (Error::FileCorrupt, "File corrupt"),
314 (Error::DoneForNow, "Done for now"),
315 (Error::MethodNotAllowed, "Method not allowed"),
316 (Error::Unexpected, "Unexpected error"),
317 (Error::RmpSerdeDecode("test".to_string()), "rmp serde decode error: test"),
318 (Error::RmpSerdeEncode("test".to_string()), "rmp serde encode error: test"),
319 (Error::FromUtf8("test".to_string()), "Invalid UTF-8: test"),
320 (Error::TimeComponentRange("test".to_string()), "time component range error: test"),
321 (Error::UuidParse("test".to_string()), "uuid parse error: test"),
322 ];
323
324 for (error, expected_message) in test_cases {
325 assert_eq!(error.to_string(), expected_message);
326 }
327 }
328
329 #[test]
330 fn test_rmp_conversions() {
331 let value_read_err = rmp::decode::ValueReadError::InvalidMarkerRead(std::io::Error::new(ErrorKind::InvalidData, "test"));
333 let filemeta_error: Error = value_read_err.into();
334 assert!(matches!(filemeta_error, Error::RmpDecodeValueRead(_)));
335
336 let num_value_err =
338 rmp::decode::NumValueReadError::InvalidMarkerRead(std::io::Error::new(ErrorKind::InvalidData, "test"));
339 let filemeta_error: Error = num_value_err.into();
340 assert!(matches!(filemeta_error, Error::RmpDecodeNumValueRead(_)));
341 }
342
343 #[test]
344 fn test_time_and_uuid_conversions() {
345 use time::{Date, Month};
347 let time_result = Date::from_calendar_date(2023, Month::January, 32); assert!(time_result.is_err());
349 let time_error = time_result.unwrap_err();
350 let filemeta_error: Error = time_error.into();
351 assert!(matches!(filemeta_error, Error::TimeComponentRange(_)));
352
353 let uuid_result = uuid::Uuid::parse_str("invalid-uuid");
355 assert!(uuid_result.is_err());
356 let uuid_error = uuid_result.unwrap_err();
357 let filemeta_error: Error = uuid_error.into();
358 assert!(matches!(filemeta_error, Error::UuidParse(_)));
359 }
360
361 #[test]
362 fn test_marker_read_error_conversion() {
363 let marker_err = rmp::decode::MarkerReadError(std::io::Error::new(ErrorKind::InvalidData, "marker test"));
365 let filemeta_error: Error = marker_err.into();
366 assert!(matches!(filemeta_error, Error::RmpDecodeMarkerRead(_)));
367 assert!(filemeta_error.to_string().contains("marker"));
368 }
369
370 #[test]
371 fn test_is_io_eof_function() {
372 let eof_error = Error::Io(IoError::new(ErrorKind::UnexpectedEof, "eof"));
374 assert!(is_io_eof(&eof_error));
375
376 let not_eof_error = Error::Io(IoError::new(ErrorKind::NotFound, "not found"));
377 assert!(!is_io_eof(¬_eof_error));
378
379 let non_io_error = Error::FileNotFound;
380 assert!(!is_io_eof(&non_io_error));
381 }
382
383 #[test]
384 fn test_filemeta_error_to_io_error_conversion() {
385 let original_io_error = IoError::new(ErrorKind::InvalidData, "test data");
387 let filemeta_error = Error::other(original_io_error);
388
389 match filemeta_error {
390 Error::Io(io_err) => {
391 assert_eq!(io_err.kind(), ErrorKind::Other);
392 assert!(io_err.to_string().contains("test data"));
393 }
394 _ => panic!("Expected Io variant"),
395 }
396 }
397
398 #[test]
399 fn test_filemeta_error_roundtrip_conversion() {
400 let original_io_error = IoError::new(ErrorKind::PermissionDenied, "permission test");
402
403 let filemeta_error: Error = original_io_error.into();
405
406 match filemeta_error {
408 Error::Io(extracted_io_error) => {
409 assert_eq!(extracted_io_error.kind(), ErrorKind::PermissionDenied);
410 assert!(extracted_io_error.to_string().contains("permission test"));
411 }
412 _ => panic!("Expected Io variant"),
413 }
414 }
415
416 #[test]
417 fn test_filemeta_error_io_error_kinds_preservation() {
418 let io_error_kinds = vec![
419 ErrorKind::NotFound,
420 ErrorKind::PermissionDenied,
421 ErrorKind::ConnectionRefused,
422 ErrorKind::ConnectionReset,
423 ErrorKind::ConnectionAborted,
424 ErrorKind::NotConnected,
425 ErrorKind::AddrInUse,
426 ErrorKind::AddrNotAvailable,
427 ErrorKind::BrokenPipe,
428 ErrorKind::AlreadyExists,
429 ErrorKind::WouldBlock,
430 ErrorKind::InvalidInput,
431 ErrorKind::InvalidData,
432 ErrorKind::TimedOut,
433 ErrorKind::WriteZero,
434 ErrorKind::Interrupted,
435 ErrorKind::UnexpectedEof,
436 ErrorKind::Other,
437 ];
438
439 for kind in io_error_kinds {
440 let io_error = IoError::new(kind, format!("test error for {kind:?}"));
441 let filemeta_error: Error = io_error.into();
442
443 match filemeta_error {
444 Error::Unexpected => {
445 assert_eq!(kind, ErrorKind::UnexpectedEof);
446 }
447 Error::Io(extracted_io_error) => {
448 assert_eq!(extracted_io_error.kind(), kind);
449 assert!(extracted_io_error.to_string().contains("test error"));
450 }
451 _ => panic!("Expected Io variant for kind {kind:?}"),
452 }
453 }
454 }
455
456 #[test]
457 fn test_filemeta_error_downcast_chain() {
458 let original_io_error = IoError::new(ErrorKind::InvalidData, "original error");
460 let filemeta_error = Error::other(original_io_error);
461
462 if let Error::Io(io_err) = filemeta_error {
464 assert_eq!(io_err.kind(), ErrorKind::Other);
466 assert!(io_err.to_string().contains("original error"));
468 } else {
469 panic!("Expected Io variant");
470 }
471 }
472
473 #[test]
474 fn test_filemeta_error_maintains_error_information() {
475 let test_cases = vec![
476 (ErrorKind::NotFound, "file not found"),
477 (ErrorKind::PermissionDenied, "access denied"),
478 (ErrorKind::InvalidData, "corrupt data"),
479 (ErrorKind::TimedOut, "operation timed out"),
480 ];
481
482 for (kind, message) in test_cases {
483 let io_error = IoError::new(kind, message);
484 let error_message = io_error.to_string();
485 let filemeta_error: Error = io_error.into();
486
487 match filemeta_error {
488 Error::Io(extracted_io_error) => {
489 assert_eq!(extracted_io_error.kind(), kind);
490 assert_eq!(extracted_io_error.to_string(), error_message);
491 }
492 _ => panic!("Expected Io variant"),
493 }
494 }
495 }
496
497 #[test]
498 fn test_filemeta_error_complex_conversion_chain() {
499 let uuid_result = uuid::Uuid::parse_str("invalid-uuid-format");
503 assert!(uuid_result.is_err());
504 let uuid_error = uuid_result.unwrap_err();
505 let filemeta_error: Error = uuid_error.into();
506
507 match filemeta_error {
508 Error::UuidParse(message) => {
509 assert!(message.contains("invalid"));
510 }
511 _ => panic!("Expected UuidParse variant"),
512 }
513
514 use time::{Date, Month};
516 let time_result = Date::from_calendar_date(2023, Month::January, 32); assert!(time_result.is_err());
518 let time_error = time_result.unwrap_err();
519 let filemeta_error2: Error = time_error.into();
520
521 match filemeta_error2 {
522 Error::TimeComponentRange(message) => {
523 assert!(message.contains("range"));
524 }
525 _ => panic!("Expected TimeComponentRange variant"),
526 }
527
528 let utf8_result = std::string::String::from_utf8(vec![0xFF]);
530 assert!(utf8_result.is_err());
531 let utf8_error = utf8_result.unwrap_err();
532 let filemeta_error3: Error = utf8_error.into();
533
534 match filemeta_error3 {
535 Error::FromUtf8(message) => {
536 assert!(message.contains("utf"));
537 }
538 _ => panic!("Expected FromUtf8 variant"),
539 }
540 }
541
542 #[test]
543 fn test_filemeta_error_equality_with_io_errors() {
544 let io_error1 = IoError::new(ErrorKind::NotFound, "test message");
546 let io_error2 = IoError::new(ErrorKind::NotFound, "test message");
547 let io_error3 = IoError::new(ErrorKind::PermissionDenied, "test message");
548 let io_error4 = IoError::new(ErrorKind::NotFound, "different message");
549
550 let filemeta_error1 = Error::Io(io_error1);
551 let filemeta_error2 = Error::Io(io_error2);
552 let filemeta_error3 = Error::Io(io_error3);
553 let filemeta_error4 = Error::Io(io_error4);
554
555 assert_eq!(filemeta_error1, filemeta_error2);
557
558 assert_ne!(filemeta_error1, filemeta_error3);
560
561 assert_ne!(filemeta_error1, filemeta_error4);
563 }
564
565 #[test]
566 fn test_filemeta_error_clone_io_variants() {
567 let io_error = IoError::new(ErrorKind::ConnectionReset, "connection lost");
568 let original_error = Error::Io(io_error);
569 let cloned_error = original_error.clone();
570
571 assert_eq!(original_error, cloned_error);
573
574 match (original_error, cloned_error) {
576 (Error::Io(orig_io), Error::Io(cloned_io)) => {
577 assert_eq!(orig_io.kind(), cloned_io.kind());
578 assert_eq!(orig_io.to_string(), cloned_io.to_string());
579 }
580 _ => panic!("Both should be Io variants"),
581 }
582 }
583}