Skip to main content

native_theme/
error.rs

1// Error enum with Display, std::error::Error, and From conversions
2
3/// Errors that can occur when reading or processing theme data.
4#[derive(Debug)]
5#[non_exhaustive]
6pub enum Error {
7    /// Operation not supported on the current platform.
8    Unsupported,
9
10    /// Data source exists but cannot be read right now.
11    Unavailable(String),
12
13    /// TOML parsing or serialization error.
14    Format(String),
15
16    /// Wrapped platform-specific error.
17    Platform(Box<dyn std::error::Error + Send + Sync>),
18}
19
20impl std::fmt::Display for Error {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        match self {
23            Error::Unsupported => write!(f, "operation not supported on this platform"),
24            Error::Unavailable(msg) => write!(f, "theme data unavailable: {msg}"),
25            Error::Format(msg) => write!(f, "theme format error: {msg}"),
26            Error::Platform(err) => write!(f, "platform error: {err}"),
27        }
28    }
29}
30
31impl std::error::Error for Error {
32    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
33        match self {
34            Error::Platform(err) => Some(&**err),
35            _ => None,
36        }
37    }
38}
39
40impl From<toml::de::Error> for Error {
41    fn from(err: toml::de::Error) -> Self {
42        Error::Format(err.to_string())
43    }
44}
45
46impl From<toml::ser::Error> for Error {
47    fn from(err: toml::ser::Error) -> Self {
48        Error::Format(err.to_string())
49    }
50}
51
52impl From<std::io::Error> for Error {
53    fn from(err: std::io::Error) -> Self {
54        Error::Unavailable(err.to_string())
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn unsupported_display() {
64        let err = Error::Unsupported;
65        let msg = err.to_string();
66        assert!(msg.contains("not supported"), "got: {msg}");
67    }
68
69    #[test]
70    fn unavailable_display() {
71        let err = Error::Unavailable("file not found".into());
72        let msg = err.to_string();
73        assert!(msg.contains("unavailable"), "got: {msg}");
74        assert!(msg.contains("file not found"), "got: {msg}");
75    }
76
77    #[test]
78    fn format_display() {
79        let err = Error::Format("invalid TOML".into());
80        let msg = err.to_string();
81        assert!(msg.contains("format error"), "got: {msg}");
82        assert!(msg.contains("invalid TOML"), "got: {msg}");
83    }
84
85    #[test]
86    fn platform_display() {
87        let inner = std::io::Error::other("dbus failure");
88        let err = Error::Platform(Box::new(inner));
89        let msg = err.to_string();
90        assert!(msg.contains("platform error"), "got: {msg}");
91        assert!(msg.contains("dbus failure"), "got: {msg}");
92    }
93
94    #[test]
95    fn platform_source_returns_inner() {
96        let inner = std::io::Error::other("inner error");
97        let err = Error::Platform(Box::new(inner));
98        let source = std::error::Error::source(&err);
99        assert!(source.is_some());
100        assert!(source.unwrap().to_string().contains("inner error"));
101    }
102
103    #[test]
104    fn non_platform_source_is_none() {
105        assert!(std::error::Error::source(&Error::Unsupported).is_none());
106        assert!(std::error::Error::source(&Error::Unavailable("x".into())).is_none());
107        assert!(std::error::Error::source(&Error::Format("x".into())).is_none());
108    }
109
110    #[test]
111    fn error_is_send_sync() {
112        fn assert_send_sync<T: Send + Sync>() {}
113        assert_send_sync::<Error>();
114    }
115
116    #[test]
117    fn from_toml_de_error() {
118        // Create a toml deserialization error by parsing invalid TOML
119        let toml_err: Result<toml::Value, toml::de::Error> = toml::from_str("=invalid");
120        let err: Error = toml_err.unwrap_err().into();
121        match &err {
122            Error::Format(msg) => assert!(!msg.is_empty()),
123            other => panic!("expected Format variant, got: {other:?}"),
124        }
125    }
126
127    #[test]
128    fn from_io_error() {
129        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "missing file");
130        let err: Error = io_err.into();
131        match &err {
132            Error::Unavailable(msg) => assert!(msg.contains("missing file")),
133            other => panic!("expected Unavailable variant, got: {other:?}"),
134        }
135    }
136}