1#[derive(Debug, thiserror::Error)]
6pub enum AppError {
7 #[error("Network error: {0}")]
9 NetworkError(String),
10 #[error("Parse error: {0}")]
12 ParseError(String),
13 #[error("Invalid URL: {0}")]
15 UrlError(String),
16 #[error("Regex error: {0}")]
18 RegexError(String),
19 #[error("CSS selector error: {0}")]
21 SelectorError(String),
22 #[error("Period '{period}' is not available. Available periods: {available}")]
24 PeriodValidationError { period: String, available: String },
25 #[error("Invalid input: {0}")]
27 InvalidInput(String),
28 #[error("IO error: {0}")]
30 IoError(String),
31}
32
33impl From<reqwest::Error> for AppError {
35 fn from(err: reqwest::Error) -> Self {
36 AppError::NetworkError(err.to_string())
37 }
38}
39
40impl From<url::ParseError> for AppError {
41 fn from(err: url::ParseError) -> Self {
42 AppError::UrlError(err.to_string())
43 }
44}
45
46impl From<regex::Error> for AppError {
47 fn from(err: regex::Error) -> Self {
48 AppError::RegexError(err.to_string())
49 }
50}
51
52impl From<std::num::ParseIntError> for AppError {
53 fn from(err: std::num::ParseIntError) -> Self {
54 AppError::InvalidInput(err.to_string())
55 }
56}
57
58impl From<std::io::Error> for AppError {
59 fn from(err: std::io::Error) -> Self {
60 AppError::IoError(err.to_string())
61 }
62}
63
64impl From<quick_xml::Error> for AppError {
65 fn from(err: quick_xml::Error) -> Self {
66 AppError::ParseError(format!("XML parsing error: {err}"))
67 }
68}
69
70pub type AppResult<T> = Result<T, AppError>;
74
75#[cfg(test)]
76mod tests {
77 use super::AppError;
78
79 #[test]
80 fn test_period_validation_error_display() {
81 let err = AppError::PeriodValidationError {
82 period: "202301".to_string(),
83 available: "202302, 202303".to_string(),
84 };
85
86 let error_msg = err.to_string();
87 assert!(error_msg.contains("202301"));
88 assert!(error_msg.contains("202302"));
89 assert!(error_msg.contains("202303"));
90 }
91
92 #[test]
93 fn test_network_error_display() {
94 let err = AppError::NetworkError("Connection timeout".to_string());
95 assert!(err.to_string().contains("Network error"));
96 assert!(err.to_string().contains("Connection timeout"));
97 }
98
99 #[test]
100 fn test_url_error_display() {
101 let err = AppError::UrlError("Invalid URL format".to_string());
102 assert!(err.to_string().contains("Invalid URL"));
103 assert!(err.to_string().contains("Invalid URL format"));
104 }
105
106 #[test]
107 fn test_regex_error_display() {
108 let err = AppError::RegexError("Invalid regex".to_string());
109 assert!(err.to_string().contains("Regex error"));
110 }
111
112 #[test]
113 fn test_selector_error_display() {
114 let err = AppError::SelectorError("Invalid selector".to_string());
115 assert!(err.to_string().contains("CSS selector error"));
116 }
117
118 #[test]
119 fn test_invalid_input_error_display() {
120 let err = AppError::InvalidInput("Not a number".to_string());
121 assert!(err.to_string().contains("Invalid input"));
122 }
123
124 #[test]
125 fn test_app_error_implements_error_trait() {
126 use std::error::Error;
127 let err: Box<dyn Error> = Box::new(AppError::NetworkError("test".to_string()));
128 assert!(!err.to_string().is_empty());
129 }
130}