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}