Skip to main content

regxml_dict/
labels.rs

1//! SMPTE LabelsRegister parser.
2//!
3//! Parses a SMPTE Labels register XML (namespace `http://www.smpte-ra.org/schemas/400/2012`)
4//! and provides UL → Symbol lookup.  Used by `regxml` to resolve ExtendibleEnumeration values.
5
6use std::collections::HashMap;
7
8use smpte_types::Auid;
9
10use crate::DictError;
11
12/// Parsed SMPTE LabelsRegister: maps 16-byte UL AUID → Symbol string.
13pub struct LabelsRegister {
14    map: HashMap<Auid, String>,
15}
16
17impl LabelsRegister {
18    /// Parse a LabelsRegister XML byte slice.
19    ///
20    /// Returns an empty register (no error) if the root element is not
21    /// `LabelsRegister`, so callers can pass any register XML without
22    /// type-checking it first.
23    pub fn from_xml(xml: &[u8]) -> Result<Self, DictError> {
24        let text = std::str::from_utf8(xml).map_err(|e| DictError::Xml(e.to_string()))?;
25        let doc = roxmltree::Document::parse(text).map_err(|e| DictError::Xml(e.to_string()))?;
26        let root = doc.root_element();
27        if root.tag_name().name() != "LabelsRegister" {
28            return Ok(Self {
29                map: HashMap::new(),
30            });
31        }
32
33        let mut map = HashMap::new();
34
35        let entries = match root.children().find(|n| n.tag_name().name() == "Entries") {
36            Some(e) => e,
37            None => return Ok(Self { map }),
38        };
39
40        for entry in entries
41            .children()
42            .filter(|n| n.tag_name().name() == "Entry")
43        {
44            // Skip NODE (non-leaf) entries.
45            let kind = entry
46                .children()
47                .find(|n| n.tag_name().name() == "Kind")
48                .and_then(|n| n.text());
49            if kind == Some("NODE") {
50                continue;
51            }
52
53            let ul_str = match entry
54                .children()
55                .find(|n| n.tag_name().name() == "UL")
56                .and_then(|n| n.text())
57            {
58                Some(s) => s,
59                None => continue,
60            };
61
62            let symbol = match entry
63                .children()
64                .find(|n| n.tag_name().name() == "Symbol")
65                .and_then(|n| n.text())
66            {
67                Some(s) => s,
68                None => continue,
69            };
70
71            if let Ok(auid) = Auid::from_urn(ul_str) {
72                map.insert(auid, symbol.to_owned());
73            }
74        }
75
76        Ok(Self { map })
77    }
78
79    /// Construct an empty register.
80    pub fn empty() -> Self {
81        Self {
82            map: HashMap::new(),
83        }
84    }
85
86    /// Look up the symbol for a UL.
87    pub fn get(&self, id: &Auid) -> Option<&str> {
88        self.map.get(id).map(String::as_str)
89    }
90
91    /// Merge `other` into `self`, returning `self`.
92    pub fn merge(mut self, other: Self) -> Self {
93        for (k, v) in other.map {
94            self.map.entry(k).or_insert(v);
95        }
96        self
97    }
98
99    /// Number of entries in the register.
100    pub fn len(&self) -> usize {
101        self.map.len()
102    }
103
104    /// Returns `true` if the register contains no entries.
105    pub fn is_empty(&self) -> bool {
106        self.map.is_empty()
107    }
108}