Skip to main content

config_disassembler/
error.rs

1//! Error type used throughout the crate.
2
3use std::fmt;
4use std::io;
5use std::path::PathBuf;
6
7/// Convenience alias for `Result<T, Error>`.
8pub type Result<T> = std::result::Result<T, Error>;
9
10/// Errors produced by the config-disassembler library.
11#[derive(Debug)]
12pub enum Error {
13    /// I/O error while reading or writing a file.
14    Io(io::Error),
15    /// Failed to parse JSON.
16    Json(serde_json::Error),
17    /// Failed to parse JSON5.
18    Json5(json5::Error),
19    /// Failed to parse YAML.
20    Yaml(serde_yaml::Error),
21    /// Could not determine the file format from a path.
22    UnknownFormat(PathBuf),
23    /// CLI usage error.
24    Usage(String),
25    /// Logical error during disassembly or reassembly.
26    Invalid(String),
27    /// Error returned by the embedded xml-disassembler.
28    Xml(String),
29}
30
31impl fmt::Display for Error {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            Error::Io(e) => write!(f, "i/o error: {e}"),
35            Error::Json(e) => write!(f, "json error: {e}"),
36            Error::Json5(e) => write!(f, "json5 error: {e}"),
37            Error::Yaml(e) => write!(f, "yaml error: {e}"),
38            Error::UnknownFormat(p) => {
39                write!(
40                    f,
41                    "could not determine config format from path: {}",
42                    p.display()
43                )
44            }
45            Error::Usage(m) => write!(f, "{m}"),
46            Error::Invalid(m) => write!(f, "{m}"),
47            Error::Xml(m) => write!(f, "xml-disassembler: {m}"),
48        }
49    }
50}
51
52impl std::error::Error for Error {
53    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
54        match self {
55            Error::Io(e) => Some(e),
56            Error::Json(e) => Some(e),
57            Error::Json5(e) => Some(e),
58            Error::Yaml(e) => Some(e),
59            _ => None,
60        }
61    }
62}
63
64impl From<io::Error> for Error {
65    fn from(e: io::Error) -> Self {
66        Error::Io(e)
67    }
68}
69
70impl From<serde_json::Error> for Error {
71    fn from(e: serde_json::Error) -> Self {
72        Error::Json(e)
73    }
74}
75
76impl From<json5::Error> for Error {
77    fn from(e: json5::Error) -> Self {
78        Error::Json5(e)
79    }
80}
81
82impl From<serde_yaml::Error> for Error {
83    fn from(e: serde_yaml::Error) -> Self {
84        Error::Yaml(e)
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use std::error::Error as _;
92
93    #[test]
94    fn display_covers_all_variants() {
95        let e = Error::UnknownFormat(PathBuf::from("foo.txt"));
96        assert!(e.to_string().contains("foo.txt"));
97        assert!(Error::Usage("u".into()).to_string().contains("u"));
98        assert!(Error::Invalid("i".into()).to_string().contains("i"));
99        assert!(Error::Xml("x".into()).to_string().contains("xml"));
100
101        let io_err = Error::from(io::Error::new(io::ErrorKind::NotFound, "missing"));
102        assert!(io_err.to_string().contains("i/o error"));
103        assert!(io_err.source().is_some());
104
105        let json_err: Error = serde_json::from_str::<serde_json::Value>("{ not json")
106            .unwrap_err()
107            .into();
108        assert!(json_err.to_string().contains("json"));
109        assert!(json_err.source().is_some());
110
111        let yaml_err: Error = serde_yaml::from_str::<serde_json::Value>("\t- :: bad")
112            .unwrap_err()
113            .into();
114        assert!(yaml_err.to_string().contains("yaml"));
115        assert!(yaml_err.source().is_some());
116
117        let json5_err: Error = json5::from_str::<serde_json::Value>("{ not json5")
118            .unwrap_err()
119            .into();
120        assert!(json5_err.to_string().contains("json5"));
121        assert!(json5_err.source().is_some());
122
123        // Variants without a wrapped source.
124        assert!(Error::Usage("u".into()).source().is_none());
125    }
126}