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