use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use crate::errors::XmlError;
use crate::parser::{XmlElement, parse_xml_tree};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ApplicationLibrary {
pub name: String,
pub applications: Vec<ApplicationEntry>,
}
impl ApplicationLibrary {
#[must_use]
pub fn application(&self, name: &str) -> Option<&ApplicationEntry> {
self.applications.iter().find(|a| a.name == name)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ApplicationEntry {
pub name: String,
pub domain_participants: Vec<String>,
}
pub fn parse_application_libraries(xml: &str) -> Result<Vec<ApplicationLibrary>, XmlError> {
let doc = parse_xml_tree(xml)?;
if doc.root.name != "dds" {
return Err(XmlError::InvalidXml(format!(
"expected <dds> root, got <{}>",
doc.root.name
)));
}
let mut libs = Vec::new();
for lib_node in doc.root.children_named("application_library") {
libs.push(parse_app_library_element(lib_node)?);
}
Ok(libs)
}
pub(crate) fn parse_app_library_element(el: &XmlElement) -> Result<ApplicationLibrary, XmlError> {
let name = el
.attribute("name")
.ok_or_else(|| XmlError::MissingRequiredElement("application_library@name".into()))?
.to_string();
let mut applications = Vec::new();
for app_node in el.children_named("application") {
applications.push(parse_app_element(app_node)?);
}
Ok(ApplicationLibrary { name, applications })
}
fn parse_app_element(el: &XmlElement) -> Result<ApplicationEntry, XmlError> {
let name = el
.attribute("name")
.ok_or_else(|| XmlError::MissingRequiredElement("application@name".into()))?
.to_string();
let mut dps = Vec::new();
for child in el.children_named("domain_participant") {
let r = child
.attribute("ref")
.ok_or_else(|| {
XmlError::MissingRequiredElement("application/domain_participant@ref".into())
})?
.to_string();
dps.push(r);
}
Ok(ApplicationEntry {
name,
domain_participants: dps,
})
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn parse_minimal_application() {
let xml = r#"<dds>
<application_library name="al">
<application name="App">
<domain_participant ref="dpl::P"/>
</application>
</application_library>
</dds>"#;
let libs = parse_application_libraries(xml).expect("parse");
assert_eq!(libs[0].name, "al");
assert_eq!(libs[0].applications[0].name, "App");
assert_eq!(libs[0].applications[0].domain_participants[0], "dpl::P");
}
#[test]
fn missing_app_name_rejected() {
let xml = r#"<dds>
<application_library name="al">
<application/>
</application_library>
</dds>"#;
let err = parse_application_libraries(xml).expect_err("missing");
assert!(matches!(err, XmlError::MissingRequiredElement(_)));
}
#[test]
fn missing_dp_ref_rejected() {
let xml = r#"<dds>
<application_library name="al">
<application name="A">
<domain_participant/>
</application>
</application_library>
</dds>"#;
let err = parse_application_libraries(xml).expect_err("missing");
assert!(matches!(err, XmlError::MissingRequiredElement(_)));
}
}