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