Skip to main content

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"&lt;".as_ref();
1824                    //                    ^= 5
1825
1826                    match $source(&mut input).read_ref(buf, &mut position) $(.$await)? {
1827                        ReadRefResult::Ref(bytes) => assert_eq!(bytes, "&lt;"),
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}