Skip to main content

aster_forge_xml/
error.rs

1//! Error types for bounded XML parsing and source I/O.
2
3use std::fmt;
4
5/// Failures produced while applying an [`XmlSafetyPolicy`](crate::XmlSafetyPolicy).
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum XmlSafetyError {
8    /// One or more configured limits are zero.
9    InvalidPolicy,
10    /// The input is larger than the configured byte limit.
11    InputTooLarge,
12    /// Generated XML exceeds the configured byte limit.
13    OutputTooLarge,
14    /// The input declares a DTD or custom entity while declarations are prohibited.
15    ExternalEntity,
16    /// The element nesting depth exceeds the configured limit.
17    TooDeep,
18    /// The total element count exceeds the configured limit.
19    TooManyElements,
20    /// An element has more attributes than the configured limit.
21    TooManyAttributes,
22    /// The total decoded text and CDATA size exceeds the configured limit.
23    TextTooLarge,
24    /// The parser emitted more events than the configured limit.
25    TooManyEvents,
26    /// The document contains bytes that are not valid in its declared encoding.
27    InvalidEncoding,
28    /// The input is not one complete, well-formed, single-root XML document.
29    Malformed,
30}
31
32impl fmt::Display for XmlSafetyError {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        f.write_str(match self {
35            Self::InvalidPolicy => "XML safety limits must be positive",
36            Self::InputTooLarge => "XML input exceeds the configured byte limit",
37            Self::OutputTooLarge => "XML output exceeds the configured byte limit",
38            Self::ExternalEntity => "XML DTD and custom entity declarations are not allowed",
39            Self::TooDeep => "XML nesting depth exceeds the configured limit",
40            Self::TooManyElements => "XML element count exceeds the configured limit",
41            Self::TooManyAttributes => "XML attribute count exceeds the configured limit",
42            Self::TextTooLarge => "XML text size exceeds the configured limit",
43            Self::TooManyEvents => "XML event count exceeds the configured limit",
44            Self::InvalidEncoding => "XML input contains invalid encoded text",
45            Self::Malformed => "malformed XML input",
46        })
47    }
48}
49
50impl std::error::Error for XmlSafetyError {}
51
52/// Errors produced while parsing or retaining an XML document.
53#[derive(Debug)]
54pub enum Error {
55    /// A configured input safety boundary was crossed.
56    Safety(XmlSafetyError),
57    /// The document is structurally invalid. The message is diagnostic only.
58    InvalidXml(String),
59    /// A writer operation would produce invalid XML or violate writer state.
60    InvalidData(String),
61    /// Reading or writing failed.
62    Io(std::io::Error),
63}
64
65impl fmt::Display for Error {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match self {
68            Self::Safety(error) => write!(f, "XML safety error: {error}"),
69            Self::InvalidXml(message) => write!(f, "invalid XML: {message}"),
70            Self::InvalidData(message) => write!(f, "invalid XML data: {message}"),
71            Self::Io(error) => write!(f, "XML I/O error: {error}"),
72        }
73    }
74}
75
76impl std::error::Error for Error {
77    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
78        match self {
79            Self::Safety(error) => Some(error),
80            Self::Io(error) => Some(error),
81            Self::InvalidXml(_) | Self::InvalidData(_) => None,
82        }
83    }
84}
85
86impl From<XmlSafetyError> for Error {
87    fn from(error: XmlSafetyError) -> Self {
88        Self::Safety(error)
89    }
90}
91
92impl From<std::io::Error> for Error {
93    fn from(error: std::io::Error) -> Self {
94        Self::Io(error)
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn wrapped_error_display_is_distinct_from_its_source() {
104        let safety = Error::Safety(XmlSafetyError::TextTooLarge);
105        assert_eq!(
106            safety.to_string(),
107            "XML safety error: XML text size exceeds the configured limit"
108        );
109        assert_eq!(
110            std::error::Error::source(&safety).map(ToString::to_string),
111            Some("XML text size exceeds the configured limit".into())
112        );
113
114        let io = Error::Io(std::io::Error::other("fixture failure"));
115        assert_eq!(io.to_string(), "XML I/O error: fixture failure");
116        assert_eq!(
117            std::error::Error::source(&io).map(ToString::to_string),
118            Some("fixture failure".into())
119        );
120    }
121}