1#![forbid(unsafe_code)]
4
5use rusty_xml_parser::{xml_read_memory, XmlError};
6use rusty_xml_tree::{NodeId, NodeKind, XmlDoc};
7
8#[derive(Clone, Copy, PartialEq, Eq, Debug)]
10#[repr(i32)]
11pub enum ReaderType {
12 None = 0,
13 Element = 1,
14 Attribute = 2,
15 Text = 3,
16 CData = 4,
17 EntityReference = 5,
18 Entity = 6,
19 ProcessingInstruction = 7,
20 Comment = 8,
21 Document = 9,
22 DocumentType = 10,
23 DocumentFragment = 11,
24 Notation = 12,
25 Whitespace = 13,
26 SignificantWhitespace = 14,
27 EndElement = 15,
28 EndEntity = 16,
29 XmlDeclaration = 17,
30}
31
32#[derive(Clone, Copy)]
33struct Cursor {
34 id: NodeId,
35 end: bool,
36}
37
38pub struct XmlTextReader {
40 doc: XmlDoc,
41 cur: Option<Cursor>,
42 started: bool,
43 eof: bool,
44 attr: Option<NodeId>,
45}
46
47impl XmlTextReader {
48 fn kind_of(doc: &XmlDoc, id: NodeId, end: bool) -> ReaderType {
49 if end {
50 return ReaderType::EndElement;
51 }
52 match doc.kind(id) {
53 NodeKind::Element => ReaderType::Element,
54 NodeKind::Attribute => ReaderType::Attribute,
55 NodeKind::Text => {
56 if doc.xml_is_blank_node(id) {
57 ReaderType::SignificantWhitespace
58 } else {
59 ReaderType::Text
60 }
61 }
62 NodeKind::CData => ReaderType::CData,
63 NodeKind::Pi => ReaderType::ProcessingInstruction,
64 NodeKind::Comment => ReaderType::Comment,
65 NodeKind::Document => ReaderType::Document,
66 _ => ReaderType::None,
67 }
68 }
69
70 #[doc(alias = "xmlReaderForMemory")]
72 pub fn xml_reader_for_memory(
73 buffer: &[u8],
74 url: Option<&str>,
75 encoding: Option<&str>,
76 options: i32,
77 ) -> Result<Self, XmlError> {
78 let doc = xml_read_memory(buffer, url, encoding, options)?;
79 Ok(Self {
80 doc,
81 cur: None,
82 started: false,
83 eof: false,
84 attr: None,
85 })
86 }
87
88 #[doc(alias = "xmlReaderWalker")]
90 pub fn xml_reader_walker(doc: XmlDoc) -> Self {
91 Self {
92 doc,
93 cur: None,
94 started: false,
95 eof: false,
96 attr: None,
97 }
98 }
99
100 #[doc(alias = "xmlTextReaderRead")]
102 pub fn read(&mut self) -> i32 {
103 self.attr = None;
104 if self.eof {
105 return 0;
106 }
107 if !self.started {
108 self.started = true;
109 if let Some(id) = self.doc.first_child(NodeId::DOCUMENT) {
110 self.cur = Some(Cursor { id, end: false });
111 return 1;
112 }
113 self.eof = true;
114 return 0;
115 }
116 let Cursor { id, end } = match self.cur {
117 Some(c) => c,
118 None => {
119 self.eof = true;
120 return 0;
121 }
122 };
123 if !end && self.doc.kind(id) == NodeKind::Element {
124 if let Some(ch) = self.doc.first_child(id) {
125 self.cur = Some(Cursor { id: ch, end: false });
126 return 1;
127 }
128 return self.advance_after(id);
130 }
131 self.advance_after(id)
132 }
133
134 fn advance_after(&mut self, id: NodeId) -> i32 {
135 if let Some(n) = self.doc.next_sibling(id) {
136 self.cur = Some(Cursor { id: n, end: false });
137 return 1;
138 }
139 match self.doc.parent(id) {
140 Some(p) if p != NodeId::DOCUMENT && self.doc.kind(p) == NodeKind::Element => {
141 self.cur = Some(Cursor { id: p, end: true });
142 1
143 }
144 _ => {
145 self.eof = true;
146 self.cur = None;
147 0
148 }
149 }
150 }
151
152 pub fn node_type(&self) -> ReaderType {
153 match self.cur {
154 Some(Cursor { id, end }) => Self::kind_of(&self.doc, id, end),
155 None => ReaderType::None,
156 }
157 }
158
159 pub fn depth(&self) -> i32 {
160 let mut d = 0;
161 if let Some(Cursor { mut id, .. }) = self.cur {
162 while let Some(p) = self.doc.parent(id) {
163 if p == NodeId::DOCUMENT {
164 break;
165 }
166 d += 1;
167 id = p;
168 }
169 }
170 d
171 }
172
173 pub fn is_empty_element(&self) -> bool {
174 match self.cur {
175 Some(Cursor { id, end: false }) if self.doc.kind(id) == NodeKind::Element => {
176 self.doc.first_child(id).is_none()
177 }
178 _ => false,
179 }
180 }
181
182 pub fn local_name(&self) -> Option<&str> {
183 self.cur.map(|c| self.doc.name(c.id))
184 }
185
186 pub fn prefix(&self) -> Option<&str> {
187 self.cur.and_then(|c| self.doc.prefix(c.id))
188 }
189
190 pub fn namespace_uri(&self) -> Option<&str> {
191 self.cur.and_then(|c| self.doc.ns_uri(c.id))
192 }
193
194 pub fn value(&self) -> Option<&str> {
195 if let Some(a) = self.attr {
196 return Some(self.doc.content(a));
197 }
198 match self.cur {
199 Some(Cursor { id, end: false }) => match self.doc.kind(id) {
200 NodeKind::Text | NodeKind::CData | NodeKind::Comment | NodeKind::Pi => {
201 Some(self.doc.content(id))
202 }
203 _ => None,
204 },
205 _ => None,
206 }
207 }
208
209 pub fn name(&self) -> Option<String> {
210 self.cur.map(|c| self.doc.qname(c.id))
211 }
212
213 pub fn attribute_count(&self) -> i32 {
214 match self.cur {
215 Some(Cursor { id, end: false }) if self.doc.kind(id) == NodeKind::Element => {
216 self.doc.attrs(id).count() as i32
217 }
218 _ => 0,
219 }
220 }
221
222 pub fn has_attributes(&self) -> bool {
223 self.attribute_count() > 0
224 }
225
226 #[doc(alias = "xmlTextReaderGetAttribute")]
228 pub fn get_attribute(&self, name: &str) -> Option<String> {
229 let Cursor { id, end } = self.cur?;
230 if end {
231 return None;
232 }
233 self.doc.xml_get_prop(id, name)
234 }
235
236 #[doc(alias = "xmlTextReaderMoveToFirstAttribute")]
238 pub fn move_to_first_attribute(&mut self) -> i32 {
239 let Cursor { id, end } = match self.cur {
240 Some(c) => c,
241 None => return 0,
242 };
243 if end {
244 return 0;
245 }
246 match self.doc.first_attr(id) {
247 Some(a) => {
248 self.attr = Some(a);
249 1
250 }
251 None => 0,
252 }
253 }
254
255 #[doc(alias = "xmlTextReaderMoveToNextAttribute")]
257 pub fn move_to_next_attribute(&mut self) -> i32 {
258 match self.attr {
259 Some(a) => match self.doc.next_sibling(a) {
260 Some(n) => {
261 self.attr = Some(n);
262 1
263 }
264 None => 0,
265 },
266 None => self.move_to_first_attribute(),
267 }
268 }
269
270 #[doc(alias = "xmlTextReaderMoveToElement")]
272 pub fn move_to_element(&mut self) -> i32 {
273 if self.attr.is_some() {
274 self.attr = None;
275 1
276 } else {
277 0
278 }
279 }
280
281 pub fn doc(&self) -> &XmlDoc {
282 &self.doc
283 }
284}
285
286#[doc(alias = "xmlReaderForMemory")]
288pub fn xml_reader_for_memory(
289 buffer: &[u8],
290 url: Option<&str>,
291 encoding: Option<&str>,
292 options: i32,
293) -> Result<XmlTextReader, XmlError> {
294 XmlTextReader::xml_reader_for_memory(buffer, url, encoding, options)
295}
296
297#[doc(alias = "xmlReaderForDoc")]
299pub fn xml_reader_for_doc(
300 cur: &str,
301 url: Option<&str>,
302 encoding: Option<&str>,
303 options: i32,
304) -> Result<XmlTextReader, XmlError> {
305 xml_reader_for_memory(cur.as_bytes(), url, encoding, options)
306}