1use thiserror::Error;
2
3#[derive(Error, Debug)]
5pub enum FeedError {
6 #[error("XML parsing error: {0}")]
8 XmlError(#[from] quick_xml::Error),
9
10 #[error("IO error: {0}")]
12 IoError(#[from] std::io::Error),
13
14 #[error("Invalid feed format: {0}")]
16 InvalidFormat(String),
17
18 #[error("Encoding error: {0}")]
20 EncodingError(String),
21
22 #[error("JSON parsing error: {0}")]
24 JsonError(#[from] serde_json::Error),
25
26 #[error("HTTP error: {message}")]
28 Http {
29 message: String,
31 },
32
33 #[error("URL parsing error: {0}")]
35 UrlError(#[from] url::ParseError),
36
37 #[error("Unknown error: {0}")]
39 Unknown(String),
40}
41
42pub type Result<T> = std::result::Result<T, FeedError>;
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use std::error::Error as StdError;
49
50 #[test]
51 fn test_error_display() {
52 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "test");
53 let inner = io_err.to_string();
54 let err = FeedError::from(quick_xml::Error::Io(std::sync::Arc::new(io_err)));
55 assert!(err.to_string().starts_with("XML parsing error: "));
56 assert!(err.to_string().contains(&inner));
57 assert!(matches!(err, FeedError::XmlError(quick_xml::Error::Io(_))));
58 }
59
60 #[test]
61 fn test_error_from_io() {
62 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
63 let feed_err = FeedError::from(io_err);
64 assert!(matches!(feed_err, FeedError::IoError(_)));
65 }
66
67 #[test]
68 #[allow(clippy::unnecessary_wraps)]
69 fn test_result_type() {
70 fn get_result() -> Result<i32> {
71 Ok(42)
72 }
73 let result = get_result();
74 assert!(result.is_ok());
75 assert_eq!(result.expect("should be ok"), 42);
76
77 let error: Result<i32> = Err(FeedError::Unknown("test".to_string()));
78 assert!(error.is_err());
79 }
80
81 #[test]
82 fn typed_variants_expose_downcastable_source() {
83 let xml_err = FeedError::from(quick_xml::Error::Io(std::sync::Arc::new(
84 std::io::Error::other("xml io failure"),
85 )));
86 let xml_source = StdError::source(&xml_err).expect("XmlError must carry a source");
87 assert!(xml_source.downcast_ref::<quick_xml::Error>().is_some());
88
89 let io_err = FeedError::from(std::io::Error::other("io failure"));
90 let io_source = StdError::source(&io_err).expect("IoError must carry a source");
91 assert!(io_source.downcast_ref::<std::io::Error>().is_some());
92
93 let json_err =
94 FeedError::from(serde_json::from_str::<u8>("not json").expect_err("must fail"));
95 let json_source = StdError::source(&json_err).expect("JsonError must carry a source");
96 assert!(json_source.downcast_ref::<serde_json::Error>().is_some());
97
98 let url_err = FeedError::from(url::ParseError::EmptyHost);
99 let url_source = StdError::source(&url_err).expect("UrlError must carry a source");
100 assert!(url_source.downcast_ref::<url::ParseError>().is_some());
101 }
102}