opcua_xml/
ext.rs

1use roxmltree::Node;
2
3use crate::{error::XmlError, FromValue, XmlLoad};
4
5pub(crate) trait NodeExt<'a, 'input: 'a> {
6    fn first_child_with_name(&self, name: &str) -> Result<Node<'a, 'input>, XmlError>;
7
8    fn with_name(&self, name: &str) -> impl Iterator<Item = Node<'a, 'input>>;
9
10    fn try_attribute(&self, name: &str) -> Result<&'a str, XmlError>;
11
12    fn try_contents(&self) -> Result<&'a str, XmlError>;
13}
14
15impl<'a, 'input: 'a> NodeExt<'a, 'input> for Node<'a, 'input> {
16    fn first_child_with_name(&self, name: &str) -> Result<Node<'a, 'input>, XmlError> {
17        self.with_name(name)
18            .next()
19            .ok_or_else(|| XmlError::missing_field(self, name))
20    }
21
22    fn with_name(&self, name: &str) -> impl Iterator<Item = Node<'a, 'input>> {
23        self.children().filter(move |n| n.has_tag_name(name))
24    }
25
26    fn try_attribute(&self, name: &str) -> Result<&'a str, XmlError> {
27        self.attribute(name)
28            .ok_or_else(|| XmlError::missing_attribute(self, name))
29    }
30
31    fn try_contents(&self) -> Result<&'a str, XmlError> {
32        self.text().ok_or_else(|| XmlError::missing_content(self))
33    }
34}
35
36pub(crate) fn children_with_name<'input, T: XmlLoad<'input>>(
37    node: &Node<'_, 'input>,
38    name: &str,
39) -> Result<Vec<T>, XmlError> {
40    node.with_name(name).map(|e| T::load(&e)).collect()
41}
42
43#[allow(unused)]
44pub(crate) fn first_child_with_name<'input, T: XmlLoad<'input>>(
45    node: &Node<'_, 'input>,
46    name: &str,
47) -> Result<T, XmlError> {
48    T::load(&node.first_child_with_name(name)?)
49}
50
51pub(crate) fn first_child_with_name_opt<'input, T: XmlLoad<'input>>(
52    node: &Node<'_, 'input>,
53    name: &str,
54) -> Result<Option<T>, XmlError> {
55    let Ok(child) = node.first_child_with_name(name) else {
56        return Ok(None);
57    };
58    T::load(&child).map(|v| Some(v))
59}
60
61pub(crate) fn uint_attr(node: &Node<'_, '_>, name: &str) -> Result<Option<u64>, XmlError> {
62    node.attribute(name)
63        .map(|a| a.parse())
64        .transpose()
65        .map_err(|e| XmlError::parse_int(node, name, e))
66}
67
68pub(crate) fn int_attr(node: &Node<'_, '_>, name: &str) -> Result<Option<i64>, XmlError> {
69    node.attribute(name)
70        .map(|a| a.parse())
71        .transpose()
72        .map_err(|e| XmlError::parse_int(node, name, e))
73}
74
75pub(crate) fn value_from_contents<T: FromValue>(node: &Node<'_, '_>) -> Result<T, XmlError> {
76    T::from_value(node, "content", node.try_contents()?)
77}
78
79pub(crate) fn value_from_attr<T: FromValue>(
80    node: &Node<'_, '_>,
81    attr: &str,
82) -> Result<T, XmlError> {
83    T::from_value(node, attr, node.try_attribute(attr)?)
84}
85
86#[allow(unused)]
87pub(crate) fn value_from_contents_opt<T: FromValue>(
88    node: &Node<'_, '_>,
89) -> Result<Option<T>, XmlError> {
90    let Some(c) = node.text() else {
91        return Ok(None);
92    };
93
94    T::from_value(node, "content", c).map(Some)
95}
96
97pub(crate) fn value_from_attr_opt<T: FromValue>(
98    node: &Node<'_, '_>,
99    attr: &str,
100) -> Result<Option<T>, XmlError> {
101    let Some(c) = node.attribute(attr) else {
102        return Ok(None);
103    };
104
105    T::from_value(node, attr, c).map(Some)
106}
107
108pub(crate) fn children_of_type<'input, T>(node: &Node<'_, 'input>) -> Result<Vec<T>, XmlError>
109where
110    Option<T>: XmlLoad<'input>,
111{
112    node.children()
113        .filter_map(|n| XmlLoad::load(&n).transpose())
114        .collect()
115}
116
117#[allow(unused)]
118pub(crate) fn first_child_of_type<'input, T>(node: &Node<'_, 'input>) -> Result<Option<T>, XmlError>
119where
120    Option<T>: XmlLoad<'input>,
121{
122    node.children()
123        .filter_map(|n| XmlLoad::load(&n).transpose())
124        .next()
125        .transpose()
126}
127
128#[allow(unused)]
129pub(crate) fn first_child_of_type_req<'input, T>(
130    node: &Node<'_, 'input>,
131    ctx: &str,
132) -> Result<T, XmlError>
133where
134    Option<T>: XmlLoad<'input>,
135{
136    node.children()
137        .filter_map(|n| XmlLoad::load(&n).transpose())
138        .next()
139        .ok_or_else(|| XmlError::other(node, &format!("Expected child of type {ctx}")))?
140}
141
142impl<T> FromValue for Vec<T>
143where
144    T: FromValue,
145{
146    fn from_value(node: &Node<'_, '_>, attr: &str, v: &str) -> Result<Self, XmlError> {
147        v.split_whitespace()
148            .map(|v| T::from_value(node, attr, v))
149            .collect()
150    }
151}