quick_xml/reader/mod.rs
1//! Contains high-level interface for a pull-based XML parser.
2
3#[cfg(feature = "encoding")]
4use encoding_rs;
5use std::io;
6use std::ops::Range;
7
8#[cfg(feature = "encoding")]
9use crate::encoding::DetectedEncoding;
10use crate::errors::{Error, IllFormedError, SyntaxError};
11use crate::events::{BytesRef, Event};
12use crate::parser::{DtdParser, ElementParser, Parser, PiParser};
13use crate::reader::state::ReaderState;
14
15/// A struct that holds a parser configuration.
16///
17/// Current parser configuration can be retrieved by calling [`Reader::config()`]
18/// and changed by changing properties of the object returned by a call to
19/// [`Reader::config_mut()`].
20///
21/// [`Reader::config()`]: crate::reader::Reader::config
22/// [`Reader::config_mut()`]: crate::reader::Reader::config_mut
23#[derive(Debug, Clone, PartialEq, Eq)]
24#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
25#[cfg_attr(feature = "serde-types", derive(serde::Deserialize, serde::Serialize))]
26#[non_exhaustive]
27pub struct Config {
28 /// Whether lone ampersand character (without a paired semicolon) should be
29 /// allowed in textual content. Unless enabled, in case of a dangling ampersand,
30 /// the [`Error::IllFormed(UnclosedReference)`] is returned from read methods.
31 ///
32 /// Default: `false`
33 ///
34 /// # Example
35 ///
36 /// ```
37 /// # use quick_xml::events::{BytesRef, BytesText, Event};
38 /// # use quick_xml::reader::Reader;
39 /// # use pretty_assertions::assert_eq;
40 /// let mut reader = Reader::from_str("text with & & & alone");
41 /// reader.config_mut().allow_dangling_amp = true;
42 ///
43 /// assert_eq!(reader.read_event().unwrap(), Event::Text(BytesText::new("text with ")));
44 /// assert_eq!(reader.read_event().unwrap(), Event::Text(BytesText::from_escaped("& ")));
45 /// assert_eq!(reader.read_event().unwrap(), Event::GeneralRef(BytesRef::new("amp")));
46 /// assert_eq!(reader.read_event().unwrap(), Event::Text(BytesText::new(" ")));
47 /// assert_eq!(reader.read_event().unwrap(), Event::Text(BytesText::from_escaped("& alone")));
48 /// assert_eq!(reader.read_event().unwrap(), Event::Eof);
49 /// ```
50 ///
51 /// [`Error::IllFormed(UnclosedReference)`]: crate::errors::IllFormedError::UnclosedReference
52 pub allow_dangling_amp: bool,
53
54 /// Whether unmatched closing tag names should be allowed. Unless enabled,
55 /// in case of a dangling end tag, the [`Error::IllFormed(UnmatchedEndTag)`]
56 /// is returned from read methods.
57 ///
58 /// When set to `true`, it won't check if a closing tag has a corresponding
59 /// opening tag at all. For example, `<a></a></b>` will be permitted.
60 ///
61 /// Note that the emitted [`End`] event will not be modified if this is enabled,
62 /// ie. it will contain the data of the unmatched end tag.
63 ///
64 /// Note, that setting this to `true` will lead to additional allocates that
65 /// needed to store tag name for an [`End`] event.
66 ///
67 /// Default: `false`
68 ///
69 /// [`Error::IllFormed(UnmatchedEndTag)`]: crate::errors::IllFormedError::UnmatchedEndTag
70 /// [`End`]: crate::events::Event::End
71 pub allow_unmatched_ends: bool,
72
73 /// Whether comments should be validated. If enabled, in case of invalid comment
74 /// [`Error::IllFormed(DoubleHyphenInComment)`] is returned from read methods.
75 ///
76 /// When set to `true`, every [`Comment`] event will be checked for not
77 /// containing `--`, which [is not allowed] in XML comments. Most of the time
78 /// we don't want comments at all so we don't really care about comment
79 /// correctness, thus the default value is `false` to improve performance.
80 ///
81 /// Default: `false`
82 ///
83 /// [`Error::IllFormed(DoubleHyphenInComment)`]: crate::errors::IllFormedError::DoubleHyphenInComment
84 /// [`Comment`]: crate::events::Event::Comment
85 /// [is not allowed]: https://www.w3.org/TR/xml11/#sec-comments
86 pub check_comments: bool,
87
88 /// Whether mismatched closing tag names should be detected. If enabled, in
89 /// case of mismatch the [`Error::IllFormed(MismatchedEndTag)`] is returned from
90 /// read methods.
91 ///
92 /// Note, that start and end tags [should match literally][spec], they cannot
93 /// have different prefixes even if both prefixes resolve to the same namespace.
94 /// The XML
95 ///
96 /// ```xml
97 /// <outer xmlns="namespace" xmlns:p="namespace">
98 /// </p:outer>
99 /// ```
100 ///
101 /// is not valid, even though semantically the start tag is the same as the
102 /// end tag. The reason is that namespaces are an extension of the original
103 /// XML specification (without namespaces) and it should be backward-compatible.
104 ///
105 /// When set to `false`, it won't check if a closing tag matches the corresponding
106 /// opening tag. For example, `<mytag></different_tag>` will be permitted.
107 ///
108 /// If the XML is known to be sane (already processed, etc.) this saves extra time.
109 ///
110 /// Note that the emitted [`End`] event will not be modified if this is disabled,
111 /// ie. it will contain the data of the mismatched end tag.
112 ///
113 /// Note, that setting this to `true` will lead to additional allocates that
114 /// needed to store tag name for an [`End`] event. However if [`expand_empty_elements`]
115 /// is also set, only one additional allocation will be performed that support
116 /// both these options.
117 ///
118 /// Default: `true`
119 ///
120 /// [`Error::IllFormed(MismatchedEndTag)`]: crate::errors::IllFormedError::MismatchedEndTag
121 /// [spec]: https://www.w3.org/TR/xml11/#dt-etag
122 /// [`End`]: crate::events::Event::End
123 /// [`expand_empty_elements`]: Self::expand_empty_elements
124 pub check_end_names: bool,
125
126 /// Whether empty elements should be split into an `Open` and a `Close` event.
127 ///
128 /// When set to `true`, all [`Empty`] events produced by a self-closing tag
129 /// like `<tag/>` are expanded into a [`Start`] event followed by an [`End`]
130 /// event. When set to `false` (the default), those tags are represented by
131 /// an [`Empty`] event instead.
132 ///
133 /// Note, that setting this to `true` will lead to additional allocates that
134 /// needed to store tag name for an [`End`] event. However if [`check_end_names`]
135 /// is also set, only one additional allocation will be performed that support
136 /// both these options.
137 ///
138 /// Default: `false`
139 ///
140 /// [`Empty`]: crate::events::Event::Empty
141 /// [`Start`]: crate::events::Event::Start
142 /// [`End`]: crate::events::Event::End
143 /// [`check_end_names`]: Self::check_end_names
144 pub expand_empty_elements: bool,
145
146 /// Whether trailing whitespace after the markup name are trimmed in closing
147 /// tags `</a >`.
148 ///
149 /// If `true` the emitted [`End`] event is stripped of trailing whitespace
150 /// after the markup name.
151 ///
152 /// Note that if set to `false` and [`check_end_names`] is `true` the comparison
153 /// of markup names is going to fail erroneously if a closing tag contains
154 /// trailing whitespace.
155 ///
156 /// Default: `true`
157 ///
158 /// [`End`]: crate::events::Event::End
159 /// [`check_end_names`]: Self::check_end_names
160 pub trim_markup_names_in_closing_tags: bool,
161
162 /// Whether leading whitespace before character data should be removed.
163 ///
164 /// When set to `true`, whitespace at the start of [`Text`] events is
165 /// stripped. If the event becomes empty after trimming, it is still
166 /// emitted as `Text("")`.
167 ///
168 /// Default: `false`
169 ///
170 /// <div style="background:rgba(255, 80, 80, 0.20);padding:0.75em;">
171 ///
172 /// **WARNING:** This option has known issues.
173 ///
174 /// - **Incorrect trimming around comments, PIs, and CDATA sections:**
175 /// Trimming applies to every [`Text`] event regardless of context.
176 /// In `text <!-- comment --> more`, the leading space of ` more` is
177 /// content, but this option trims it because the parser treats each
178 /// text chunk independently without knowledge of the surrounding markup.
179 /// - **Empty events:** Whitespace-only text that is fully trimmed
180 /// produces an empty `Text("")` event instead of being suppressed
181 /// ([#984]).
182 ///
183 /// To correctly trim data manually apply [`BytesText::inplace_trim_start`]
184 /// and [`BytesText::inplace_trim_end`] only to necessary events.
185 /// </div>
186 ///
187 /// [`Text`]: crate::events::Event::Text
188 /// [`BytesText::inplace_trim_start`]: crate::events::BytesText::inplace_trim_start
189 /// [`BytesText::inplace_trim_end`]: crate::events::BytesText::inplace_trim_end
190 /// [#984]: https://github.com/tafia/quick-xml/issues/984
191 pub trim_text_start: bool,
192
193 /// Whether trailing whitespace after character data should be removed.
194 ///
195 /// When set to `true`, trailing whitespace in [`Text`] events is
196 /// stripped. If the event becomes empty after trimming, it is still
197 /// emitted as `Text("")`.
198 ///
199 /// Default: `false`
200 ///
201 /// <div style="background:rgba(255, 80, 80, 0.20);padding:0.75em;">
202 ///
203 /// **WARNING:** This option has known issues.
204 ///
205 /// - **Incorrect trimming around comments, PIs, and CDATA sections:**
206 /// Trimming applies to every [`Text`] event regardless of context.
207 /// In `text <!-- comment --> more`, the trailing space of `text ` is
208 /// content, but this option trims it because the parser treats each
209 /// text chunk independently without knowledge of the surrounding markup.
210 /// - **Empty events:** Whitespace-only text that is fully trimmed
211 /// produces an empty `Text("")` event instead of being suppressed
212 /// ([#984]).
213 ///
214 /// To correctly trim data manually apply [`BytesText::inplace_trim_start`]
215 /// and [`BytesText::inplace_trim_end`] only to necessary events.
216 /// </div>
217 ///
218 /// [`Text`]: crate::events::Event::Text
219 /// [`BytesText::inplace_trim_start`]: crate::events::BytesText::inplace_trim_start
220 /// [`BytesText::inplace_trim_end`]: crate::events::BytesText::inplace_trim_end
221 /// [#984]: https://github.com/tafia/quick-xml/issues/984
222 pub trim_text_end: bool,
223}
224
225impl Config {
226 /// Set both [`trim_text_start`] and [`trim_text_end`] to the same value.
227 ///
228 /// See those options for more details, including known issues.
229 ///
230 /// Default: `false`
231 ///
232 /// <div style="background:rgba(255, 80, 80, 0.20);padding:0.75em;">
233 ///
234 /// **WARNING:** This option has known issues.
235 ///
236 /// With this option every text event will be trimmed which is incorrect
237 /// behavior when text events delimited by comments, processing instructions
238 /// or CDATA sections. To correctly trim data manually apply
239 /// [`BytesText::inplace_trim_start`] and [`BytesText::inplace_trim_end`]
240 /// only to necessary events.
241 /// </div>
242 ///
243 /// [`trim_text_start`]: Self::trim_text_start
244 /// [`trim_text_end`]: Self::trim_text_end
245 /// [`BytesText::inplace_trim_start`]: crate::events::BytesText::inplace_trim_start
246 /// [`BytesText::inplace_trim_end`]: crate::events::BytesText::inplace_trim_end
247 /// [#984]: https://github.com/tafia/quick-xml/issues/984
248 #[inline]
249 pub fn trim_text(&mut self, trim: bool) {
250 self.trim_text_start = trim;
251 self.trim_text_end = trim;
252 }
253
254 /// Turn on or off all checks for well-formedness. Currently it is that settings:
255 /// - [`check_comments`](Self::check_comments)
256 /// - [`check_end_names`](Self::check_end_names)
257 #[inline]
258 pub fn enable_all_checks(&mut self, enable: bool) {
259 self.check_comments = enable;
260 self.check_end_names = enable;
261 }
262}
263
264impl Default for Config {
265 fn default() -> Self {
266 Self {
267 allow_dangling_amp: false,
268 allow_unmatched_ends: false,
269 check_comments: false,
270 check_end_names: true,
271 expand_empty_elements: false,
272 trim_markup_names_in_closing_tags: true,
273 trim_text_start: false,
274 trim_text_end: false,
275 }
276 }
277}
278
279////////////////////////////////////////////////////////////////////////////////////////////////////
280
281macro_rules! read_event_impl {
282 (
283 $self:ident, $buf:ident,
284 $reader:expr,
285 $read_until_close:ident
286 $(, $await:ident)?
287 ) => {{
288 let event = loop {
289 break match $self.state.state {
290 ParseState::Init => { // Go to InsideText state
291 // If encoding set explicitly, we not need to detect it. For example,
292 // explicit UTF-8 set automatically if Reader was created using `from_str`.
293 // But we still need to remove BOM for consistency with no encoding
294 // feature enabled path
295 #[cfg(feature = "encoding")]
296 if let Some(encoding) = $reader.detect_encoding() $(.$await)? ? {
297 if $self.state.encoding.can_be_refined() {
298 $self.state.encoding = crate::reader::EncodingRef::BomDetected(encoding.encoding());
299 }
300 }
301
302 // Removes UTF-8 BOM if it is present
303 #[cfg(not(feature = "encoding"))]
304 $reader.remove_utf8_bom() $(.$await)? ?;
305
306 $self.state.state = ParseState::InsideText;
307 continue;
308 },
309 ParseState::InsideRef => { // Go to InsideText
310 let start = $self.state.offset;
311 match $reader.read_ref($buf, &mut $self.state.offset) $(.$await)? {
312 // Emit reference, go to InsideText state
313 ReadRefResult::Ref(bytes) => {
314 $self.state.state = ParseState::InsideText;
315 // +1 to skip start `&`
316 // -1 to skip end `;`
317 Ok(Event::GeneralRef(BytesRef::wrap(&bytes[1..bytes.len() - 1])))
318 }
319 // Go to Done state
320 ReadRefResult::UpToEof(bytes) if $self.state.config.allow_dangling_amp => {
321 $self.state.state = ParseState::Done;
322 Ok(Event::Text($self.state.emit_text(bytes)?))
323 }
324 ReadRefResult::UpToEof(_) => {
325 $self.state.state = ParseState::Done;
326 $self.state.last_error_offset = start;
327 Err(Error::IllFormed(IllFormedError::UnclosedReference))
328 }
329 // Do not change state, stay in InsideRef
330 ReadRefResult::UpToRef(bytes) if $self.state.config.allow_dangling_amp => {
331 Ok(Event::Text($self.state.emit_text(bytes)?))
332 }
333 ReadRefResult::UpToRef(_) => {
334 $self.state.last_error_offset = start;
335 Err(Error::IllFormed(IllFormedError::UnclosedReference))
336 }
337 // Go to InsideMarkup state
338 ReadRefResult::UpToMarkup(bytes) if $self.state.config.allow_dangling_amp => {
339 $self.state.state = ParseState::InsideMarkup;
340 Ok(Event::Text($self.state.emit_text(bytes)?))
341 }
342 ReadRefResult::UpToMarkup(_) => {
343 $self.state.state = ParseState::InsideMarkup;
344 $self.state.last_error_offset = start;
345 Err(Error::IllFormed(IllFormedError::UnclosedReference))
346 }
347 ReadRefResult::Err(e) => Err(e),
348 }
349 }
350 ParseState::InsideText => { // Go to InsideMarkup or Done state
351 if $self.state.config.trim_text_start {
352 $reader.skip_whitespace(&mut $self.state.offset) $(.$await)? ?;
353 }
354
355 match $reader.read_text($buf, &mut $self.state.offset) $(.$await)? {
356 ReadTextResult::Markup(buf) => {
357 $self.state.state = ParseState::InsideMarkup;
358 // Pass `buf` to the next next iteration of parsing loop
359 $buf = buf;
360 continue;
361 }
362 ReadTextResult::Ref(buf) => {
363 $self.state.state = ParseState::InsideRef;
364 // Pass `buf` to the next next iteration of parsing loop
365 $buf = buf;
366 continue;
367 }
368 ReadTextResult::UpToMarkup(bytes) => {
369 $self.state.state = ParseState::InsideMarkup;
370 // FIXME: Can produce an empty event if:
371 // - event contains only spaces
372 // - trim_text_start = false
373 // - trim_text_end = true
374 Ok(Event::Text($self.state.emit_text(bytes)?))
375 }
376 ReadTextResult::UpToRef(bytes) => {
377 $self.state.state = ParseState::InsideRef;
378 // Return Text event with `bytes` content or Eof if bytes is empty
379 Ok(Event::Text($self.state.emit_text(bytes)?))
380 }
381 ReadTextResult::UpToEof(bytes) => {
382 $self.state.state = ParseState::Done;
383 // Trim bytes from end if required
384 let event = $self.state.emit_text(bytes)?;
385 if event.is_empty() {
386 Ok(Event::Eof)
387 } else {
388 Ok(Event::Text(event))
389 }
390 }
391 ReadTextResult::Err(e) => Err(e),
392 }
393 },
394 // Go to InsideText state in next two arms
395 ParseState::InsideMarkup => $self.$read_until_close($buf) $(.$await)?,
396 ParseState::InsideEmpty => Ok(Event::End($self.state.close_expanded_empty())),
397 ParseState::Done => Ok(Event::Eof),
398 };
399 };
400 match event {
401 // #513: In case of ill-formed errors we already consume the wrong data
402 // and change the state. We can continue parsing if we wish
403 Err(Error::IllFormed(_)) => {}
404 Err(_) | Ok(Event::Eof) => $self.state.state = ParseState::Done,
405 _ => {}
406 }
407 event
408 }};
409}
410
411/// Read bytes up to the `>` and skip it. This method is expected to be called
412/// after seeing the `<` symbol and skipping it. Inspects the next (current)
413/// symbol and returns an appropriate [`Event`]:
414///
415/// |Symbol |Event
416/// |-------|-------------------------------------
417/// |`!` |[`Comment`], [`CData`] or [`DocType`]
418/// |`/` |[`End`]
419/// |`?` |[`PI`]
420/// |_other_|[`Start`] or [`Empty`]
421///
422/// Moves parser to the `InsideText` state.
423///
424/// [`Comment`]: Event::Comment
425/// [`CData`]: Event::CData
426/// [`DocType`]: Event::DocType
427/// [`End`]: Event::End
428/// [`PI`]: Event::PI
429/// [`Start`]: Event::Start
430/// [`Empty`]: Event::Empty
431macro_rules! read_until_close {
432 (
433 $self:ident, $buf:ident,
434 $reader:expr
435 $(, $await:ident)?
436 ) => {{
437 $self.state.state = ParseState::InsideText;
438
439 let start = $self.state.offset;
440 match $reader.peek_one() $(.$await)? {
441 // `<!` - comment, CDATA or DOCTYPE declaration
442 Ok(Some(b'!')) => match $reader
443 .read_bang_element($buf, &mut $self.state.offset)
444 $(.$await)?
445 {
446 Ok((bang_type, bytes)) => $self.state.emit_bang(bang_type, bytes),
447 Err(e) => {
448 // We want to report error at `<`
449 $self.state.last_error_offset = start;
450 Err(e)
451 }
452 },
453 // `</` - closing tag
454 // #776: We parse using ElementParser which allows us to have attributes
455 // in close tags. While such tags are not allowed by the specification,
456 // we anyway allow to parse them because:
457 // - we do not check constraints during parsing. This is performed by the
458 // optional validate step which user should call manually
459 // - if we just look for `>` we will parse `</tag attr=">" >` as end tag
460 // `</tag attr=">` and text `" >` which probably no one existing parser
461 // does. This is malformed XML, however it is tolerated by some parsers
462 // (e.g. the one used by Adobe Flash) and such documents do exist in the wild.
463 Ok(Some(b'/')) => match $reader
464 .read_with(ElementParser::Outside, $buf, &mut $self.state.offset)
465 $(.$await)?
466 {
467 Ok(bytes) => $self.state.emit_end(bytes),
468 Err(e) => {
469 // We want to report error at `<`
470 $self.state.last_error_offset = start;
471 Err(e)
472 }
473 },
474 // `<?` - processing instruction
475 Ok(Some(b'?')) => match $reader
476 .read_with(PiParser(false), $buf, &mut $self.state.offset)
477 $(.$await)?
478 {
479 Ok(bytes) => $self.state.emit_question_mark(bytes),
480 Err(e) => {
481 // We want to report error at `<`
482 $self.state.last_error_offset = start;
483 Err(e)
484 }
485 },
486 // `<...` - opening or self-closed tag
487 Ok(Some(_)) => match $reader
488 .read_with(ElementParser::Outside, $buf, &mut $self.state.offset)
489 $(.$await)?
490 {
491 Ok(bytes) => $self.state.emit_start(bytes),
492 Err(e) => {
493 // We want to report error at `<`
494 $self.state.last_error_offset = start;
495 Err(e)
496 }
497 },
498 // `<` - syntax error, tag not closed
499 Ok(None) => {
500 // We want to report error at `<`
501 $self.state.last_error_offset = start;
502 Err(Error::Syntax(SyntaxError::UnclosedTag))
503 }
504 Err(e) => Err(Error::from(e)),
505 }
506 }};
507}
508
509/// Generalization of `read_to_end` method for buffered and borrowed readers
510macro_rules! read_to_end {
511 (
512 // $self: &mut Reader
513 $self:expr, $end:expr, $buf:expr,
514 $read_event:ident,
515 // Code block that performs clearing of internal buffer after read of each event
516 $clear:block
517 $(, $await:ident)?
518 ) => {{
519 // Because we take position after the event before the End event,
520 // it is important that this position indicates beginning of the End event.
521 // If between last event and the End event would be only spaces, then we
522 // take position before the spaces, but spaces would be skipped without
523 // generating event if `trim_text_start` is set to `true`. To prevent that
524 // we temporary disable start text trimming.
525 //
526 // We also cannot take position after getting End event, because if
527 // `trim_markup_names_in_closing_tags` is set to `true` (which is the default),
528 // we do not known the real size of the End event that it is occupies in
529 // the source and cannot correct the position after the End event.
530 // So, we in any case should tweak parser configuration.
531 let config = $self.config_mut();
532 let trim = config.trim_text_start;
533 config.trim_text_start = false;
534
535 let start = $self.buffer_position();
536 let mut depth = 0;
537 loop {
538 $clear
539 let end = $self.buffer_position();
540 match $self.$read_event($buf) $(.$await)? {
541 Err(e) => {
542 $self.config_mut().trim_text_start = trim;
543 return Err(e);
544 }
545
546 Ok(Event::Start(e)) if e.name() == $end => depth += 1,
547 Ok(Event::End(e)) if e.name() == $end => {
548 if depth == 0 {
549 $self.config_mut().trim_text_start = trim;
550 break start..end;
551 }
552 depth -= 1;
553 }
554 Ok(Event::Eof) => {
555 $self.config_mut().trim_text_start = trim;
556 return Err(Error::missed_end($end));
557 }
558 _ => (),
559 }
560 }
561 }};
562}
563
564#[cfg(feature = "async-tokio")]
565mod async_tokio;
566mod buffered_reader;
567mod ns_reader;
568mod slice_reader;
569mod state;
570
571pub use ns_reader::NsReader;
572
573/// Range of input in bytes, that corresponds to some piece of XML
574pub type Span = Range<u64>;
575
576////////////////////////////////////////////////////////////////////////////////////////////////////
577
578/// Possible reader states. The state transition diagram (`true` and `false` shows
579/// value of [`Config::expand_empty_elements`] option):
580///
581/// ```mermaid
582/// flowchart LR
583/// subgraph _
584/// direction LR
585///
586/// Init -- "(no event)"\n --> InsideMarkup
587/// InsideMarkup -- Decl, DocType, PI\nComment, CData\nStart, Empty, End --> InsideText
588/// InsideText -- "#lt;false#gt;\n(no event)"\nText --> InsideMarkup
589/// InsideRef -- "(no event)"\nGeneralRef --> InsideText
590/// end
591/// InsideText -- "#lt;true#gt;"\nStart --> InsideEmpty
592/// InsideEmpty -- End --> InsideText
593/// _ -. Eof .-> Done
594/// ```
595#[derive(Clone, Debug)]
596enum ParseState {
597 /// Initial state in which reader stay after creation. Transition from that
598 /// state could produce a `Text`, `Decl`, `Comment` or `Start` event. The next
599 /// state is always `InsideMarkup`. The reader will never return to this state. The
600 /// event emitted during transition to `InsideMarkup` is a `StartEvent` if the
601 /// first symbol not `<`, otherwise no event are emitted.
602 Init,
603 /// State after seeing the `&` symbol in textual content. Depending on the next symbol all other
604 /// events could be generated.
605 ///
606 /// After generating one event the reader moves to the `ClosedTag` state.
607 InsideRef,
608 /// State after seeing the `<` symbol. Depending on the next symbol all other
609 /// events could be generated.
610 ///
611 /// After generating one event the reader moves to the `InsideText` state.
612 InsideMarkup,
613 /// State in which reader searches the `<` symbol of a markup. All bytes before
614 /// that symbol will be returned in the [`Event::Text`] event. After that
615 /// the reader moves to the `InsideMarkup` state.
616 InsideText,
617 /// This state is used only if option [`expand_empty_elements`] is set to `true`.
618 /// Reader enters to this state when it is in a `InsideText` state and emits an
619 /// [`Event::Start`] event. The next event emitted will be an [`Event::End`],
620 /// after which reader returned to the `InsideText` state.
621 ///
622 /// [`expand_empty_elements`]: Config::expand_empty_elements
623 InsideEmpty,
624 /// Reader enters this state when `Eof` event generated or an error occurred.
625 /// This is the last state, the reader stay in it forever.
626 Done,
627}
628
629/// A reference to an encoding together with information about how it was retrieved.
630///
631/// The state transition diagram:
632///
633/// ```mermaid
634/// flowchart LR
635/// Implicit -- from_str --> Explicit
636/// Implicit -- BOM --> BomDetected
637/// Implicit -- "encoding=..." --> XmlDetected
638/// BomDetected -- "encoding=..." --> XmlDetected
639/// ```
640#[cfg(feature = "encoding")]
641#[derive(Clone, Copy, Debug)]
642enum EncodingRef {
643 /// Encoding was implicitly assumed to have a specified value. It can be refined
644 /// using BOM or by the XML declaration event (`<?xml encoding=... ?>`)
645 Implicit(&'static encoding_rs::Encoding),
646 /// Encoding was explicitly set to the desired value. It cannot be changed
647 /// nor by BOM, nor by parsing XML declaration (`<?xml encoding=... ?>`)
648 Explicit(&'static encoding_rs::Encoding),
649 /// Encoding was detected from a byte order mark (BOM) or by the first bytes
650 /// of the content. It can be refined by the XML declaration event (`<?xml encoding=... ?>`)
651 BomDetected(&'static encoding_rs::Encoding),
652 /// Encoding was detected using XML declaration event (`<?xml encoding=... ?>`).
653 /// It can no longer change
654 XmlDetected(&'static encoding_rs::Encoding),
655}
656#[cfg(feature = "encoding")]
657impl EncodingRef {
658 #[inline]
659 const fn encoding(&self) -> &'static encoding_rs::Encoding {
660 match self {
661 Self::Implicit(e) => e,
662 Self::Explicit(e) => e,
663 Self::BomDetected(e) => e,
664 Self::XmlDetected(e) => e,
665 }
666 }
667 #[inline]
668 const fn can_be_refined(&self) -> bool {
669 match self {
670 Self::Implicit(_) | Self::BomDetected(_) => true,
671 Self::Explicit(_) | Self::XmlDetected(_) => false,
672 }
673 }
674}
675
676////////////////////////////////////////////////////////////////////////////////////////////////////
677
678/// A direct stream to the underlying [`Reader`]s reader which updates
679/// [`Reader::buffer_position()`] when read from it.
680#[derive(Debug)]
681#[must_use = "streams do nothing unless read or polled"]
682pub struct BinaryStream<'r, R> {
683 inner: &'r mut R,
684 offset: &'r mut u64,
685}
686
687impl<'r, R> BinaryStream<'r, R> {
688 /// Returns current position in bytes in the original source.
689 #[inline]
690 pub const fn offset(&self) -> u64 {
691 *self.offset
692 }
693
694 /// Gets a reference to the underlying reader.
695 #[inline]
696 pub const fn get_ref(&self) -> &R {
697 self.inner
698 }
699
700 /// Gets a mutable reference to the underlying reader.
701 ///
702 /// Avoid read from this reader because this will not update reader's position
703 /// and will lead to incorrect positions of errors. Read from this stream instead.
704 #[inline]
705 pub fn get_mut(&mut self) -> &mut R {
706 self.inner
707 }
708}
709
710impl<'r, R> io::Read for BinaryStream<'r, R>
711where
712 R: io::Read,
713{
714 #[inline]
715 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
716 let amt = self.inner.read(buf)?;
717 *self.offset += amt as u64;
718 Ok(amt)
719 }
720}
721
722impl<'r, R> io::BufRead for BinaryStream<'r, R>
723where
724 R: io::BufRead,
725{
726 #[inline]
727 fn fill_buf(&mut self) -> io::Result<&[u8]> {
728 self.inner.fill_buf()
729 }
730
731 #[inline]
732 fn consume(&mut self, amt: usize) {
733 self.inner.consume(amt);
734 *self.offset += amt as u64;
735 }
736}
737
738////////////////////////////////////////////////////////////////////////////////////////////////////
739
740/// A low level XML event reader that expects UTF-8 input.
741///
742/// Consumes bytes and streams XML [`Event`]s.
743///
744/// This reader does not manage namespace declarations and not able to resolve
745/// prefixes. If you want these features, use the [`NsReader`].
746///
747/// If you need to decode a document which may not be UTF-8, enable the `encoding` feature
748/// and wrap the input in `DecodingReader`. This is a `BufRead` adapter that auto-detects
749/// encoding from BOM or XML declaration and transcodes to UTF-8.
750///
751/// # Examples
752///
753/// ```
754/// use quick_xml::events::Event;
755/// use quick_xml::reader::Reader;
756///
757/// let xml = r#"<tag1 att1 = "test">
758/// <tag2><!--Test comment-->Test</tag2>
759/// <tag2>Test 2</tag2>
760/// </tag1>"#;
761/// let mut reader = Reader::from_str(xml);
762/// reader.config_mut().trim_text(true);
763///
764/// let mut count = 0;
765/// let mut txt = Vec::new();
766/// let mut buf = Vec::new();
767///
768/// // The `Reader` does not implement `Iterator` because it outputs borrowed data (`Cow`s)
769/// loop {
770/// // NOTE: this is the generic case when we don't know about the input BufRead.
771/// // when the input is a &str or a &[u8], we don't actually need to use another
772/// // buffer, we could directly call `reader.read_event()`
773/// match reader.read_event_into(&mut buf) {
774/// Err(e) => panic!("Error at position {}: {:?}", reader.error_position(), e),
775/// // exits the loop when reaching end of file
776/// Ok(Event::Eof) => break,
777///
778/// Ok(Event::Start(e)) => {
779/// match e.name().as_ref() {
780/// "tag1" => println!("attributes values: {:?}",
781/// e.attributes().map(|a| a.unwrap().value)
782/// .collect::<Vec<_>>()),
783/// "tag2" => count += 1,
784/// _ => (),
785/// }
786/// }
787/// Ok(Event::Text(e)) => txt.push(e.into_inner().into_owned()),
788///
789/// // There are several other `Event`s we do not consider here
790/// _ => (),
791/// }
792/// // if we don't keep a borrow elsewhere, we can clear the buffer to keep memory usage low
793/// buf.clear();
794/// }
795/// ```
796///
797/// [`NsReader`]: crate::reader::NsReader
798#[derive(Debug, Clone)]
799pub struct Reader<R> {
800 /// Source of data for parse
801 reader: R,
802 /// Configuration and current parse state
803 state: ReaderState,
804}
805
806/// Builder methods
807impl<R> Reader<R> {
808 /// Creates a `Reader` that reads from a given reader.
809 pub fn from_reader(reader: R) -> Self {
810 Self {
811 reader,
812 state: ReaderState::default(),
813 }
814 }
815
816 /// Returns reference to the parser configuration
817 pub const fn config(&self) -> &Config {
818 &self.state.config
819 }
820
821 /// Returns mutable reference to the parser configuration
822 pub fn config_mut(&mut self) -> &mut Config {
823 &mut self.state.config
824 }
825}
826
827/// Getters
828impl<R> Reader<R> {
829 /// Consumes `Reader` returning the underlying reader
830 ///
831 /// Can be used to compute line and column of a parsing error position
832 ///
833 /// # Examples
834 ///
835 /// ```
836 /// # use pretty_assertions::assert_eq;
837 /// use std::{str, io::Cursor};
838 /// use quick_xml::events::Event;
839 /// use quick_xml::reader::Reader;
840 ///
841 /// let xml = r#"<tag1 att1 = "test">
842 /// <tag2><!--Test comment-->Test</tag2>
843 /// <tag3>Test 2</tag3>
844 /// </tag1>"#;
845 /// let mut reader = Reader::from_reader(Cursor::new(xml.as_bytes()));
846 /// let mut buf = Vec::new();
847 ///
848 /// fn into_line_and_column(reader: Reader<Cursor<&[u8]>>) -> (usize, usize) {
849 /// // We known that size cannot exceed usize::MAX because we created parser from single &[u8]
850 /// let end_pos = reader.buffer_position() as usize;
851 /// let mut cursor = reader.into_inner();
852 /// let s = String::from_utf8(cursor.into_inner()[0..end_pos].to_owned())
853 /// .expect("can't make a string");
854 /// let mut line = 1;
855 /// let mut column = 0;
856 /// for c in s.chars() {
857 /// if c == '\n' {
858 /// line += 1;
859 /// column = 0;
860 /// } else {
861 /// column += 1;
862 /// }
863 /// }
864 /// (line, column)
865 /// }
866 ///
867 /// loop {
868 /// match reader.read_event_into(&mut buf) {
869 /// Ok(Event::Start(ref e)) => match e.name().as_ref() {
870 /// "tag1" | "tag2" => (),
871 /// tag => {
872 /// assert_eq!("tag3", tag);
873 /// assert_eq!((3, 22), into_line_and_column(reader));
874 /// break;
875 /// }
876 /// },
877 /// Ok(Event::Eof) => unreachable!(),
878 /// _ => (),
879 /// }
880 /// buf.clear();
881 /// }
882 /// ```
883 pub fn into_inner(self) -> R {
884 self.reader
885 }
886
887 /// Gets a reference to the underlying reader.
888 pub const fn get_ref(&self) -> &R {
889 &self.reader
890 }
891
892 /// Gets a mutable reference to the underlying reader.
893 ///
894 /// Avoid read from this reader because this will not update reader's position
895 /// and will lead to incorrect positions of errors. If you want to read, use
896 /// [`stream()`] instead.
897 ///
898 /// [`stream()`]: Self::stream
899 pub fn get_mut(&mut self) -> &mut R {
900 &mut self.reader
901 }
902
903 /// Gets the byte position in the input data just after the last emitted event
904 /// (i.e. this is position where data of last event ends).
905 ///
906 /// Note, that for text events which is originally ended with whitespace characters
907 /// (` `, `\t`, `\r`, and `\n`) if [`Config::trim_text_end`] is set this is position
908 /// before trim, not the position of the last byte of the [`Event::Text`] content.
909 pub const fn buffer_position(&self) -> u64 {
910 self.state.offset
911 }
912
913 /// Gets the last error byte position in the input data. If there is no errors
914 /// yet, returns `0`.
915 ///
916 /// Unlike `buffer_position` it will point to the place where it is rational
917 /// to report error to the end user. For example, all [`SyntaxError`]s are
918 /// reported when the parser sees EOF inside of some kind of markup. The
919 /// `buffer_position()` will point to the last byte of input which is not
920 /// very useful. `error_position()` will point to the start of corresponding
921 /// markup element (i. e. to the `<` character).
922 ///
923 /// This position is always `<= buffer_position()`.
924 pub const fn error_position(&self) -> u64 {
925 self.state.last_error_offset
926 }
927
928 /// Returns the encoding used by this reader.
929 ///
930 /// The used encoding may change after parsing the XML declaration,
931 /// otherwise encoding is fixed to UTF-8.
932 ///
933 /// If no encoding is specified in the declaration, defaults to UTF-8.
934 #[cfg(feature = "encoding")]
935 #[inline]
936 pub const fn encoding(&self) -> &'static encoding_rs::Encoding {
937 self.state.encoding.encoding()
938 }
939
940 /// Get the direct access to the underlying reader, but tracks the amount of
941 /// read data and update [`Reader::buffer_position()`] accordingly.
942 ///
943 /// Note, that this method gives you access to the internal reader and read
944 /// data will not be returned in any subsequent events read by `read_event`
945 /// family of methods.
946 ///
947 /// # Example
948 ///
949 /// This example demonstrates how to read stream raw bytes from an XML document.
950 /// This could be used to implement streaming read of text, or to read raw binary
951 /// bytes embedded in an XML document. (Documents with embedded raw bytes are not
952 /// valid XML, but XML-derived file formats exist where such documents are valid).
953 ///
954 /// ```
955 /// # use pretty_assertions::assert_eq;
956 /// use std::io::{BufRead, Read};
957 /// use quick_xml::events::{BytesEnd, BytesStart, Event};
958 /// use quick_xml::reader::Reader;
959 ///
960 /// let mut reader = Reader::from_str("<tag>binary << data&></tag>");
961 /// // ^ ^ ^ ^
962 /// // 0 5 21 27
963 ///
964 /// assert_eq!(
965 /// (reader.read_event().unwrap(), reader.buffer_position()),
966 /// // 5 - end of the `<tag>`
967 /// (Event::Start(BytesStart::new("tag")), 5)
968 /// );
969 ///
970 /// // Reading directly from underlying reader will not update position
971 /// // let mut inner = reader.get_mut();
972 ///
973 /// // Reading from the stream() advances position
974 /// let mut inner = reader.stream();
975 ///
976 /// // Read binary data. We must know its size
977 /// let mut binary = [0u8; 16];
978 /// inner.read_exact(&mut binary).unwrap();
979 /// assert_eq!(&binary, b"binary << data&>");
980 /// // 21 - end of the `binary << data&>`
981 /// assert_eq!(inner.offset(), 21);
982 /// assert_eq!(reader.buffer_position(), 21);
983 ///
984 /// assert_eq!(
985 /// (reader.read_event().unwrap(), reader.buffer_position()),
986 /// // 27 - end of the `</tag>`
987 /// (Event::End(BytesEnd::new("tag")), 27)
988 /// );
989 ///
990 /// assert_eq!(reader.read_event().unwrap(), Event::Eof);
991 /// ```
992 #[inline]
993 pub fn stream(&mut self) -> BinaryStream<'_, R> {
994 BinaryStream {
995 inner: &mut self.reader,
996 offset: &mut self.state.offset,
997 }
998 }
999}
1000
1001/// Private sync reading methods
1002impl<R> Reader<R> {
1003 /// Read text into the given buffer, and return an event that borrows from
1004 /// either that buffer or from the input itself, based on the type of the
1005 /// reader.
1006 fn read_event_impl<'i, B>(&mut self, mut buf: B) -> Result<Event<'i>, Error>
1007 where
1008 R: XmlSource<'i, B>,
1009 {
1010 read_event_impl!(self, buf, self.reader, read_until_close)
1011 }
1012
1013 /// Private function to read until `>` is found. This function expects that
1014 /// it was called just after encounter a `<` symbol.
1015 fn read_until_close<'i, B>(&mut self, buf: B) -> Result<Event<'i>, Error>
1016 where
1017 R: XmlSource<'i, B>,
1018 {
1019 read_until_close!(self, buf, self.reader)
1020 }
1021}
1022
1023////////////////////////////////////////////////////////////////////////////////////////////////////
1024
1025/// Result of an attempt to read XML textual data from the source.
1026#[derive(Debug)]
1027enum ReadTextResult<'r, B> {
1028 /// The reader is positioned at `<` (start of markup). `<` was not consumed.
1029 /// Contains buffer that should be returned back to the next iteration cycle
1030 /// to satisfy borrow checker requirements.
1031 Markup(B),
1032 /// The reader is positioned at `&` (start of a reference). `&` was not consumed.
1033 /// Contains buffer that should be returned back to the next iteration cycle
1034 /// to satisfy borrow checker requirements.
1035 Ref(B),
1036 /// Contains text block up to start of markup (`<` character). `<` was not consumed.
1037 UpToMarkup(&'r str),
1038 /// Contains text block up to start of reference (`&` character).
1039 /// `&` was not consumed.
1040 UpToRef(&'r str),
1041 /// Contains text block up to EOF, neither start of markup (`<` character)
1042 /// or start of reference (`&` character) was found.
1043 UpToEof(&'r str),
1044 /// IO or decoding error occurred.
1045 Err(Error),
1046}
1047
1048/// Result of an attempt to read general reference from the reader.
1049#[derive(Debug)]
1050enum ReadRefResult<'r> {
1051 /// Contains text block up to end of reference (`;` character).
1052 /// Result includes start `&`, but not end `;`.
1053 Ref(&'r str),
1054 /// Contains text block up to EOF. Neither end of reference (`;`), start of
1055 /// another reference (`&`) or start of markup (`<`) characters was found.
1056 /// Result includes start `&`.
1057 UpToEof(&'r str),
1058 /// Contains text block up to next possible reference (`&` character).
1059 /// Result includes start `&`.
1060 UpToRef(&'r str),
1061 /// Contains text block up to start of markup (`<` character).
1062 /// Result includes start `&`.
1063 UpToMarkup(&'r str),
1064 /// IO or decoding error occurred.
1065 Err(Error),
1066}
1067
1068/// Represents an input for a reader that can return borrowed data.
1069///
1070/// There are two implementors of this trait: generic one that read data from
1071/// `Self`, copies some part of it into a provided buffer of type `B` and then
1072/// returns data that borrow from that buffer.
1073///
1074/// The other implementor is for `&[u8]` and instead of copying data returns
1075/// borrowed data from `Self` instead. This implementation allows zero-copy
1076/// deserialization.
1077///
1078/// # Parameters
1079/// - `'r`: lifetime of a buffer from which events will borrow
1080/// - `B`: a type of a buffer that can be used to store data read from `Self` and
1081/// from which events can borrow
1082trait XmlSource<'r, B> {
1083 /// Removes UTF-8 BOM if it is present
1084 #[cfg(not(feature = "encoding"))]
1085 fn remove_utf8_bom(&mut self) -> io::Result<()>;
1086
1087 /// Determines encoding from the start of input and removes BOM if it is present
1088 #[cfg(feature = "encoding")]
1089 fn detect_encoding(&mut self) -> io::Result<Option<DetectedEncoding>>;
1090
1091 /// Read input until start of markup (the `<`) is found, start of general entity
1092 /// reference (the `&`) is found or end of input is reached.
1093 ///
1094 /// # Parameters
1095 /// - `buf`: Buffer that could be filled from an input (`Self`) and
1096 /// from which [events] could borrow their data
1097 /// - `position`: Will be increased by amount of bytes consumed
1098 ///
1099 /// [events]: crate::events::Event
1100 fn read_text(&mut self, buf: B, position: &mut u64) -> ReadTextResult<'r, B>;
1101
1102 /// Read input until end of general reference (the `;`) is found, start of
1103 /// another general reference (the `&`) is found or end of input is reached.
1104 ///
1105 /// This method must be called when current character is `&`.
1106 ///
1107 /// # Parameters
1108 /// - `buf`: Buffer that could be filled from an input (`Self`) and
1109 /// from which [events] could borrow their data
1110 /// - `position`: Will be increased by amount of bytes consumed
1111 ///
1112 /// [events]: crate::events::Event
1113 fn read_ref(&mut self, buf: B, position: &mut u64) -> ReadRefResult<'r>;
1114
1115 /// Read input until processing instruction is finished.
1116 ///
1117 /// This method expect that start sequence of a parser already was read.
1118 ///
1119 /// Returns a slice of data read up to the end of the thing being parsed.
1120 /// The end of thing and the returned content is determined by the used parser.
1121 ///
1122 /// If input (`Self`) is exhausted and no bytes was read, or if the specified
1123 /// parser could not find the ending sequence of the thing, returns `SyntaxError`.
1124 ///
1125 /// # Parameters
1126 /// - `buf`: Buffer that could be filled from an input (`Self`) and
1127 /// from which [events] could borrow their data
1128 /// - `position`: Will be increased by amount of bytes consumed
1129 ///
1130 /// A `P` type parameter is used to preserve state between calls to the underlying
1131 /// reader which provides bytes fed into the parser.
1132 ///
1133 /// [events]: crate::events::Event
1134 fn read_with<P>(&mut self, parser: P, buf: B, position: &mut u64) -> Result<&'r str, Error>
1135 where
1136 P: Parser;
1137
1138 /// Read input until comment or CDATA is finished.
1139 ///
1140 /// This method expect that `<` already was read.
1141 ///
1142 /// Returns a slice of data read up to end of comment or CDATA (`>`),
1143 /// which does not include into result.
1144 ///
1145 /// If input (`Self`) is exhausted and nothing was read, returns `None`.
1146 ///
1147 /// # Parameters
1148 /// - `buf`: Buffer that could be filled from an input (`Self`) and
1149 /// from which [events] could borrow their data
1150 /// - `position`: Will be increased by amount of bytes consumed
1151 ///
1152 /// [events]: crate::events::Event
1153 fn read_bang_element(
1154 &mut self,
1155 buf: B,
1156 position: &mut u64,
1157 ) -> Result<(BangType, &'r str), Error>;
1158
1159 /// Consume and discard all the whitespace until the next non-whitespace
1160 /// character or EOF.
1161 ///
1162 /// # Parameters
1163 /// - `position`: Will be increased by amount of bytes consumed
1164 fn skip_whitespace(&mut self, position: &mut u64) -> io::Result<()>;
1165
1166 /// Return one character without consuming it, so that future `read_*` calls
1167 /// will still include it. On EOF, return `None`.
1168 fn peek_one(&mut self) -> io::Result<Option<u8>>;
1169}
1170
1171/// Possible elements started with `<!`
1172#[derive(Debug, PartialEq)]
1173enum BangType {
1174 /// <![CDATA[...]]>
1175 CData,
1176 /// <!--...-->
1177 Comment,
1178 /// <!DOCTYPE...>. Contains balance of '<' (+1) and '>' (-1)
1179 DocType(DtdParser),
1180}
1181impl BangType {
1182 #[inline(always)]
1183 const fn new(byte: Option<u8>) -> Result<Self, SyntaxError> {
1184 Ok(match byte {
1185 Some(b'[') => Self::CData,
1186 Some(b'-') => Self::Comment,
1187 Some(b'D') | Some(b'd') => Self::DocType(DtdParser::BeforeInternalSubset(0)),
1188 _ => return Err(SyntaxError::InvalidBangMarkup),
1189 })
1190 }
1191
1192 /// If element is finished, returns its content up to `>` symbol and
1193 /// an index of this symbol, otherwise returns `None`
1194 ///
1195 /// # Parameters
1196 /// - `buf`: buffer with data consumed on previous iterations
1197 /// - `chunk`: data read on current iteration and not yet consumed from reader
1198 #[inline(always)]
1199 fn feed(&mut self, buf: &[u8], chunk: &[u8]) -> Option<usize> {
1200 match self {
1201 Self::Comment => {
1202 for i in memchr::memchr_iter(b'>', chunk) {
1203 // Need to read at least 6 symbols (`!---->`) for properly finished comment
1204 // <!----> - XML comment
1205 // 0123456 - i
1206 if buf.len() + i > 5 {
1207 if chunk[..i].ends_with(b"--") {
1208 // We cannot strip last `--` from the buffer because we need it in case of
1209 // check_comments enabled option. XML standard requires that comment
1210 // will not end with `--->` sequence because this is a special case of
1211 // `--` in the comment (https://www.w3.org/TR/xml11/#sec-comments)
1212 return Some(i);
1213 }
1214 // End sequence `-|->` was splitted at |
1215 // buf --/ \-- chunk
1216 if i == 1 && buf.ends_with(b"-") && chunk[0] == b'-' {
1217 return Some(i);
1218 }
1219 // End sequence `--|>` was splitted at |
1220 // buf --/ \-- chunk
1221 if i == 0 && buf.ends_with(b"--") {
1222 return Some(i);
1223 }
1224 }
1225 }
1226 }
1227 Self::CData => {
1228 for i in memchr::memchr_iter(b'>', chunk) {
1229 if chunk[..i].ends_with(b"]]") {
1230 return Some(i);
1231 }
1232 // End sequence `]|]>` was splitted at |
1233 // buf --/ \-- chunk
1234 if i == 1 && buf.ends_with(b"]") && chunk[0] == b']' {
1235 return Some(i);
1236 }
1237 // End sequence `]]|>` was splitted at |
1238 // buf --/ \-- chunk
1239 if i == 0 && buf.ends_with(b"]]") {
1240 return Some(i);
1241 }
1242 }
1243 }
1244 Self::DocType(parser) => return parser.feed(buf, chunk),
1245 }
1246 None
1247 }
1248 #[inline]
1249 const fn to_err(&self) -> SyntaxError {
1250 match self {
1251 Self::CData => SyntaxError::UnclosedCData,
1252 Self::Comment => SyntaxError::UnclosedComment,
1253 Self::DocType(_) => SyntaxError::UnclosedDoctype,
1254 }
1255 }
1256}
1257
1258////////////////////////////////////////////////////////////////////////////////////////////////////
1259
1260#[cfg(test)]
1261mod test {
1262 /// Checks the internal implementation of the various reader methods
1263 macro_rules! check {
1264 (
1265 #[$test:meta]
1266 $read_event:ident,
1267 // constructor of the XML source on which internal functions will be called
1268 $source:path,
1269 $skip:literal,
1270 // constructor of the buffer to which read data will stored
1271 $buf:expr
1272 $(, $async:ident, $await:ident)?
1273 ) => {
1274 mod read_bang_element {
1275 use super::*;
1276 use crate::errors::{Error, SyntaxError};
1277 use crate::reader::{BangType, DtdParser};
1278
1279
1280 /// Checks that reading CDATA content works correctly
1281 mod cdata {
1282 use super::*;
1283 use pretty_assertions::assert_eq;
1284
1285 /// Checks that if input begins like CDATA element, but CDATA start sequence
1286 /// is not finished, parsing ends with an error
1287 #[$test]
1288 #[ignore = "start CDATA sequence fully checked outside of `read_bang_element`"]
1289 $($async)? fn not_properly_start() {
1290 let buf = $buf;
1291 let mut position = 0;
1292 let mut input = &b"<![]]>other content"[$skip..];
1293 // ^= 0
1294
1295 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1296 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedCData),
1297 x => panic!(
1298 "Expected `Err(Syntax(_))`, but got `{:?}`",
1299 x
1300 ),
1301 }
1302 assert_eq!(position, 1);
1303 }
1304
1305 /// Checks that if CDATA startup sequence was matched, but an end sequence
1306 /// is not found, parsing ends with an error
1307 #[$test]
1308 $($async)? fn not_closed() {
1309 let buf = $buf;
1310 let mut position = 0;
1311 let mut input = &b"<![CDATA[other content"[$skip..];
1312 // ^= 0 ^= 22
1313
1314 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1315 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedCData),
1316 x => panic!(
1317 "Expected `Err(Syntax(_))`, but got `{:?}`",
1318 x
1319 ),
1320 }
1321 assert_eq!(position, 22);
1322 }
1323
1324 /// Checks that CDATA element without content inside parsed successfully
1325 #[$test]
1326 $($async)? fn empty() {
1327 let buf = $buf;
1328 let mut position = 0;
1329 let mut input = &b"<![CDATA[]]>other content"[$skip..];
1330 // ^= 0 ^= 12
1331
1332 let (ty, bytes) = $source(&mut input)
1333 .read_bang_element(buf, &mut position)
1334 $(.$await)?
1335 .unwrap();
1336 assert_eq!(
1337 (ty, bytes),
1338 (BangType::CData, "<![CDATA[]]>")
1339 );
1340 assert_eq!(position, 12);
1341 }
1342
1343 /// Checks that CDATA element with content parsed successfully.
1344 /// Additionally checks that sequences inside CDATA that may look like
1345 /// a CDATA end sequence do not interrupt CDATA parsing
1346 #[$test]
1347 $($async)? fn with_content() {
1348 let buf = $buf;
1349 let mut position = 0;
1350 let mut input = &b"<![CDATA[cdata]] ]>content]]>other content]]>"[$skip..];
1351 // ^= 0 ^= 29
1352
1353 let (ty, bytes) = $source(&mut input)
1354 .read_bang_element(buf, &mut position)
1355 $(.$await)?
1356 .unwrap();
1357 assert_eq!(
1358 (ty, bytes),
1359 (BangType::CData, "<![CDATA[cdata]] ]>content]]>")
1360 );
1361 assert_eq!(position, 29);
1362 }
1363 }
1364
1365 /// Checks that reading XML comments works correctly. According to the [specification],
1366 /// comment data can contain any sequence except `--`:
1367 ///
1368 /// ```peg
1369 /// comment = '<--' (!'--' char)* '-->';
1370 /// char = [#x1-#x2C]
1371 /// / [#x2E-#xD7FF]
1372 /// / [#xE000-#xFFFD]
1373 /// / [#x10000-#x10FFFF]
1374 /// ```
1375 ///
1376 /// The presence of this limitation, however, is simply a poorly designed specification
1377 /// (maybe for purpose of building of LL(1) XML parser) and quick-xml does not check for
1378 /// presence of these sequences by default. This tests allow such content.
1379 ///
1380 /// [specification]: https://www.w3.org/TR/xml11/#dt-comment
1381 mod comment {
1382 use super::*;
1383 use pretty_assertions::assert_eq;
1384
1385 #[$test]
1386 #[ignore = "start comment sequence fully checked outside of `read_bang_element`"]
1387 $($async)? fn not_properly_start() {
1388 let buf = $buf;
1389 let mut position = 0;
1390 let mut input = &b"<!- -->other content"[$skip..];
1391 // ^= 1
1392
1393 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1394 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedComment),
1395 x => panic!(
1396 "Expected `Err(Syntax(_))`, but got `{:?}`",
1397 x
1398 ),
1399 }
1400 assert_eq!(position, 1);
1401 }
1402
1403 #[$test]
1404 $($async)? fn not_properly_end() {
1405 let buf = $buf;
1406 let mut position = 0;
1407 let mut input = &b"<!->other content"[$skip..];
1408 // ^= 0 ^= 17
1409
1410 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1411 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedComment),
1412 x => panic!(
1413 "Expected `Err(Syntax(_))`, but got `{:?}`",
1414 x
1415 ),
1416 }
1417 assert_eq!(position, 17);
1418 }
1419
1420 #[$test]
1421 $($async)? fn not_closed1() {
1422 let buf = $buf;
1423 let mut position = 0;
1424 let mut input = &b"<!--other content"[$skip..];
1425 // ^= 0 ^= 17
1426
1427 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1428 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedComment),
1429 x => panic!(
1430 "Expected `Err(Syntax(_))`, but got `{:?}`",
1431 x
1432 ),
1433 }
1434 assert_eq!(position, 17);
1435 }
1436
1437 #[$test]
1438 $($async)? fn not_closed2() {
1439 let buf = $buf;
1440 let mut position = 0;
1441 let mut input = &b"<!-->other content"[$skip..];
1442 // ^= 0 ^= 18
1443
1444 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1445 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedComment),
1446 x => panic!(
1447 "Expected `Err(Syntax(_))`, but got `{:?}`",
1448 x
1449 ),
1450 }
1451 assert_eq!(position, 18);
1452 }
1453
1454 #[$test]
1455 $($async)? fn not_closed3() {
1456 let buf = $buf;
1457 let mut position = 0;
1458 let mut input = &b"<!--->other content"[$skip..];
1459 // ^= 0 ^= 19
1460
1461 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1462 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedComment),
1463 x => panic!(
1464 "Expected `Err(Syntax(_))`, but got `{:?}`",
1465 x
1466 ),
1467 }
1468 assert_eq!(position, 19);
1469 }
1470
1471 #[$test]
1472 $($async)? fn empty() {
1473 let buf = $buf;
1474 let mut position = 0;
1475 let mut input = &b"<!---->other content"[$skip..];
1476 // ^= 0 ^= 7
1477
1478 let (ty, bytes) = $source(&mut input)
1479 .read_bang_element(buf, &mut position)
1480 $(.$await)?
1481 .unwrap();
1482 assert_eq!(
1483 (ty, bytes),
1484 (BangType::Comment, "<!---->")
1485 );
1486 assert_eq!(position, 7);
1487 }
1488
1489 #[$test]
1490 $($async)? fn with_content() {
1491 let buf = $buf;
1492 let mut position = 0;
1493 let mut input = &b"<!--->comment<--->other content"[$skip..];
1494 // ^= 0 ^= 18
1495
1496 let (ty, bytes) = $source(&mut input)
1497 .read_bang_element(buf, &mut position)
1498 $(.$await)?
1499 .unwrap();
1500 assert_eq!(
1501 (ty, bytes),
1502 (BangType::Comment, "<!--->comment<--->")
1503 );
1504 assert_eq!(position, 18);
1505 }
1506 }
1507
1508 /// Checks that reading DOCTYPE definition works correctly
1509 mod doctype {
1510 use super::*;
1511
1512 mod uppercase {
1513 use super::*;
1514 use pretty_assertions::assert_eq;
1515
1516 #[$test]
1517 $($async)? fn not_properly_start() {
1518 let buf = $buf;
1519 let mut position = 0;
1520 let mut input = &b"<!D other content"[$skip..];
1521 // ^= 0 ^= 17
1522
1523 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1524 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedDoctype),
1525 x => panic!(
1526 "Expected `Err(Syntax(_))`, but got `{:?}`",
1527 x
1528 ),
1529 }
1530 assert_eq!(position, 17);
1531 }
1532
1533 #[$test]
1534 $($async)? fn without_space() {
1535 let buf = $buf;
1536 let mut position = 0;
1537 let mut input = &b"<!DOCTYPEother content"[$skip..];
1538 // ^= 0 ^= 22
1539
1540 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1541 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedDoctype),
1542 x => panic!(
1543 "Expected `Err(Syntax(_))`, but got `{:?}`",
1544 x
1545 ),
1546 }
1547 assert_eq!(position, 22);
1548 }
1549
1550 #[$test]
1551 $($async)? fn empty() {
1552 let buf = $buf;
1553 let mut position = 0;
1554 let mut input = &b"<!DOCTYPE>other content"[$skip..];
1555 // ^= 0 ^= 10
1556
1557 let (ty, bytes) = $source(&mut input)
1558 .read_bang_element(buf, &mut position)
1559 $(.$await)?
1560 .unwrap();
1561 assert_eq!(
1562 (ty, bytes),
1563 (BangType::DocType(DtdParser::Finished), "<!DOCTYPE>")
1564 );
1565 assert_eq!(position, 10);
1566 }
1567
1568 #[$test]
1569 $($async)? fn not_closed() {
1570 let buf = $buf;
1571 let mut position = 0;
1572 let mut input = &b"<!DOCTYPE other content"[$skip..];
1573 // ^= 0 ^23
1574
1575 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1576 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedDoctype),
1577 x => panic!(
1578 "Expected `Err(Syntax(_))`, but got `{:?}`",
1579 x
1580 ),
1581 }
1582 assert_eq!(position, 23);
1583 }
1584 }
1585
1586 mod lowercase {
1587 use super::*;
1588 use pretty_assertions::assert_eq;
1589
1590 #[$test]
1591 $($async)? fn not_properly_start() {
1592 let buf = $buf;
1593 let mut position = 0;
1594 let mut input = &b"<!d other content"[$skip..];
1595 // ^= 0 ^= 17
1596
1597 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1598 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedDoctype),
1599 x => panic!(
1600 "Expected `Err(Syntax(_))`, but got `{:?}`",
1601 x
1602 ),
1603 }
1604 assert_eq!(position, 17);
1605 }
1606
1607 #[$test]
1608 $($async)? fn without_space() {
1609 let buf = $buf;
1610 let mut position = 0;
1611 let mut input = &b"<!doctypeother content"[$skip..];
1612 // ^= 0 ^= 22
1613
1614 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1615 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedDoctype),
1616 x => panic!(
1617 "Expected `Err(Syntax(_))`, but got `{:?}`",
1618 x
1619 ),
1620 }
1621 assert_eq!(position, 22);
1622 }
1623
1624 #[$test]
1625 $($async)? fn empty() {
1626 let buf = $buf;
1627 let mut position = 0;
1628 let mut input = &b"<!doctype>other content"[$skip..];
1629 // ^= 0 ^= 10
1630
1631 let (ty, bytes) = $source(&mut input)
1632 .read_bang_element(buf, &mut position)
1633 $(.$await)?
1634 .unwrap();
1635 assert_eq!(
1636 (ty, bytes),
1637 (BangType::DocType(DtdParser::Finished), "<!doctype>")
1638 );
1639 assert_eq!(position, 10);
1640 }
1641
1642 #[$test]
1643 $($async)? fn not_closed() {
1644 let buf = $buf;
1645 let mut position = 0;
1646 let mut input = &b"<!doctype other content"[$skip..];
1647 // ^= 0 ^= 23
1648
1649 match $source(&mut input).read_bang_element(buf, &mut position) $(.$await)? {
1650 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedDoctype),
1651 x => panic!(
1652 "Expected `Err(Syntax(_))`, but got `{:?}`",
1653 x
1654 ),
1655 }
1656 assert_eq!(position, 23);
1657 }
1658 }
1659 }
1660 }
1661
1662 mod read_text {
1663 use super::*;
1664 use crate::reader::ReadTextResult;
1665
1666 use pretty_assertions::assert_eq;
1667
1668 #[$test]
1669 $($async)? fn empty() {
1670 let buf = $buf;
1671 let mut position = 1;
1672 let mut input = b"".as_ref();
1673 // ^= 1
1674
1675 match $source(&mut input).read_text(buf, &mut position) $(.$await)? {
1676 ReadTextResult::UpToEof(bytes) => assert_eq!(bytes, ""),
1677 x => panic!("Expected `UpToEof(_)`, but got `{:?}`", x),
1678 }
1679 assert_eq!(position, 1);
1680 }
1681
1682 #[$test]
1683 $($async)? fn markup() {
1684 let buf = $buf;
1685 let mut position = 1;
1686 let mut input = b"<".as_ref();
1687 // ^= 1
1688
1689 match $source(&mut input).read_text(buf, &mut position) $(.$await)? {
1690 ReadTextResult::Markup(b) => assert_eq!(b, $buf),
1691 x => panic!("Expected `Markup(_)`, but got `{:?}`", x),
1692 }
1693 assert_eq!(position, 1);
1694 }
1695
1696 #[$test]
1697 $($async)? fn ref_() {
1698 let buf = $buf;
1699 let mut position = 1;
1700 let mut input = b"&".as_ref();
1701 // ^= 1
1702
1703 match $source(&mut input).read_text(buf, &mut position) $(.$await)? {
1704 ReadTextResult::Ref(b) => assert_eq!(b, $buf),
1705 x => panic!("Expected `Ref(_)`, but got `{:?}`", x),
1706 }
1707 assert_eq!(position, 1);
1708 }
1709
1710 #[$test]
1711 $($async)? fn up_to_markup() {
1712 let buf = $buf;
1713 let mut position = 1;
1714 let mut input = b"a<".as_ref();
1715 // ^= 2
1716
1717 match $source(&mut input).read_text(buf, &mut position) $(.$await)? {
1718 ReadTextResult::UpToMarkup(bytes) => assert_eq!(bytes, "a"),
1719 x => panic!("Expected `UpToMarkup(_)`, but got `{:?}`", x),
1720 }
1721 assert_eq!(position, 2);
1722 }
1723
1724 #[$test]
1725 $($async)? fn up_to_ref() {
1726 let buf = $buf;
1727 let mut position = 1;
1728 let mut input = b"a&".as_ref();
1729 // ^= 2
1730
1731 match $source(&mut input).read_text(buf, &mut position) $(.$await)? {
1732 ReadTextResult::UpToRef(bytes) => assert_eq!(bytes, "a"),
1733 x => panic!("Expected `UpToRef(_)`, but got `{:?}`", x),
1734 }
1735 assert_eq!(position, 2);
1736 }
1737
1738 #[$test]
1739 $($async)? fn up_to_eof() {
1740 let buf = $buf;
1741 let mut position = 1;
1742 let mut input = b"a".as_ref();
1743 // ^= 2
1744
1745 match $source(&mut input).read_text(buf, &mut position) $(.$await)? {
1746 ReadTextResult::UpToEof(bytes) => assert_eq!(bytes, "a"),
1747 x => panic!("Expected `UpToEof(_)`, but got `{:?}`", x),
1748 }
1749 assert_eq!(position, 2);
1750 }
1751 }
1752
1753 mod read_ref {
1754 use super::*;
1755 use crate::reader::ReadRefResult;
1756
1757 use pretty_assertions::assert_eq;
1758
1759 // Empty input is not allowed for `read_ref` so not tested.
1760 // Borrowed source triggers debug assertion,
1761 // buffered do nothing due to implementation details.
1762
1763 #[$test]
1764 $($async)? fn up_to_eof() {
1765 let buf = $buf;
1766 let mut position = 1;
1767 let mut input = b"&".as_ref();
1768 // ^= 2
1769
1770 match $source(&mut input).read_ref(buf, &mut position) $(.$await)? {
1771 ReadRefResult::UpToEof(bytes) => assert_eq!(bytes, "&"),
1772 x => panic!("Expected `UpToEof(_)`, but got `{:?}`", x),
1773 }
1774 assert_eq!(position, 2);
1775 }
1776
1777 #[$test]
1778 $($async)? fn up_to_ref() {
1779 let buf = $buf;
1780 let mut position = 1;
1781 let mut input = b"&&".as_ref();
1782 // ^= 2
1783
1784 match $source(&mut input).read_ref(buf, &mut position) $(.$await)? {
1785 ReadRefResult::UpToRef(bytes) => assert_eq!(bytes, "&"),
1786 x => panic!("Expected `UpToRef(_)`, but got `{:?}`", x),
1787 }
1788 assert_eq!(position, 2);
1789 }
1790
1791 #[$test]
1792 $($async)? fn up_to_markup() {
1793 let buf = $buf;
1794 let mut position = 1;
1795 let mut input = b"&<".as_ref();
1796 // ^= 2
1797
1798 match $source(&mut input).read_ref(buf, &mut position) $(.$await)? {
1799 ReadRefResult::UpToMarkup(bytes) => assert_eq!(bytes, "&"),
1800 x => panic!("Expected `UpToMarkup(_)`, but got `{:?}`", x),
1801 }
1802 assert_eq!(position, 2);
1803 }
1804
1805 #[$test]
1806 $($async)? fn empty_ref() {
1807 let buf = $buf;
1808 let mut position = 1;
1809 let mut input = b"&;".as_ref();
1810 // ^= 3
1811
1812 match $source(&mut input).read_ref(buf, &mut position) $(.$await)? {
1813 ReadRefResult::Ref(bytes) => assert_eq!(bytes, "&;"),
1814 x => panic!("Expected `Ref(_)`, but got `{:?}`", x),
1815 }
1816 assert_eq!(position, 3);
1817 }
1818
1819 #[$test]
1820 $($async)? fn normal() {
1821 let buf = $buf;
1822 let mut position = 1;
1823 let mut input = b"<".as_ref();
1824 // ^= 5
1825
1826 match $source(&mut input).read_ref(buf, &mut position) $(.$await)? {
1827 ReadRefResult::Ref(bytes) => assert_eq!(bytes, "<"),
1828 x => panic!("Expected `Ref(_)`, but got `{:?}`", x),
1829 }
1830 assert_eq!(position, 5);
1831 }
1832 }
1833
1834 mod read_element {
1835 use super::*;
1836 use crate::errors::{Error, SyntaxError};
1837 use crate::parser::ElementParser;
1838
1839 use pretty_assertions::assert_eq;
1840
1841 /// Checks that nothing was read from empty buffer
1842 /// `<` read in peek_one that is called before read_with, that is why it in the input buffer
1843 /// peek_one, however, does not increment position for simplicity of the code
1844 #[$test]
1845 $($async)? fn empty() {
1846 let buf = $buf;
1847 let mut position = 0;
1848 let mut input = &b"<"[$skip..];
1849 // ^= 1
1850
1851 match $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? {
1852 Err(Error::Syntax(cause)) => assert_eq!(cause, SyntaxError::UnclosedTag),
1853 x => panic!(
1854 "Expected `Err(Syntax(_))`, but got `{:?}`",
1855 x
1856 ),
1857 }
1858 assert_eq!(position, 1);
1859 }
1860
1861 mod open {
1862 use super::*;
1863 use pretty_assertions::assert_eq;
1864
1865 #[$test]
1866 $($async)? fn empty_tag() {
1867 let buf = $buf;
1868 let mut position = 0;
1869 let mut input = &b"<>"[$skip..];
1870 // ^= 2
1871
1872 assert_eq!(
1873 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
1874 "<>"
1875 );
1876 assert_eq!(position, 2);
1877 }
1878
1879 #[$test]
1880 $($async)? fn normal() {
1881 let buf = $buf;
1882 let mut position = 0;
1883 let mut input = &b"<tag>"[$skip..];
1884 // ^= 5
1885
1886 assert_eq!(
1887 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
1888 "<tag>"
1889 );
1890 assert_eq!(position, 5);
1891 }
1892
1893 #[$test]
1894 $($async)? fn empty_ns_empty_tag() {
1895 let buf = $buf;
1896 let mut position = 0;
1897 let mut input = &b"<:>"[$skip..];
1898 // ^= 3
1899
1900 assert_eq!(
1901 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
1902 "<:>"
1903 );
1904 assert_eq!(position, 3);
1905 }
1906
1907 #[$test]
1908 $($async)? fn empty_ns() {
1909 let buf = $buf;
1910 let mut position = 0;
1911 let mut input = &b"<:tag>"[$skip..];
1912 // ^= 6
1913
1914 assert_eq!(
1915 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
1916 "<:tag>"
1917 );
1918 assert_eq!(position, 6);
1919 }
1920
1921 #[$test]
1922 $($async)? fn with_attributes() {
1923 let buf = $buf;
1924 let mut position = 0;
1925 let mut input = &br#"<tag attr-1=">" attr2 = '>' 3attr>"#[$skip..];
1926 // ^= 39
1927
1928 assert_eq!(
1929 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
1930 r#"<tag attr-1=">" attr2 = '>' 3attr>"#
1931 );
1932 assert_eq!(position, 39);
1933 }
1934 }
1935
1936 mod self_closed {
1937 use super::*;
1938 use pretty_assertions::assert_eq;
1939
1940 #[$test]
1941 $($async)? fn empty_tag() {
1942 let buf = $buf;
1943 let mut position = 0;
1944 let mut input = &b"</>"[$skip..];
1945 // ^= 3
1946
1947 assert_eq!(
1948 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
1949 "</>"
1950 );
1951 assert_eq!(position, 3);
1952 }
1953
1954 #[$test]
1955 $($async)? fn normal() {
1956 let buf = $buf;
1957 let mut position = 0;
1958 let mut input = &b"<tag/>"[$skip..];
1959 // ^= 6
1960
1961 assert_eq!(
1962 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
1963 "<tag/>"
1964 );
1965 assert_eq!(position, 6);
1966 }
1967
1968 #[$test]
1969 $($async)? fn empty_ns_empty_tag() {
1970 let buf = $buf;
1971 let mut position = 0;
1972 let mut input = &b"<:/>"[$skip..];
1973 // ^= 4
1974
1975 assert_eq!(
1976 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
1977 "<:/>"
1978 );
1979 assert_eq!(position, 4);
1980 }
1981
1982 #[$test]
1983 $($async)? fn empty_ns() {
1984 let buf = $buf;
1985 let mut position = 0;
1986 let mut input = &b"<:tag/>"[$skip..];
1987 // ^= 7
1988
1989 assert_eq!(
1990 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
1991 "<:tag/>"
1992 );
1993 assert_eq!(position, 7);
1994 }
1995
1996 #[$test]
1997 $($async)? fn with_attributes() {
1998 let buf = $buf;
1999 let mut position = 0;
2000 let mut input = &br#"<tag attr-1="/>" attr2 = '/>' 3attr/>"#[$skip..];
2001 // ^= 42
2002
2003 assert_eq!(
2004 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
2005 r#"<tag attr-1="/>" attr2 = '/>' 3attr/>"#
2006 );
2007 assert_eq!(position, 42);
2008 }
2009 }
2010
2011 mod close {
2012 use super::*;
2013 use pretty_assertions::assert_eq;
2014
2015 #[$test]
2016 $($async)? fn empty_tag() {
2017 let buf = $buf;
2018 let mut position = 0;
2019 let mut input = &b"</ >"[$skip..];
2020 // ^= 4
2021
2022 assert_eq!(
2023 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
2024 "</ >"
2025 );
2026 assert_eq!(position, 4);
2027 }
2028
2029 #[$test]
2030 $($async)? fn normal() {
2031 let buf = $buf;
2032 let mut position = 0;
2033 let mut input = &b"</tag>"[$skip..];
2034 // ^= 6
2035
2036 assert_eq!(
2037 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
2038 "</tag>"
2039 );
2040 assert_eq!(position, 6);
2041 }
2042
2043 #[$test]
2044 $($async)? fn empty_ns_empty_tag() {
2045 let buf = $buf;
2046 let mut position = 0;
2047 let mut input = &b"</:>"[$skip..];
2048 // ^= 4
2049
2050 assert_eq!(
2051 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
2052 "</:>"
2053 );
2054 assert_eq!(position, 4);
2055 }
2056
2057 #[$test]
2058 $($async)? fn empty_ns() {
2059 let buf = $buf;
2060 let mut position = 0;
2061 let mut input = &b"</:tag>"[$skip..];
2062 // ^= 7
2063
2064 assert_eq!(
2065 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
2066 "</:tag>"
2067 );
2068 assert_eq!(position, 7);
2069 }
2070
2071 #[$test]
2072 $($async)? fn with_attributes() {
2073 let buf = $buf;
2074 let mut position = 0;
2075 let mut input = &br#"</tag attr-1=">" attr2 = '>' 3attr>"#[$skip..];
2076 // ^= 40
2077
2078 assert_eq!(
2079 $source(&mut input).read_with(ElementParser::default(), buf, &mut position) $(.$await)? .unwrap(),
2080 r#"</tag attr-1=">" attr2 = '>' 3attr>"#
2081 );
2082 assert_eq!(position, 40);
2083 }
2084 }
2085 }
2086
2087 /// Ensures, that no empty `Text` events are generated
2088 mod $read_event {
2089 use crate::events::{BytesCData, BytesDecl, BytesEnd, BytesPI, BytesStart, BytesText, Event};
2090 use crate::reader::Reader;
2091 use pretty_assertions::assert_eq;
2092
2093 /// When `encoding` feature is enabled, encoding should be detected
2094 /// from BOM (UTF-8) and BOM should be stripped.
2095 ///
2096 /// When `encoding` feature is disabled, UTF-8 is assumed and BOM
2097 /// character should be stripped for consistency
2098 #[$test]
2099 $($async)? fn bom_from_reader() {
2100 let mut reader = Reader::from_reader("\u{feff}\u{feff}".as_bytes());
2101
2102 assert_eq!(
2103 reader.$read_event($buf) $(.$await)? .unwrap(),
2104 Event::Text(BytesText::from_escaped("\u{feff}"))
2105 );
2106
2107 assert_eq!(
2108 reader.$read_event($buf) $(.$await)? .unwrap(),
2109 Event::Eof
2110 );
2111 }
2112
2113 /// When parsing from &str, encoding is fixed (UTF-8), so
2114 /// - when `encoding` feature is disabled, the behavior the
2115 /// same as in `bom_from_reader` text
2116 /// - when `encoding` feature is enabled, the behavior should
2117 /// stay consistent, so the first BOM character is stripped
2118 #[$test]
2119 $($async)? fn bom_from_str() {
2120 let mut reader = Reader::from_str("\u{feff}\u{feff}");
2121
2122 assert_eq!(
2123 reader.$read_event($buf) $(.$await)? .unwrap(),
2124 Event::Text(BytesText::from_escaped("\u{feff}"))
2125 );
2126
2127 assert_eq!(
2128 reader.$read_event($buf) $(.$await)? .unwrap(),
2129 Event::Eof
2130 );
2131 }
2132
2133 #[$test]
2134 $($async)? fn declaration() {
2135 let mut reader = Reader::from_str("<?xml ?>");
2136
2137 assert_eq!(
2138 reader.$read_event($buf) $(.$await)? .unwrap(),
2139 Event::Decl(BytesDecl::from_start(BytesStart::from_content("xml ", 3)))
2140 );
2141 }
2142
2143 #[$test]
2144 $($async)? fn doctype() {
2145 let mut reader = Reader::from_str("<!DOCTYPE x>");
2146
2147 assert_eq!(
2148 reader.$read_event($buf) $(.$await)? .unwrap(),
2149 Event::DocType(BytesText::from_escaped("x"))
2150 );
2151 }
2152
2153 #[$test]
2154 $($async)? fn processing_instruction() {
2155 let mut reader = Reader::from_str("<?xml-stylesheet '? >\" ?>");
2156
2157 assert_eq!(
2158 reader.$read_event($buf) $(.$await)? .unwrap(),
2159 Event::PI(BytesPI::new("xml-stylesheet '? >\" "))
2160 );
2161 }
2162
2163 /// Lone closing tags are not allowed, so testing it together with start tag
2164 #[$test]
2165 $($async)? fn start_and_end() {
2166 let mut reader = Reader::from_str("<tag></tag>");
2167
2168 assert_eq!(
2169 reader.$read_event($buf) $(.$await)? .unwrap(),
2170 Event::Start(BytesStart::new("tag"))
2171 );
2172
2173 assert_eq!(
2174 reader.$read_event($buf) $(.$await)? .unwrap(),
2175 Event::End(BytesEnd::new("tag"))
2176 );
2177 }
2178
2179 #[$test]
2180 $($async)? fn empty() {
2181 let mut reader = Reader::from_str("<tag/>");
2182
2183 assert_eq!(
2184 reader.$read_event($buf) $(.$await)? .unwrap(),
2185 Event::Empty(BytesStart::new("tag"))
2186 );
2187 }
2188
2189 #[$test]
2190 $($async)? fn text() {
2191 let mut reader = Reader::from_str("text");
2192
2193 assert_eq!(
2194 reader.$read_event($buf) $(.$await)? .unwrap(),
2195 Event::Text(BytesText::from_escaped("text"))
2196 );
2197 }
2198
2199 #[$test]
2200 $($async)? fn cdata() {
2201 let mut reader = Reader::from_str("<![CDATA[]]>");
2202
2203 assert_eq!(
2204 reader.$read_event($buf) $(.$await)? .unwrap(),
2205 Event::CData(BytesCData::new(""))
2206 );
2207 }
2208
2209 #[$test]
2210 $($async)? fn comment() {
2211 let mut reader = Reader::from_str("<!---->");
2212
2213 assert_eq!(
2214 reader.$read_event($buf) $(.$await)? .unwrap(),
2215 Event::Comment(BytesText::from_escaped(""))
2216 );
2217 }
2218
2219 #[$test]
2220 $($async)? fn eof() {
2221 let mut reader = Reader::from_str("");
2222
2223 assert_eq!(
2224 reader.$read_event($buf) $(.$await)? .unwrap(),
2225 Event::Eof
2226 );
2227 }
2228 }
2229 };
2230 }
2231
2232 // Export macros for the child modules:
2233 // - buffered_reader
2234 // - slice_reader
2235 pub(super) use check;
2236}