Skip to main content

feedparser_rs/
error.rs

1use thiserror::Error;
2
3/// Feed parsing errors
4#[derive(Error, Debug)]
5pub enum FeedError {
6    /// XML parsing error
7    #[error("XML parsing error: {0}")]
8    XmlError(#[from] quick_xml::Error),
9
10    /// I/O error
11    #[error("IO error: {0}")]
12    IoError(#[from] std::io::Error),
13
14    /// Invalid feed format
15    #[error("Invalid feed format: {0}")]
16    InvalidFormat(String),
17
18    /// Encoding error
19    #[error("Encoding error: {0}")]
20    EncodingError(String),
21
22    /// JSON parsing error
23    #[error("JSON parsing error: {0}")]
24    JsonError(#[from] serde_json::Error),
25
26    /// HTTP error
27    #[error("HTTP error: {message}")]
28    Http {
29        /// Error message
30        message: String,
31    },
32
33    /// URL parsing error
34    #[error("URL parsing error: {0}")]
35    UrlError(#[from] url::ParseError),
36
37    /// Unknown error
38    #[error("Unknown error: {0}")]
39    Unknown(String),
40}
41
42/// Result type for feed parsing operations
43pub 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}