1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Error, Debug)]
10pub enum Error {
11 #[error("Configuration error: {0}")]
12 Config(String),
13
14 #[error("Parse error: {0}")]
15 Parse(String),
16
17 #[error("Component error: {0}")]
18 Component(String),
19
20 #[error("Template error: {0}")]
21 Template(String),
22
23 #[error("Data error: {0}")]
24 Data(String),
25
26 #[error("Cache error: {0}")]
27 Cache(String),
28
29 #[error("Action error: {0}")]
30 Action(String),
31
32 #[error("Server error: {0}")]
33 Server(String),
34
35 #[error("IO error: {0}")]
36 Io(#[from] std::io::Error),
37
38 #[error("HTTP error: {0}")]
39 Http(#[from] reqwest::Error),
40
41 #[error("JSON error: {0}")]
42 Json(#[from] serde_json::Error),
43
44 #[error("TOML error: {0}")]
45 Toml(#[from] toml::de::Error),
46
47 #[error("Session error: {0}")]
48 Session(String),
49
50 #[error("SQLite error: {0}")]
51 Sqlite(#[from] rusqlite::Error),
52
53 #[error("Authentication error: {0}")]
54 Auth(String),
55
56 #[error("JWT error: {0}")]
57 Jwt(#[from] jsonwebtoken::errors::Error),
58
59 #[error("Upload error: {0}")]
60 Upload(String),
61
62 #[error("Validation error: {0}")]
63 Validation(String),
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
73 fn test_config_error_display() {
74 let err = Error::Config("missing field".to_string());
75 assert_eq!(err.to_string(), "Configuration error: missing field");
76 }
77
78 #[test]
79 fn test_parse_error_display() {
80 let err = Error::Parse("unexpected token".to_string());
81 assert_eq!(err.to_string(), "Parse error: unexpected token");
82 }
83
84 #[test]
85 fn test_component_error_display() {
86 let err = Error::Component("slot not found".to_string());
87 assert_eq!(err.to_string(), "Component error: slot not found");
88 }
89
90 #[test]
91 fn test_template_error_display() {
92 let err = Error::Template("invalid syntax".to_string());
93 assert_eq!(err.to_string(), "Template error: invalid syntax");
94 }
95
96 #[test]
97 fn test_data_error_display() {
98 let err = Error::Data("source unavailable".to_string());
99 assert_eq!(err.to_string(), "Data error: source unavailable");
100 }
101
102 #[test]
103 fn test_cache_error_display() {
104 let err = Error::Cache("connection lost".to_string());
105 assert_eq!(err.to_string(), "Cache error: connection lost");
106 }
107
108 #[test]
109 fn test_action_error_display() {
110 let err = Error::Action("handler failed".to_string());
111 assert_eq!(err.to_string(), "Action error: handler failed");
112 }
113
114 #[test]
115 fn test_server_error_display() {
116 let err = Error::Server("bind failed".to_string());
117 assert_eq!(err.to_string(), "Server error: bind failed");
118 }
119
120 #[test]
121 fn test_session_error_display() {
122 let err = Error::Session("expired".to_string());
123 assert_eq!(err.to_string(), "Session error: expired");
124 }
125
126 #[test]
127 fn test_auth_error_display() {
128 let err = Error::Auth("unauthorized".to_string());
129 assert_eq!(err.to_string(), "Authentication error: unauthorized");
130 }
131
132 #[test]
135 fn test_from_io_error() {
136 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
137 let err: Error = Error::from(io_err);
138 match &err {
139 Error::Io(_) => {}
140 _ => panic!("Expected Error::Io variant, got: {:?}", err),
141 }
142 assert!(err.to_string().contains("file not found"));
143 }
144
145 #[test]
146 fn test_from_json_error() {
147 let json_result: std::result::Result<serde_json::Value, _> =
148 serde_json::from_str("not json");
149 let json_err = json_result.unwrap_err();
150 let err: Error = Error::from(json_err);
151 match &err {
152 Error::Json(_) => {}
153 _ => panic!("Expected Error::Json variant, got: {:?}", err),
154 }
155 assert!(err.to_string().starts_with("JSON error:"));
156 }
157
158 #[test]
159 fn test_from_toml_error() {
160 let toml_result: std::result::Result<toml::Value, _> = toml::from_str("not valid [[[toml");
161 let toml_err = toml_result.unwrap_err();
162 let err: Error = Error::from(toml_err);
163 match &err {
164 Error::Toml(_) => {}
165 _ => panic!("Expected Error::Toml variant, got: {:?}", err),
166 }
167 assert!(err.to_string().starts_with("TOML error:"));
168 }
169
170 #[test]
171 fn test_from_rusqlite_error() {
172 let sqlite_err = rusqlite::Error::InvalidParameterName("bad_param".to_string());
173 let err: Error = Error::from(sqlite_err);
174 match &err {
175 Error::Sqlite(_) => {}
176 _ => panic!("Expected Error::Sqlite variant, got: {:?}", err),
177 }
178 assert!(err.to_string().starts_with("SQLite error:"));
179 }
180
181 #[test]
182 fn test_from_jwt_error() {
183 let jwt_result = jsonwebtoken::decode::<serde_json::Value>(
185 "not.a.token",
186 &jsonwebtoken::DecodingKey::from_secret(b"secret"),
187 &jsonwebtoken::Validation::default(),
188 );
189 let jwt_err = jwt_result.unwrap_err();
190 let err: Error = Error::from(jwt_err);
191 match &err {
192 Error::Jwt(_) => {}
193 _ => panic!("Expected Error::Jwt variant, got: {:?}", err),
194 }
195 assert!(err.to_string().starts_with("JWT error:"));
196 }
197
198 #[test]
201 fn test_result_type_ok() {
202 let result: Result<i32> = Ok(42);
203 assert_eq!(result.unwrap(), 42);
204 }
205
206 #[test]
207 fn test_result_type_err() {
208 let result: Result<i32> = Err(Error::Config("test".to_string()));
209 assert!(result.is_err());
210 }
211
212 #[test]
215 fn test_error_is_debuggable() {
216 let err = Error::Config("test error".to_string());
217 let debug_str = format!("{:?}", err);
218 assert!(debug_str.contains("Config"));
219 assert!(debug_str.contains("test error"));
220 }
221
222 #[test]
225 fn test_error_with_empty_message() {
226 let err = Error::Parse(String::new());
227 assert_eq!(err.to_string(), "Parse error: ");
228 }
229
230 #[test]
233 fn test_io_error_via_question_mark() {
234 fn may_fail() -> Result<()> {
235 let _file = std::fs::File::open("/nonexistent/file/path")?;
236 Ok(())
237 }
238 let result = may_fail();
239 assert!(result.is_err());
240 match result.unwrap_err() {
241 Error::Io(_) => {}
242 other => panic!("Expected Error::Io, got: {:?}", other),
243 }
244 }
245}