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    /// Failed to parse TOML.
22    TomlDe(toml::de::Error),
23    /// Could not determine the file format from a path.
24    UnknownFormat(PathBuf),
25    /// CLI usage error.
26    Usage(String),
27    /// Logical error during disassembly or reassembly.
28    Invalid(String),
29    /// Error returned by the in-tree XML disassembler.
30    Xml(String),
31}
32
33impl fmt::Display for Error {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            Error::Io(e) => write!(f, "i/o error: {e}"),
37            Error::Json(e) => write!(f, "json error: {e}"),
38            Error::Json5(e) => write!(f, "json5 error: {e}"),
39            Error::Yaml(e) => write!(f, "yaml error: {e}"),
40            Error::TomlDe(e) => write!(f, "toml parse error: {e}"),
41            Error::UnknownFormat(p) => {
42                write!(
43                    f,
44                    "could not determine config format from path: {}",
45                    p.display()
46                )
47            }
48            Error::Usage(m) => write!(f, "{m}"),
49            Error::Invalid(m) => write!(f, "{m}"),
50            Error::Xml(m) => write!(f, "xml-disassembler: {m}"),
51        }
52    }
53}
54
55impl std::error::Error for Error {
56    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
57        match self {
58            Error::Io(e) => Some(e),
59            Error::Json(e) => Some(e),
60            Error::Json5(e) => Some(e),
61            Error::Yaml(e) => Some(e),
62            Error::TomlDe(e) => Some(e),
63            _ => None,
64        }
65    }
66}
67
68impl From<io::Error> for Error {
69    fn from(e: io::Error) -> Self {
70        Error::Io(e)
71    }
72}
73
74impl From<serde_json::Error> for Error {
75    fn from(e: serde_json::Error) -> Self {
76        Error::Json(e)
77    }
78}
79
80impl From<json5::Error> for Error {
81    fn from(e: json5::Error) -> Self {
82        Error::Json5(e)
83    }
84}
85
86impl From<serde_yaml::Error> for Error {
87    fn from(e: serde_yaml::Error) -> Self {
88        Error::Yaml(e)
89    }
90}
91
92impl From<toml::de::Error> for Error {
93    fn from(e: toml::de::Error) -> Self {
94        Error::TomlDe(e)
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use std::error::Error as _;
102
103    #[test]
104    fn display_covers_all_variants() {
105        let e = Error::UnknownFormat(PathBuf::from("foo.txt"));
106        assert!(e.to_string().contains("foo.txt"));
107        assert!(Error::Usage("u".into()).to_string().contains("u"));
108        assert!(Error::Invalid("i".into()).to_string().contains("i"));
109        assert!(Error::Xml("x".into()).to_string().contains("xml"));
110
111        let io_err = Error::from(io::Error::new(io::ErrorKind::NotFound, "missing"));
112        assert!(io_err.to_string().contains("i/o error"));
113        assert!(io_err.source().is_some());
114
115        let json_err: Error = serde_json::from_str::<serde_json::Value>("{ not json")
116            .unwrap_err()
117            .into();
118        assert!(json_err.to_string().contains("json"));
119        assert!(json_err.source().is_some());
120
121        let yaml_err: Error = serde_yaml::from_str::<serde_json::Value>("\t- :: bad")
122            .unwrap_err()
123            .into();
124        assert!(yaml_err.to_string().contains("yaml"));
125        assert!(yaml_err.source().is_some());
126
127        let json5_err: Error = json5::from_str::<serde_json::Value>("{ not json5")
128            .unwrap_err()
129            .into();
130        assert!(json5_err.to_string().contains("json5"));
131        assert!(json5_err.source().is_some());
132
133        let toml_de_err: Error = toml::from_str::<serde_json::Value>("not = = toml")
134            .unwrap_err()
135            .into();
136        assert!(toml_de_err.to_string().contains("toml"));
137        assert!(toml_de_err.source().is_some());
138
139        // Variants without a wrapped source.
140        assert!(Error::Usage("u".into()).source().is_none());
141    }
142}