1use alloc::format;
32use alloc::string::{String, ToString};
33use alloc::vec::Vec;
34
35use crate::errors::XmlError;
36use crate::parser::{XmlElement, parse_xml_tree};
37use crate::qos::EntityQos;
38use crate::qos_parser::parse_entity_qos_public;
39
40#[derive(Debug, Clone, Default, PartialEq, Eq)]
42pub struct DomainParticipantLibrary {
43 pub name: String,
45 pub participants: Vec<DomainParticipantEntry>,
47}
48
49impl DomainParticipantLibrary {
50 #[must_use]
52 pub fn participant(&self, name: &str) -> Option<&DomainParticipantEntry> {
53 self.participants.iter().find(|p| p.name == name)
54 }
55}
56
57#[derive(Debug, Clone, Default, PartialEq, Eq)]
59pub struct DomainParticipantEntry {
60 pub name: String,
62 pub domain_ref: String,
64 pub base_name: Option<String>,
66 pub qos: Option<EntityQos>,
68 pub register_types_ref: Vec<String>,
71 pub topics_ref: Vec<String>,
73 pub publishers: Vec<PublisherEntry>,
75 pub subscribers: Vec<SubscriberEntry>,
77}
78
79#[derive(Debug, Clone, Default, PartialEq, Eq)]
81pub struct PublisherEntry {
82 pub name: String,
84 pub qos: Option<EntityQos>,
86 pub data_writers: Vec<DataWriterEntry>,
88}
89
90#[derive(Debug, Clone, Default, PartialEq, Eq)]
92pub struct SubscriberEntry {
93 pub name: String,
95 pub qos: Option<EntityQos>,
97 pub data_readers: Vec<DataReaderEntry>,
99}
100
101#[derive(Debug, Clone, Default, PartialEq, Eq)]
103pub struct DataWriterEntry {
104 pub name: String,
106 pub topic_ref: String,
108 pub qos: Option<EntityQos>,
110 pub qos_profile_ref: Option<String>,
112}
113
114#[derive(Debug, Clone, Default, PartialEq, Eq)]
116pub struct DataReaderEntry {
117 pub name: String,
119 pub topic_ref: String,
121 pub qos: Option<EntityQos>,
123 pub qos_profile_ref: Option<String>,
125}
126
127pub fn parse_domain_participant_libraries(
133 xml: &str,
134) -> Result<Vec<DomainParticipantLibrary>, XmlError> {
135 let doc = parse_xml_tree(xml)?;
136 if doc.root.name != "dds" {
137 return Err(XmlError::InvalidXml(format!(
138 "expected <dds> root, got <{}>",
139 doc.root.name
140 )));
141 }
142 let mut libs = Vec::new();
143 for lib_node in doc.root.children_named("domain_participant_library") {
144 libs.push(parse_dp_library_element(lib_node)?);
145 }
146 Ok(libs)
147}
148
149pub(crate) fn parse_dp_library_element(
150 el: &XmlElement,
151) -> Result<DomainParticipantLibrary, XmlError> {
152 let name = el
153 .attribute("name")
154 .ok_or_else(|| XmlError::MissingRequiredElement("domain_participant_library@name".into()))?
155 .to_string();
156 let mut participants = Vec::new();
157 for p in el.children_named("domain_participant") {
158 participants.push(parse_dp_element(p)?);
159 }
160 Ok(DomainParticipantLibrary { name, participants })
161}
162
163fn parse_dp_element(el: &XmlElement) -> Result<DomainParticipantEntry, XmlError> {
164 let name = el
165 .attribute("name")
166 .ok_or_else(|| XmlError::MissingRequiredElement("domain_participant@name".into()))?
167 .to_string();
168 let domain_ref = el
169 .attribute("domain_ref")
170 .ok_or_else(|| XmlError::MissingRequiredElement("domain_participant@domain_ref".into()))?
171 .to_string();
172 let base_name = el.attribute("base_name").map(ToString::to_string);
173
174 let mut entry = DomainParticipantEntry {
175 name,
176 domain_ref,
177 base_name,
178 ..DomainParticipantEntry::default()
179 };
180
181 for child in &el.children {
182 match child.name.as_str() {
183 "domain_participant_qos" => entry.qos = Some(parse_entity_qos_public(child)?),
184 "register_type" => {
185 let r = child
186 .attribute("ref")
187 .or_else(|| child.attribute("name"))
188 .ok_or_else(|| {
189 XmlError::MissingRequiredElement(
190 "domain_participant/register_type@ref".into(),
191 )
192 })?
193 .to_string();
194 entry.register_types_ref.push(r);
195 }
196 "topic" => {
197 let r = child
198 .attribute("ref")
199 .or_else(|| child.attribute("name"))
200 .ok_or_else(|| {
201 XmlError::MissingRequiredElement("domain_participant/topic@ref".into())
202 })?
203 .to_string();
204 entry.topics_ref.push(r);
205 }
206 "publisher" => entry.publishers.push(parse_publisher_element(child)?),
207 "subscriber" => entry.subscribers.push(parse_subscriber_element(child)?),
208 _ => {}
209 }
210 }
211 Ok(entry)
212}
213
214fn parse_publisher_element(el: &XmlElement) -> Result<PublisherEntry, XmlError> {
215 let name = el
216 .attribute("name")
217 .ok_or_else(|| XmlError::MissingRequiredElement("publisher@name".into()))?
218 .to_string();
219 let mut qos: Option<EntityQos> = None;
220 let mut data_writers = Vec::new();
221 for child in &el.children {
222 match child.name.as_str() {
223 "publisher_qos" => qos = Some(parse_entity_qos_public(child)?),
224 "data_writer" => data_writers.push(parse_dw_element(child)?),
225 _ => {}
226 }
227 }
228 Ok(PublisherEntry {
229 name,
230 qos,
231 data_writers,
232 })
233}
234
235fn parse_subscriber_element(el: &XmlElement) -> Result<SubscriberEntry, XmlError> {
236 let name = el
237 .attribute("name")
238 .ok_or_else(|| XmlError::MissingRequiredElement("subscriber@name".into()))?
239 .to_string();
240 let mut qos: Option<EntityQos> = None;
241 let mut data_readers = Vec::new();
242 for child in &el.children {
243 match child.name.as_str() {
244 "subscriber_qos" => qos = Some(parse_entity_qos_public(child)?),
245 "data_reader" => data_readers.push(parse_dr_element(child)?),
246 _ => {}
247 }
248 }
249 Ok(SubscriberEntry {
250 name,
251 qos,
252 data_readers,
253 })
254}
255
256fn parse_dw_element(el: &XmlElement) -> Result<DataWriterEntry, XmlError> {
257 let name = el
258 .attribute("name")
259 .ok_or_else(|| XmlError::MissingRequiredElement("data_writer@name".into()))?
260 .to_string();
261 let topic_ref = el
262 .attribute("topic_ref")
263 .ok_or_else(|| XmlError::MissingRequiredElement("data_writer@topic_ref".into()))?
264 .to_string();
265 let qos_profile_ref = el.attribute("qos_profile_ref").map(ToString::to_string);
266 let mut qos: Option<EntityQos> = None;
267 for child in &el.children {
268 if child.name == "datawriter_qos" {
269 qos = Some(parse_entity_qos_public(child)?);
270 }
271 }
272 Ok(DataWriterEntry {
273 name,
274 topic_ref,
275 qos,
276 qos_profile_ref,
277 })
278}
279
280fn parse_dr_element(el: &XmlElement) -> Result<DataReaderEntry, XmlError> {
281 let name = el
282 .attribute("name")
283 .ok_or_else(|| XmlError::MissingRequiredElement("data_reader@name".into()))?
284 .to_string();
285 let topic_ref = el
286 .attribute("topic_ref")
287 .ok_or_else(|| XmlError::MissingRequiredElement("data_reader@topic_ref".into()))?
288 .to_string();
289 let qos_profile_ref = el.attribute("qos_profile_ref").map(ToString::to_string);
290 let mut qos: Option<EntityQos> = None;
291 for child in &el.children {
292 if child.name == "datareader_qos" {
293 qos = Some(parse_entity_qos_public(child)?);
294 }
295 }
296 Ok(DataReaderEntry {
297 name,
298 topic_ref,
299 qos,
300 qos_profile_ref,
301 })
302}
303
304#[cfg(test)]
305#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn parse_minimal_participant() {
311 let xml = r#"<dds>
312 <domain_participant_library name="dpl">
313 <domain_participant name="P" domain_ref="my_lib::MyDomain"/>
314 </domain_participant_library>
315 </dds>"#;
316 let libs = parse_domain_participant_libraries(xml).expect("parse");
317 assert_eq!(libs[0].name, "dpl");
318 assert_eq!(libs[0].participants[0].name, "P");
319 assert_eq!(libs[0].participants[0].domain_ref, "my_lib::MyDomain");
320 }
321
322 #[test]
323 fn parse_pub_with_writer() {
324 let xml = r#"<dds>
325 <domain_participant_library name="dpl">
326 <domain_participant name="P" domain_ref="dl::D">
327 <publisher name="Pub1">
328 <data_writer name="W1" topic_ref="StateTopic">
329 <datawriter_qos>
330 <reliability><kind>RELIABLE</kind></reliability>
331 </datawriter_qos>
332 </data_writer>
333 </publisher>
334 </domain_participant>
335 </domain_participant_library>
336 </dds>"#;
337 let libs = parse_domain_participant_libraries(xml).expect("parse");
338 let p = &libs[0].participants[0];
339 let pub1 = &p.publishers[0];
340 assert_eq!(pub1.name, "Pub1");
341 let w = &pub1.data_writers[0];
342 assert_eq!(w.name, "W1");
343 assert_eq!(w.topic_ref, "StateTopic");
344 assert!(w.qos.is_some());
345 }
346
347 #[test]
348 fn missing_domain_ref_rejected() {
349 let xml = r#"<dds>
350 <domain_participant_library name="dpl">
351 <domain_participant name="P"/>
352 </domain_participant_library>
353 </dds>"#;
354 let err = parse_domain_participant_libraries(xml).expect_err("missing");
355 assert!(matches!(err, XmlError::MissingRequiredElement(_)));
356 }
357
358 #[test]
359 fn dw_missing_topic_ref_rejected() {
360 let xml = r#"<dds>
361 <domain_participant_library name="dpl">
362 <domain_participant name="P" domain_ref="dl::D">
363 <publisher name="Pub1">
364 <data_writer name="W1"/>
365 </publisher>
366 </domain_participant>
367 </domain_participant_library>
368 </dds>"#;
369 let err = parse_domain_participant_libraries(xml).expect_err("missing");
370 assert!(matches!(err, XmlError::MissingRequiredElement(_)));
371 }
372}