Skip to main content

zerodds_xml/
errors.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! `XmlError` — Fehler-Enum fuer den DDS-XML-Loader.
4//!
5//! Spec-Referenzen siehe Doc-Comment pro Variante.
6
7use alloc::string::String;
8use core::fmt;
9
10/// Fehler beim Parsen oder Aufloesen eines DDS-XML-Dokuments.
11///
12/// Spec-Quelle: OMG DDS-XML 1.0 §7.1 (XML Representation Syntax) und
13/// §7.2 (XML Representation of DDS IDL PSM).
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum XmlError {
16    /// XML ist nicht wohlgeformt gemaess [XML] §2.1.
17    ///
18    /// Spec-Ref: DDS-XML 1.0 §7.1.1 ("XML shall be well-formed").
19    InvalidXml(String),
20
21    /// Ein gemaess Spec-Tabelle 7.2 / 7.3.x verpflichtendes Element fehlt.
22    ///
23    /// Spec-Ref: DDS-XML 1.0 §7.1.4 Tab.7.1, §7.2.x.
24    MissingRequiredElement(String),
25
26    /// Ein Element, das nicht in der Element-Tabelle 7.1/7.2/7.3 steht,
27    /// wurde gefunden. Strict-Modus rejected; Lax-Modus ignoriert.
28    ///
29    /// Spec-Ref: DDS-XML 1.0 §7.1.4 (Element-Werte-Tabelle).
30    UnknownElement(String),
31
32    /// Enum-String passt nicht in die DCPS-IDL-Whitelist
33    /// (Spec §7.1.4 Tab.7.1 — `enum`-Werte sind String-Literale, *nicht*
34    /// numerisch).
35    ///
36    /// Spec-Ref: DDS-XML 1.0 §7.1.4 Tab.7.1 (enum), §7.2.1.
37    BadEnum(String),
38
39    /// `base_name`-Inheritance bildet einen Zyklus (A erbt von B erbt von A).
40    ///
41    /// Spec-Ref: DDS-XML 1.0 §7.3.2.4.2 (QoS Profile Inheritance —
42    /// "shall only inherit from previously defined profiles"). Naive
43    /// Implementierungen koennen Zyklen ueber Bibliotheks-Grenzen
44    /// erzeugen; der Loader fuehrt darum DAG-Pruefung durch.
45    CircularInheritance(String),
46
47    /// Element-Wert ausserhalb des Spec-Wertebereichs (z.B. `long` >
48    /// `0x7fffffff` ohne Symbol-Aliasing).
49    ///
50    /// Spec-Ref: DDS-XML 1.0 §7.1.4 Tab.7.1 (Wertebereiche), §7.2.2
51    /// (`LENGTH_UNLIMITED`, `DURATION_INFINITE_*`).
52    ValueOutOfRange(String),
53
54    /// DoS-Cap getroffen — Liste/String ueberschreitet die im Loader
55    /// konfigurierte Obergrenze (Default: 1024 Listen-Elemente, 64 KiB
56    /// Strings).
57    ///
58    /// Kein direkter Spec-Bezug; folgt der ZeroDDS-Security-Posture
59    /// (`docs/spec-coverage/zerodds-xml-1.0.open.md` Risiken-Abschnitt).
60    LimitExceeded(String),
61
62    /// Eine Cross-Reference (z.B. `base_name` einer QoS-Profile-
63    /// Inheritance) konnte nicht aufgeloest werden, weil das referenzierte
64    /// Item nicht existiert.
65    ///
66    /// Spec-Ref: DDS-XML 1.0 §7.3.2.4.2 (QoS-Profile-Inheritance).
67    UnresolvedReference(String),
68}
69
70impl fmt::Display for XmlError {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        match self {
73            Self::InvalidXml(msg) => write!(f, "invalid XML: {msg}"),
74            Self::MissingRequiredElement(name) => {
75                write!(f, "missing required element <{name}>")
76            }
77            Self::UnknownElement(name) => write!(f, "unknown element <{name}>"),
78            Self::BadEnum(value) => write!(f, "invalid enum value `{value}`"),
79            Self::CircularInheritance(chain) => {
80                write!(f, "circular base_name inheritance: {chain}")
81            }
82            Self::ValueOutOfRange(msg) => write!(f, "value out of range: {msg}"),
83            Self::LimitExceeded(msg) => write!(f, "DoS limit exceeded: {msg}"),
84            Self::UnresolvedReference(name) => write!(f, "unresolved reference `{name}`"),
85        }
86    }
87}
88
89#[cfg(feature = "std")]
90impl std::error::Error for XmlError {}
91
92#[cfg(test)]
93#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
94mod tests {
95    use super::*;
96    use alloc::string::ToString;
97
98    #[test]
99    fn display_invalid_xml() {
100        let e = XmlError::InvalidXml("unexpected token".into());
101        assert_eq!(e.to_string(), "invalid XML: unexpected token");
102    }
103
104    #[test]
105    fn display_missing_required() {
106        let e = XmlError::MissingRequiredElement("qos_profile".into());
107        assert_eq!(e.to_string(), "missing required element <qos_profile>");
108    }
109
110    #[test]
111    fn display_unknown_element() {
112        let e = XmlError::UnknownElement("foo".into());
113        assert_eq!(e.to_string(), "unknown element <foo>");
114    }
115
116    #[test]
117    fn display_bad_enum() {
118        let e = XmlError::BadEnum("WRONG".into());
119        assert_eq!(e.to_string(), "invalid enum value `WRONG`");
120    }
121
122    #[test]
123    fn display_circular() {
124        let e = XmlError::CircularInheritance("A -> B -> A".into());
125        assert_eq!(e.to_string(), "circular base_name inheritance: A -> B -> A");
126    }
127
128    #[test]
129    fn display_value_out_of_range() {
130        let e = XmlError::ValueOutOfRange("long > 0x7fffffff".into());
131        assert_eq!(e.to_string(), "value out of range: long > 0x7fffffff");
132    }
133
134    #[test]
135    fn display_limit_exceeded() {
136        let e = XmlError::LimitExceeded("seq > 1024".into());
137        assert_eq!(e.to_string(), "DoS limit exceeded: seq > 1024");
138    }
139
140    #[test]
141    fn equality_and_clone() {
142        let a = XmlError::InvalidXml("x".into());
143        let b = a.clone();
144        assert_eq!(a, b);
145    }
146
147    #[test]
148    fn display_unresolved_reference() {
149        let e = XmlError::UnresolvedReference("MissingProfile".into());
150        assert_eq!(e.to_string(), "unresolved reference `MissingProfile`");
151    }
152}