cameleon_genapi/parser/
xml.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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}