1use std::io;
2
3#[derive(Debug, thiserror::Error)]
5pub enum Error {
6 #[error("I/O error: {0}")]
7 Io(#[from] io::Error),
8
9 #[error("unsupported file type: {0}")]
10 UnsupportedFileType(String),
11
12 #[error("invalid data: {0}")]
13 InvalidData(String),
14
15 #[error("tag not found: {0}")]
16 TagNotFound(String),
17
18 #[error("invalid TIFF header")]
19 InvalidTiffHeader,
20
21 #[error("invalid EXIF data: {0}")]
22 InvalidExif(String),
23
24 #[error("invalid IPTC data: {0}")]
25 InvalidIptc(String),
26
27 #[error("invalid XMP data: {0}")]
28 InvalidXmp(String),
29
30 #[error("truncated data: expected {expected} bytes, got {actual}")]
31 TruncatedData { expected: usize, actual: usize },
32
33 #[error("value conversion error: {0}")]
34 ConversionError(String),
35}
36
37pub type Result<T> = std::result::Result<T, Error>;
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn display_io_error() {
45 let err = Error::Io(io::Error::new(io::ErrorKind::NotFound, "gone"));
46 let s = format!("{}", err);
47 assert!(s.contains("I/O error"), "got: {}", s);
48 assert!(s.contains("gone"), "got: {}", s);
49 }
50
51 #[test]
52 fn display_unsupported_file_type() {
53 let err = Error::UnsupportedFileType("xyz".into());
54 assert_eq!(format!("{}", err), "unsupported file type: xyz");
55 }
56
57 #[test]
58 fn display_invalid_data() {
59 let err = Error::InvalidData("bad header".into());
60 assert_eq!(format!("{}", err), "invalid data: bad header");
61 }
62
63 #[test]
64 fn display_tag_not_found() {
65 let err = Error::TagNotFound("FooBar".into());
66 assert_eq!(format!("{}", err), "tag not found: FooBar");
67 }
68
69 #[test]
70 fn display_invalid_tiff_header() {
71 let err = Error::InvalidTiffHeader;
72 assert_eq!(format!("{}", err), "invalid TIFF header");
73 }
74
75 #[test]
76 fn display_invalid_exif() {
77 let err = Error::InvalidExif("offset overflow".into());
78 assert_eq!(format!("{}", err), "invalid EXIF data: offset overflow");
79 }
80
81 #[test]
82 fn display_invalid_iptc() {
83 let err = Error::InvalidIptc("bad record".into());
84 assert_eq!(format!("{}", err), "invalid IPTC data: bad record");
85 }
86
87 #[test]
88 fn display_invalid_xmp() {
89 let err = Error::InvalidXmp("malformed xml".into());
90 assert_eq!(format!("{}", err), "invalid XMP data: malformed xml");
91 }
92
93 #[test]
94 fn display_truncated_data() {
95 let err = Error::TruncatedData {
96 expected: 100,
97 actual: 50,
98 };
99 assert_eq!(
100 format!("{}", err),
101 "truncated data: expected 100 bytes, got 50"
102 );
103 }
104
105 #[test]
106 fn display_conversion_error() {
107 let err = Error::ConversionError("not a number".into());
108 assert_eq!(format!("{}", err), "value conversion error: not a number");
109 }
110
111 #[test]
112 fn io_error_from_conversion() {
113 let io_err = io::Error::new(io::ErrorKind::PermissionDenied, "no access");
114 let err: Error = io_err.into();
115 assert!(matches!(err, Error::Io(_)));
116 }
117}