Skip to main content

energyml_core/
error.rs

1//! Error type shared by serialization, reflection and validation.
2
3/// Result alias used throughout the crate.
4pub type Result<T> = std::result::Result<T, Error>;
5
6/// Everything that can go wrong while reading, writing or reflecting over an
7/// EnergyML object.
8#[derive(Debug, thiserror::Error)]
9#[non_exhaustive]
10pub enum Error {
11    /// Low level XML failure reported by the parser or writer.
12    #[error("XML error: {0}")]
13    Xml(String),
14
15    /// I/O failure while reading or writing.
16    #[error("I/O error: {0}")]
17    Io(#[from] std::io::Error),
18
19    /// The document ended while a value was still being read.
20    #[error("unexpected end of document")]
21    Eof,
22
23    /// An element was found where another one was required.
24    #[error("expected element <{expected}>, found <{found}>")]
25    UnexpectedElement {
26        /// Element name the reader was looking for.
27        expected: String,
28        /// Element name actually encountered.
29        found: String,
30    },
31
32    /// A mandatory attribute or child element was absent.
33    #[error("missing required {kind} `{name}` in <{parent}>")]
34    MissingRequired {
35        /// `"attribute"` or `"element"`.
36        kind: &'static str,
37        /// Name of the missing item.
38        name: String,
39        /// Element that should have contained it.
40        parent: String,
41    },
42
43    /// A lexical value could not be converted to its Rust type.
44    #[error("cannot parse `{value}` as {ty}: {reason}")]
45    ParseValue {
46        /// The offending lexical value.
47        value: String,
48        /// Target type name.
49        ty: &'static str,
50        /// Why the conversion failed.
51        reason: String,
52    },
53
54    /// An `xsi:type` (or substituted element name) did not match any known
55    /// concrete subtype of the expected abstract type.
56    #[error("unknown xsi:type `{value}` for abstract type `{ty}`")]
57    UnknownXsiType {
58        /// The `xsi:type` value read from the document.
59        value: String,
60        /// Abstract type being resolved.
61        ty: &'static str,
62    },
63
64    /// Reflection was asked for a field the type does not declare.
65    #[error("type `{ty}` has no field `{field}`")]
66    UnknownField {
67        /// Type being reflected over.
68        ty: &'static str,
69        /// Requested field name.
70        field: String,
71    },
72
73    /// A dotted path could not be parsed.
74    #[error("invalid path `{path}`: {reason}")]
75    InvalidPath {
76        /// The path as supplied by the caller.
77        path: String,
78        /// Why it is rejected.
79        reason: String,
80    },
81
82    /// A value of the wrong shape was supplied to a reflective setter.
83    #[error("type mismatch at `{path}`: expected {expected}, found {found}")]
84    TypeMismatch {
85        /// Location of the mismatch.
86        path: String,
87        /// Expected kind or type name.
88        expected: String,
89        /// Kind or type name actually supplied.
90        found: String,
91    },
92
93    /// The registry does not know the requested type.
94    #[error("unknown type `{0}` (not registered)")]
95    UnknownType(String),
96
97    /// The requested type belongs to a schema version the project knows but
98    /// this build does not carry (see `Registry::declare_absent`). A refusal
99    /// with its reason, not an unknown name: the data may be perfectly good.
100    /// The message is the whole story and says what would bring the version.
101    #[error("{0}")]
102    VersionAbsent(String),
103
104    /// Catch-all for anything not worth its own variant.
105    #[error("{0}")]
106    Other(String),
107}
108
109impl Error {
110    /// Convenience constructor for [`Error::Other`].
111    pub fn other(msg: impl Into<String>) -> Self {
112        Error::Other(msg.into())
113    }
114
115    /// Convenience constructor for [`Error::ParseValue`].
116    pub fn parse(value: impl Into<String>, ty: &'static str, reason: impl Into<String>) -> Self {
117        Error::ParseValue {
118            value: value.into(),
119            ty,
120            reason: reason.into(),
121        }
122    }
123}
124
125impl From<quick_xml::Error> for Error {
126    fn from(e: quick_xml::Error) -> Self {
127        Error::Xml(e.to_string())
128    }
129}
130
131impl From<quick_xml::events::attributes::AttrError> for Error {
132    fn from(e: quick_xml::events::attributes::AttrError) -> Self {
133        Error::Xml(e.to_string())
134    }
135}
136
137impl From<quick_xml::encoding::EncodingError> for Error {
138    fn from(e: quick_xml::encoding::EncodingError) -> Self {
139        Error::Xml(e.to_string())
140    }
141}
142
143impl From<quick_xml::escape::EscapeError> for Error {
144    fn from(e: quick_xml::escape::EscapeError) -> Self {
145        Error::Xml(e.to_string())
146    }
147}
148
149impl From<std::str::Utf8Error> for Error {
150    fn from(e: std::str::Utf8Error) -> Self {
151        Error::Xml(format!("invalid UTF-8: {e}"))
152    }
153}
154
155impl From<std::string::FromUtf8Error> for Error {
156    fn from(e: std::string::FromUtf8Error) -> Self {
157        Error::Xml(format!("invalid UTF-8: {e}"))
158    }
159}