1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, ThingsError>;
7
8#[derive(Error, Debug)]
10pub enum ThingsError {
11 #[error("Database error: {0}")]
12 Database(String),
13
14 #[error("Serialization error: {0}")]
15 Serialization(#[from] serde_json::Error),
16
17 #[error("IO error: {0}")]
18 Io(#[from] std::io::Error),
19
20 #[error("Database not found: {path}")]
21 DatabaseNotFound { path: String },
22
23 #[error("Invalid UUID: {uuid}")]
24 InvalidUuid { uuid: String },
25
26 #[error("Invalid date: {date}")]
27 InvalidDate { date: String },
28
29 #[error("Task not found: {uuid}")]
30 TaskNotFound { uuid: String },
31
32 #[error("Project not found: {uuid}")]
33 ProjectNotFound { uuid: String },
34
35 #[error("Area not found: {uuid}")]
36 AreaNotFound { uuid: String },
37
38 #[error("Validation error: {message}")]
39 Validation { message: String },
40
41 #[error("Configuration error: {message}")]
42 Configuration { message: String },
43
44 #[error("Unknown error: {message}")]
45 Unknown { message: String },
46}
47
48impl ThingsError {
49 pub fn validation(message: impl Into<String>) -> Self {
51 Self::Validation {
52 message: message.into(),
53 }
54 }
55
56 pub fn configuration(message: impl Into<String>) -> Self {
58 Self::Configuration {
59 message: message.into(),
60 }
61 }
62
63 pub fn unknown(message: impl Into<String>) -> Self {
65 Self::Unknown {
66 message: message.into(),
67 }
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use std::io;
75
76 #[test]
77 fn test_database_error_from_rusqlite() {
78 }
81
82 #[test]
83 fn test_serialization_error_from_serde() {
84 let json_error = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
85 let things_error: ThingsError = json_error.into();
86
87 match things_error {
88 ThingsError::Serialization(_) => (),
89 _ => panic!("Expected Serialization error"),
90 }
91 }
92
93 #[test]
94 fn test_io_error_from_std() {
95 let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found");
96 let things_error: ThingsError = io_error.into();
97
98 match things_error {
99 ThingsError::Io(_) => (),
100 _ => panic!("Expected Io error"),
101 }
102 }
103
104 #[test]
105 fn test_database_not_found_error() {
106 let error = ThingsError::DatabaseNotFound {
107 path: "/path/to/db".to_string(),
108 };
109
110 assert!(error.to_string().contains("Database not found"));
111 assert!(error.to_string().contains("/path/to/db"));
112 }
113
114 #[test]
115 fn test_invalid_uuid_error() {
116 let error = ThingsError::InvalidUuid {
117 uuid: "invalid-uuid".to_string(),
118 };
119
120 assert!(error.to_string().contains("Invalid UUID"));
121 assert!(error.to_string().contains("invalid-uuid"));
122 }
123
124 #[test]
125 fn test_invalid_date_error() {
126 let error = ThingsError::InvalidDate {
127 date: "2023-13-45".to_string(),
128 };
129
130 assert!(error.to_string().contains("Invalid date"));
131 assert!(error.to_string().contains("2023-13-45"));
132 }
133
134 #[test]
135 fn test_task_not_found_error() {
136 let error = ThingsError::TaskNotFound {
137 uuid: "task-uuid-123".to_string(),
138 };
139
140 assert!(error.to_string().contains("Task not found"));
141 assert!(error.to_string().contains("task-uuid-123"));
142 }
143
144 #[test]
145 fn test_project_not_found_error() {
146 let error = ThingsError::ProjectNotFound {
147 uuid: "project-uuid-456".to_string(),
148 };
149
150 assert!(error.to_string().contains("Project not found"));
151 assert!(error.to_string().contains("project-uuid-456"));
152 }
153
154 #[test]
155 fn test_area_not_found_error() {
156 let error = ThingsError::AreaNotFound {
157 uuid: "area-uuid-789".to_string(),
158 };
159
160 assert!(error.to_string().contains("Area not found"));
161 assert!(error.to_string().contains("area-uuid-789"));
162 }
163
164 #[test]
165 fn test_validation_error() {
166 let error = ThingsError::Validation {
167 message: "Invalid input data".to_string(),
168 };
169
170 assert!(error.to_string().contains("Validation error"));
171 assert!(error.to_string().contains("Invalid input data"));
172 }
173
174 #[test]
175 fn test_configuration_error() {
176 let error = ThingsError::Configuration {
177 message: "Missing required config".to_string(),
178 };
179
180 assert!(error.to_string().contains("Configuration error"));
181 assert!(error.to_string().contains("Missing required config"));
182 }
183
184 #[test]
185 fn test_unknown_error() {
186 let error = ThingsError::Unknown {
187 message: "Something went wrong".to_string(),
188 };
189
190 assert!(error.to_string().contains("Unknown error"));
191 assert!(error.to_string().contains("Something went wrong"));
192 }
193
194 #[test]
195 fn test_validation_helper() {
196 let error = ThingsError::validation("Test validation message");
197
198 match error {
199 ThingsError::Validation { message } => {
200 assert_eq!(message, "Test validation message");
201 }
202 _ => panic!("Expected Validation error"),
203 }
204 }
205
206 #[test]
207 fn test_validation_helper_with_string() {
208 let message = "Test validation message".to_string();
209 let error = ThingsError::validation(message);
210
211 match error {
212 ThingsError::Validation { message } => {
213 assert_eq!(message, "Test validation message");
214 }
215 _ => panic!("Expected Validation error"),
216 }
217 }
218
219 #[test]
220 fn test_configuration_helper() {
221 let error = ThingsError::configuration("Test config message");
222
223 match error {
224 ThingsError::Configuration { message } => {
225 assert_eq!(message, "Test config message");
226 }
227 _ => panic!("Expected Configuration error"),
228 }
229 }
230
231 #[test]
232 fn test_configuration_helper_with_string() {
233 let message = "Test config message".to_string();
234 let error = ThingsError::configuration(message);
235
236 match error {
237 ThingsError::Configuration { message } => {
238 assert_eq!(message, "Test config message");
239 }
240 _ => panic!("Expected Configuration error"),
241 }
242 }
243
244 #[test]
245 fn test_unknown_helper() {
246 let error = ThingsError::unknown("Test unknown message");
247
248 match error {
249 ThingsError::Unknown { message } => {
250 assert_eq!(message, "Test unknown message");
251 }
252 _ => panic!("Expected Unknown error"),
253 }
254 }
255
256 #[test]
257 fn test_unknown_helper_with_string() {
258 let message = "Test unknown message".to_string();
259 let error = ThingsError::unknown(message);
260
261 match error {
262 ThingsError::Unknown { message } => {
263 assert_eq!(message, "Test unknown message");
264 }
265 _ => panic!("Expected Unknown error"),
266 }
267 }
268
269 #[test]
270 fn test_error_display_formatting() {
271 let errors = vec![
272 ThingsError::DatabaseNotFound {
273 path: "test.db".to_string(),
274 },
275 ThingsError::InvalidUuid {
276 uuid: "bad-uuid".to_string(),
277 },
278 ThingsError::InvalidDate {
279 date: "bad-date".to_string(),
280 },
281 ThingsError::TaskNotFound {
282 uuid: "task-123".to_string(),
283 },
284 ThingsError::ProjectNotFound {
285 uuid: "project-456".to_string(),
286 },
287 ThingsError::AreaNotFound {
288 uuid: "area-789".to_string(),
289 },
290 ThingsError::Validation {
291 message: "validation failed".to_string(),
292 },
293 ThingsError::Configuration {
294 message: "config error".to_string(),
295 },
296 ThingsError::Unknown {
297 message: "unknown error".to_string(),
298 },
299 ];
300
301 for error in errors {
302 let error_string = error.to_string();
303 assert!(!error_string.is_empty());
304 assert!(error_string.len() > 10); }
306 }
307
308 #[test]
309 fn test_error_debug_formatting() {
310 let error = ThingsError::Validation {
311 message: "test message".to_string(),
312 };
313
314 let debug_string = format!("{error:?}");
315 assert!(debug_string.contains("Validation"));
316 assert!(debug_string.contains("test message"));
317 }
318
319 #[test]
320 fn test_result_type_alias() {
321 fn returns_result() -> String {
323 "success".to_string()
324 }
325
326 fn returns_error() -> Result<String> {
327 Err(ThingsError::validation("test error"))
328 }
329
330 assert_eq!(returns_result(), "success");
331 assert!(returns_error().is_err());
332
333 match returns_error() {
334 Err(ThingsError::Validation { message }) => {
335 assert_eq!(message, "test error");
336 }
337 _ => panic!("Expected Validation error"),
338 }
339 }
340}