cameleon_genapi/parser/
xml.rs1use std::{fmt, iter::Peekable};
6
7use crate::builder::{CacheStoreBuilder, NodeStoreBuilder, ValueStoreBuilder};
8
9use super::{Parse, ParseResult};
10
11pub(super) struct Document<'input> {
12 document: roxmltree::Document<'input>,
13}
14
15impl<'input> Document<'input> {
16 pub(super) fn from_str(s: &'input str) -> ParseResult<Self> {
17 let document = roxmltree::Document::parse(s)?;
18 Ok(Self { document })
19 }
20
21 pub(super) fn root_node<'a>(&'a self) -> Node<'a, 'input> {
22 let root = self.document.root_element();
23 Node::from_xmltree_node(root, self.inner_str())
24 }
25
26 pub(super) fn inner_str(&self) -> &'input str {
27 self.document.input_text()
28 }
29}
30
31pub(super) struct Node<'a, 'input> {
32 inner: roxmltree::Node<'a, 'input>,
33 children: Peekable<roxmltree::Children<'a, 'input>>,
34 attributes: Attributes<'a, 'input>,
35 src: &'input str,
36}
37
38impl<'a, 'input> Node<'a, 'input> {
39 pub(super) fn parse<T: Parse>(
40 &mut self,
41 node_builder: &mut impl NodeStoreBuilder,
42 value_builder: &mut impl ValueStoreBuilder,
43 cache_builder: &mut impl CacheStoreBuilder,
44 ) -> T {
45 T::parse(self, node_builder, value_builder, cache_builder)
46 }
47
48 pub(super) fn parse_if<T: Parse>(
49 &mut self,
50 tag_name: &str,
51 node_builder: &mut impl NodeStoreBuilder,
52 value_builder: &mut impl ValueStoreBuilder,
53 cache_builder: &mut impl CacheStoreBuilder,
54 ) -> Option<T> {
55 if self.peek()?.tag_name() == tag_name {
56 Some(self.parse(node_builder, value_builder, cache_builder))
57 } else {
58 None
59 }
60 }
61
62 pub(super) fn parse_while<T: Parse>(
63 &mut self,
64 tag_name: &str,
65 node_builder: &mut impl NodeStoreBuilder,
66 value_builder: &mut impl ValueStoreBuilder,
67 cache_builder: &mut impl CacheStoreBuilder,
68 ) -> Vec<T> {
69 let mut res = vec![];
70 while let Some(parsed) = self.parse_if(tag_name, node_builder, value_builder, cache_builder)
71 {
72 res.push(parsed);
73 }
74 res
75 }
76
77 pub(super) fn next(&mut self) -> Option<Self> {
78 let node = self.peek()?;
79 self.children.next();
80
81 Some(node)
82 }
83
84 pub(super) fn next_if(&mut self, tag_name: &str) -> Option<Self> {
85 if self.peek()?.tag_name() == tag_name {
86 self.next()
87 } else {
88 None
89 }
90 }
91
92 pub(super) fn next_text(&mut self) -> Option<TextView<'a, 'input>> {
93 Some(self.next()?.text())
94 }
95
96 pub(super) fn peek(&mut self) -> Option<Self> {
97 let mut inner;
98 loop {
99 inner = self.children.peek()?;
100 if inner.node_type() == roxmltree::NodeType::Element {
101 break;
102 }
103 self.children.next();
104 }
105 let node = Self::from_xmltree_node(*inner, self.src);
106
107 Some(node)
108 }
109
110 pub(super) fn tag_name(&self) -> &str {
111 self.inner.tag_name().name()
112 }
113
114 pub(super) fn attribute_of(&self, name: &str) -> Option<&str> {
115 self.attributes.attribute_of(name)
116 }
117
118 pub(super) fn text(&self) -> TextView<'a, 'input> {
119 TextView { inner: self.inner }
120 }
121
122 fn from_xmltree_node(node: roxmltree::Node<'a, 'input>, src: &'input str) -> Self {
123 debug_assert!(node.node_type() == roxmltree::NodeType::Element);
124 let children = node.children().peekable();
125 let attributes = Attributes::from_xmltree_attrs(node.attributes());
126
127 Self {
128 inner: node,
129 children,
130 attributes,
131 src,
132 }
133 }
134}
135
136impl fmt::Debug for Node<'_, '_> {
137 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138 let span = self.inner.range();
139 let node_src = std::str::from_utf8(&self.src.as_bytes()[span]).unwrap();
140 write!(f, "{}", node_src)
141 }
142}
143
144struct Attributes<'a, 'input> {
145 attrs: &'a [roxmltree::Attribute<'input>],
146}
147
148impl<'a, 'input> Attributes<'a, 'input> {
149 fn from_xmltree_attrs(attrs: &'a [roxmltree::Attribute<'input>]) -> Self {
150 Self { attrs }
151 }
152
153 fn attribute_of(&self, name: &str) -> Option<&str> {
154 self.attrs.iter().find_map(|attr| {
155 if attr.name() == name {
156 Some(roxmltree::Attribute::value(attr))
157 } else {
158 None
159 }
160 })
161 }
162}
163
164pub(super) struct TextView<'a, 'input> {
165 inner: roxmltree::Node<'a, 'input>,
166}
167
168impl<'a> TextView<'a, '_> {
169 pub(super) fn view(&self) -> std::borrow::Cow<'a, str> {
170 let first_child = self.inner.first_child().unwrap();
171 if first_child.has_siblings() {
172 let mut s = String::new();
173 for child in self.inner.children() {
174 let child = if child.is_text() {
175 child.text().unwrap()
176 } else {
177 continue;
178 };
179 s.push_str(child);
180 }
181 s.into()
182 } else {
183 first_child.text().unwrap().into()
184 }
185 }
186}
187
188impl PartialEq<&str> for TextView<'_, '_> {
189 fn eq(&self, rhs: &&str) -> bool {
190 &self.view() == rhs
191 }
192}