1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 ZeroDDS Contributors
//! `XmlError` — error enum for the DDS-XML loader.
//!
//! Spec references see the doc comment per variant.
use alloc::string::String;
use core::fmt;
/// Error while parsing or resolving a DDS-XML document.
///
/// Spec source: OMG DDS-XML 1.0 §7.1 (XML Representation Syntax) and
/// §7.2 (XML Representation of DDS IDL PSM).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum XmlError {
/// XML is not well-formed per [XML] §2.1.
///
/// Spec ref: DDS-XML 1.0 §7.1.1 ("XML shall be well-formed").
InvalidXml(String),
/// An element mandatory per spec table 7.2 / 7.3.x is missing.
///
/// Spec ref: DDS-XML 1.0 §7.1.4 Tab.7.1, §7.2.x.
MissingRequiredElement(String),
/// An element not present in the element table 7.1/7.2/7.3
/// was found. Strict mode rejects; lax mode ignores.
///
/// Spec ref: DDS-XML 1.0 §7.1.4 (element value table).
UnknownElement(String),
/// Enum string does not fit the DCPS-IDL whitelist
/// (Spec §7.1.4 Tab.7.1 — `enum` values are string literals, *not*
/// numeric).
///
/// Spec ref: DDS-XML 1.0 §7.1.4 Tab.7.1 (enum), §7.2.1.
BadEnum(String),
/// `base_name` inheritance forms a cycle (A inherits from B inherits from A).
///
/// Spec ref: DDS-XML 1.0 §7.3.2.4.2 (QoS Profile Inheritance —
/// "shall only inherit from previously defined profiles"). Naive
/// implementations can create cycles across library boundaries;
/// the loader therefore performs DAG checking.
CircularInheritance(String),
/// Element value outside the spec value range (e.g. `long` >
/// `0x7fffffff` without symbol aliasing).
///
/// Spec ref: DDS-XML 1.0 §7.1.4 Tab.7.1 (value ranges), §7.2.2
/// (`LENGTH_UNLIMITED`, `DURATION_INFINITE_*`).
ValueOutOfRange(String),
/// DoS cap hit — list/string exceeds the upper bound
/// configured in the loader (default: 1024 list elements, 64 KiB
/// strings).
///
/// No direct spec reference; follows the ZeroDDS security posture
/// (`docs/spec-coverage/zerodds-xml-1.0.open.md` risks section).
LimitExceeded(String),
/// A cross-reference (e.g. `base_name` of a QoS profile
/// inheritance) could not be resolved because the referenced
/// item does not exist.
///
/// Spec ref: DDS-XML 1.0 §7.3.2.4.2 (QoS profile inheritance).
UnresolvedReference(String),
}
impl fmt::Display for XmlError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidXml(msg) => write!(f, "invalid XML: {msg}"),
Self::MissingRequiredElement(name) => {
write!(f, "missing required element <{name}>")
}
Self::UnknownElement(name) => write!(f, "unknown element <{name}>"),
Self::BadEnum(value) => write!(f, "invalid enum value `{value}`"),
Self::CircularInheritance(chain) => {
write!(f, "circular base_name inheritance: {chain}")
}
Self::ValueOutOfRange(msg) => write!(f, "value out of range: {msg}"),
Self::LimitExceeded(msg) => write!(f, "DoS limit exceeded: {msg}"),
Self::UnresolvedReference(name) => write!(f, "unresolved reference `{name}`"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for XmlError {}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn display_invalid_xml() {
let e = XmlError::InvalidXml("unexpected token".into());
assert_eq!(e.to_string(), "invalid XML: unexpected token");
}
#[test]
fn display_missing_required() {
let e = XmlError::MissingRequiredElement("qos_profile".into());
assert_eq!(e.to_string(), "missing required element <qos_profile>");
}
#[test]
fn display_unknown_element() {
let e = XmlError::UnknownElement("foo".into());
assert_eq!(e.to_string(), "unknown element <foo>");
}
#[test]
fn display_bad_enum() {
let e = XmlError::BadEnum("WRONG".into());
assert_eq!(e.to_string(), "invalid enum value `WRONG`");
}
#[test]
fn display_circular() {
let e = XmlError::CircularInheritance("A -> B -> A".into());
assert_eq!(e.to_string(), "circular base_name inheritance: A -> B -> A");
}
#[test]
fn display_value_out_of_range() {
let e = XmlError::ValueOutOfRange("long > 0x7fffffff".into());
assert_eq!(e.to_string(), "value out of range: long > 0x7fffffff");
}
#[test]
fn display_limit_exceeded() {
let e = XmlError::LimitExceeded("seq > 1024".into());
assert_eq!(e.to_string(), "DoS limit exceeded: seq > 1024");
}
#[test]
fn equality_and_clone() {
let a = XmlError::InvalidXml("x".into());
let b = a.clone();
assert_eq!(a, b);
}
#[test]
fn display_unresolved_reference() {
let e = XmlError::UnresolvedReference("MissingProfile".into());
assert_eq!(e.to_string(), "unresolved reference `MissingProfile`");
}
}