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