1#![cfg_attr(not(feature = "std"), no_std)]
43#![forbid(unsafe_code)]
44#![warn(missing_docs)]
45#![cfg_attr(test, allow(clippy::panic, clippy::unwrap_used, clippy::expect_used))]
46
47extern crate alloc;
48
49#[cfg(feature = "std")]
50extern crate std;
51
52pub mod application;
53pub mod conformance;
54pub mod domain;
55pub mod errors;
56pub mod inheritance;
57pub mod parser;
58pub mod participant;
59pub mod qos;
60pub mod qos_inheritance;
61pub mod qos_parser;
62pub mod registry;
63pub mod resolver;
64pub mod sample;
65pub mod schemas;
66pub mod typeobject_bridge;
67pub mod types;
68pub mod xsd_loader;
69pub mod xsd_schema;
70pub mod xtypes_def;
71pub mod xtypes_parser;
72pub mod zerodds_xml;
73
74pub use application::{ApplicationEntry, ApplicationLibrary, parse_application_libraries};
75pub use conformance::{IDL_TO_XML_MAPPING, SUPPORTED_BUILDING_BLOCKS};
76pub use domain::{DomainEntry, DomainLibrary, RegisterType, TopicEntry, parse_domain_libraries};
77pub use errors::XmlError;
78pub use inheritance::{MAX_INHERITANCE_DEPTH, resolve_chain};
79pub use parser::{
80 DDS_XML_NS, DdsXmlDocument, MAX_LIST_ELEMENTS, MAX_TOTAL_ELEMENTS, XmlElement, parse_xml_tree,
81};
82pub use participant::{
83 DataReaderEntry, DataWriterEntry, DomainParticipantEntry, DomainParticipantLibrary,
84 PublisherEntry, SubscriberEntry, parse_domain_participant_libraries,
85};
86pub use qos::{EntityQos, QosLibrary, QosProfile, topic_filter_matches};
87pub use qos_inheritance::{ResolvedQos, resolve_profile};
88pub use qos_parser::{
89 parse_bool_strict, parse_entity_qos_public, parse_qos_libraries, parse_qos_library,
90 parse_qos_library_element_public,
91};
92pub use registry::QosProfileRegistry;
93pub use resolver::{LibraryRef, parse_library_ref};
94pub use sample::{
95 PrimitiveValue, SampleValue, parse_sample, parse_sample_element, serialize_sample,
96};
97pub use schemas::{
98 ALL_SCHEMAS, APPLICATIONS_NAMESPACED_XSD, APPLICATIONS_NONAMESPACE_XSD, COMMON_XSD,
99 DATA_SAMPLES_NAMESPACED_XSD, DATA_SAMPLES_NONAMESPACE_XSD, DDS_SYSTEM_NAMESPACED_XSD,
100 DDS_SYSTEM_NONAMESPACE_XSD, DOMAIN_PARTICIPANTS_NAMESPACED_XSD,
101 DOMAIN_PARTICIPANTS_NONAMESPACE_XSD, DOMAINS_NAMESPACED_XSD, DOMAINS_NONAMESPACE_XSD,
102 QOS_NAMESPACED_XSD, QOS_NONAMESPACE_XSD, TYPES_NAMESPACED_XSD, TYPES_NONAMESPACE_XSD,
103 embedded_block_names,
104};
105pub use typeobject_bridge::{
106 BridgeError, bridge_library, xml_type_to_minimal_typeobject, xml_type_to_typeobject,
107};
108pub use types::{
109 DURATION_INFINITE_NSEC, DURATION_INFINITE_SEC, DURATION_ZERO_NSEC, DURATION_ZERO_SEC, Duration,
110 LENGTH_UNLIMITED, MAX_STRING_BYTES, TIME_INVALID_NSEC, TIME_INVALID_SEC, parse_bool,
111 parse_duration_nsec, parse_duration_sec, parse_enum, parse_long, parse_octet_sequence,
112 parse_positive_long_unlimited, parse_string, parse_ulong,
113};
114pub use xtypes_def::{
115 BitField, BitValue, BitmaskType, BitsetType, EnumLiteral, EnumType, Extensibility, ModuleEntry,
116 PrimitiveType, StructMember, StructType, TypeDef, TypeLibrary, TypeRef, TypedefType, UnionCase,
117 UnionDiscriminator, UnionType,
118};
119pub use xtypes_parser::{parse_type_libraries, parse_types_element};
120pub use zerodds_xml::{
121 DdsXml, ParticipantFactoryAdapter, ResolvedDataReader, ResolvedDataWriter, ResolvedParticipant,
122 ResolvedPublisher, ResolvedSubscriber, ResolvedTopic, apply_to_factory, parse_dds_xml,
123};
124
125#[cfg(feature = "std")]
126pub use xsd_loader::load_type_libraries_from_uri;
127pub use xsd_loader::{
128 DDS_XML_NAMESPACE, MAX_DATA_URI_BODY, MAX_FILE_BYTES, ValidationMode,
129 load_type_libraries_from_string,
130};
131
132#[cfg(test)]
133#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
134mod integration_tests {
135 use super::*;
136 use alloc::collections::BTreeMap;
137 use alloc::string::{String, ToString};
138
139 #[test]
142 fn qos_profile_shape_roundtrip() {
143 let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
144<dds xmlns="http://www.omg.org/spec/DDS-XML">
145 <!-- root namespace per §7.3.x targetNamespace -->
146 <qos_library name="Lib1">
147 <qos_profile name="Base">
148 <datawriter_qos>
149 <reliability>
150 <kind>RELIABLE_RELIABILITY_QOS</kind>
151 <max_blocking_time>
152 <sec>DURATION_INFINITE_SEC</sec>
153 <nanosec>DURATION_INFINITE_NSEC</nanosec>
154 </max_blocking_time>
155 </reliability>
156 <history>
157 <kind>KEEP_LAST_HISTORY_QOS</kind>
158 <depth>10</depth>
159 </history>
160 <resource_limits>
161 <max_samples>LENGTH_UNLIMITED</max_samples>
162 </resource_limits>
163 </datawriter_qos>
164 </qos_profile>
165 <qos_profile name="Derived" base_name="Base">
166 </qos_profile>
167 </qos_library>
168</dds>"#;
169 let doc = parse_xml_tree(xml).expect("parse");
170 assert_eq!(doc.root.name, "dds");
171 assert_eq!(doc.root.namespace.as_deref(), Some(DDS_XML_NS));
172
173 let lib = doc.root.child("qos_library").expect("lib");
174 assert_eq!(lib.attribute("name"), Some("Lib1"));
175
176 let profiles: alloc::vec::Vec<_> = lib.children_named("qos_profile").collect();
177 assert_eq!(profiles.len(), 2);
178
179 let base = profiles[0];
180 assert_eq!(base.attribute("name"), Some("Base"));
181 assert_eq!(base.attribute("base_name"), None);
182
183 let derived = profiles[1];
184 assert_eq!(derived.attribute("name"), Some("Derived"));
185 assert_eq!(derived.attribute("base_name"), Some("Base"));
186
187 let dw = base.child("datawriter_qos").expect("dw");
189 let rel = dw.child("reliability").expect("rel");
190 let kind_str = rel.child("kind").expect("kind").text.as_str();
191 let kind = parse_enum(
192 kind_str,
193 &["BEST_EFFORT_RELIABILITY_QOS", "RELIABLE_RELIABILITY_QOS"],
194 )
195 .expect("enum");
196 assert_eq!(kind, "RELIABLE_RELIABILITY_QOS");
197
198 let mbt = rel.child("max_blocking_time").expect("mbt");
199 let sec = parse_duration_sec(mbt.child("sec").expect("sec").text.as_str()).expect("sec");
200 let nsec =
201 parse_duration_nsec(mbt.child("nanosec").expect("nsec").text.as_str()).expect("nsec");
202 let dur = Duration { sec, nanosec: nsec };
203 assert!(dur.is_infinite(), "max_blocking_time should be INFINITE");
204
205 let rl = dw.child("resource_limits").expect("rl");
207 let ms = parse_long(rl.child("max_samples").expect("ms").text.as_str()).expect("long");
208 assert_eq!(ms, LENGTH_UNLIMITED);
209
210 let hist = dw.child("history").expect("hist");
212 let depth = parse_long(hist.child("depth").expect("depth").text.as_str()).expect("depth");
213 assert_eq!(depth, 10);
214
215 let mut by_name: BTreeMap<String, Option<String>> = BTreeMap::new();
218 for p in lib.children_named("qos_profile") {
219 let n = p.attribute("name").expect("named").to_string();
220 let b = p.attribute("base_name").map(ToString::to_string);
221 by_name.insert(n, b);
222 }
223 let chain = resolve_chain("Derived", |n| {
224 by_name
225 .get(n)
226 .cloned()
227 .ok_or_else(|| XmlError::MissingRequiredElement(n.to_string()))
228 })
229 .expect("chain");
230 assert_eq!(
231 chain,
232 alloc::vec!["Base".to_string(), "Derived".to_string()]
233 );
234 }
235
236 #[test]
238 fn detect_cycle_between_profiles() {
239 let xml = r#"<dds>
240 <qos_library name="L">
241 <qos_profile name="A" base_name="B"/>
242 <qos_profile name="B" base_name="A"/>
243 </qos_library>
244 </dds>"#;
245 let doc = parse_xml_tree(xml).expect("parse");
246 let lib = doc.root.child("qos_library").expect("lib");
247 let mut by_name: BTreeMap<String, Option<String>> = BTreeMap::new();
248 for p in lib.children_named("qos_profile") {
249 let n = p.attribute("name").expect("name").to_string();
250 let b = p.attribute("base_name").map(ToString::to_string);
251 by_name.insert(n, b);
252 }
253 let err = resolve_chain("A", |n| {
254 by_name
255 .get(n)
256 .cloned()
257 .ok_or_else(|| XmlError::MissingRequiredElement(n.to_string()))
258 })
259 .expect_err("cycle");
260 assert!(matches!(err, XmlError::CircularInheritance(_)));
261 }
262}