1use thiserror::Error;
8
9#[derive(Debug, Error)]
17#[non_exhaustive]
18pub enum DocError {
19 #[error("I/O error: {0}")]
23 Io(#[from] std::io::Error),
24
25 #[error("ZIP error: {0}")]
29 Zip(String),
30
31 #[error("Format error: {0}")]
35 Format(String),
36
37 #[error("Template error at placeholder '{placeholder}': {message}")]
41 Template {
42 placeholder: String,
44 message: String,
46 },
47
48 #[error("Conversion error: field '{field}', value '{value}': {message}")]
52 Conversion {
53 field: String,
55 value: String,
57 message: String,
59 },
60
61 #[error("Unsupported operation: {0}")]
65 Unsupported(String),
66
67 #[error("Document error: {0}")]
71 Document(String),
72}
73
74pub type Result<T> = std::result::Result<T, DocError>;
76
77impl From<zip::result::ZipError> for DocError {
78 fn from(e: zip::result::ZipError) -> Self {
79 Self::Zip(e.to_string())
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn error_display_io() {
89 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "missing");
90 let err = DocError::Io(io_err);
91 assert!(format!("{err}").contains("I/O error"));
92 }
93
94 #[test]
95 fn error_display_zip() {
96 let err = DocError::Zip("corrupt".into());
97 assert!(format!("{err}").contains("ZIP error"));
98 }
99
100 #[test]
101 fn error_display_format() {
102 let err = DocError::Format("bad xml".into());
103 assert!(format!("{err}").contains("Format error"));
104 }
105
106 #[test]
107 fn error_display_template() {
108 let err = DocError::Template {
109 placeholder: "name".into(),
110 message: "not found".into(),
111 };
112 let s = format!("{err}");
113 assert!(s.contains("name"));
114 assert!(s.contains("not found"));
115 }
116
117 #[test]
118 fn error_display_conversion() {
119 let err = DocError::Conversion {
120 field: "age".into(),
121 value: "abc".into(),
122 message: "not a number".into(),
123 };
124 let s = format!("{err}");
125 assert!(s.contains("age"));
126 assert!(s.contains("abc"));
127 }
128
129 #[test]
130 fn error_display_unsupported() {
131 let err = DocError::Unsupported("macro".into());
132 assert!(format!("{err}").contains("Unsupported operation"));
133 }
134
135 #[test]
136 fn error_display_document() {
137 let err = DocError::Document("corrupted".into());
138 assert!(format!("{err}").contains("Document error"));
139 }
140
141 #[test]
142 fn error_from_zip_error() {
143 let zip_err = zip::result::ZipError::FileNotFound;
144 let err: DocError = zip_err.into();
145 assert!(matches!(err, DocError::Zip(_)));
146 }
147
148 #[test]
149 fn error_from_io_error() {
150 let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "denied");
151 let err: DocError = io_err.into();
152 assert!(matches!(err, DocError::Io(_)));
153 }
154
155 #[test]
156 fn error_debug() {
157 let err = DocError::Document("test".into());
158 let dbg = format!("{err:?}");
159 assert!(dbg.contains("Document"));
160 }
161}