Coverage Report

Created: 2026-11-11 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/quick-xml-0.41.0/src/parser/element.rs
Line
Count
Source
1
//! Contains a parser for an XML element.
2
3
use crate::errors::SyntaxError;
4
use crate::parser::Parser;
5
6
/// A parser that search a `>` symbol in the slice outside of quoted regions.
7
///
8
/// The parser considers two quoted regions: a double-quoted (`"..."`) and
9
/// a single-quoted (`'...'`) region. Matches found inside those regions are not
10
/// considered as results. Each region starts and ends by its quote symbol,
11
/// which cannot be escaped (but can be encoded as XML character entity or named
12
/// entity. Anyway, that encoding does not contain literal quotes).
13
///
14
/// To use a parser create an instance of parser and [`feed`] data into it.
15
/// After successful search the parser will return [`Some`] with position of
16
/// found symbol. If search is unsuccessful, a [`None`] will be returned. You
17
/// typically would expect positive result of search, so that you should feed
18
/// new data until you get it.
19
///
20
/// NOTE: after successful match the parser does not returned to the initial
21
/// state and should not be used anymore. Create a new parser if you want to perform
22
/// new search.
23
///
24
/// # Example
25
///
26
/// ```
27
/// # use pretty_assertions::assert_eq;
28
/// use quick_xml::parser::{ElementParser, Parser};
29
///
30
/// let mut parser = ElementParser::default();
31
///
32
/// // Parse `<my-element  with = 'some > inside'>and the text follow...`
33
/// // splitted into three chunks
34
/// assert_eq!(parser.feed(b"<my-element"), None);
35
/// // ...get new chunk of data
36
/// assert_eq!(parser.feed(b" with = 'some >"), None);
37
/// // ...get another chunk of data
38
/// assert_eq!(parser.feed(b" inside'>and the text follow..."), Some(8));
39
/// //                       ^       ^
40
/// //                       0       8
41
/// ```
42
///
43
/// [`feed`]: Self::feed()
44
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
45
pub enum ElementParser {
46
    /// The initial state (inside element, but outside of attribute value).
47
    Outside,
48
    /// Inside a single-quoted region (`'...'`).
49
    SingleQ,
50
    /// Inside a double-quoted region (`"..."`).
51
    DoubleQ,
52
}
53
54
impl Parser for ElementParser {
55
    /// Returns number of consumed bytes or `None` if `>` was not found in `bytes`.
56
    #[inline]
57
123k
    fn feed(&mut self, bytes: &[u8]) -> Option<usize> {
58
123k
        for i in memchr::memchr3_iter(b'>', b'\'', b'"', bytes) {
59
123k
            *self = match (*self, bytes[i]) {
60
                // only allowed to match `>` while we are in state `Outside`
61
123k
                (Self::Outside, b'>') => return Some(i),
62
0
                (Self::Outside, b'\'') => Self::SingleQ,
63
0
                (Self::Outside, b'\"') => Self::DoubleQ,
64
65
                // the only end_byte that gets us out if the same character
66
0
                (Self::SingleQ, b'\'') | (Self::DoubleQ, b'"') => Self::Outside,
67
68
                // all other bytes: no state change
69
0
                _ => continue,
70
            };
71
        }
72
0
        None
73
123k
    }
74
75
    #[inline]
76
0
    fn eof_error(self, _content: &[u8]) -> SyntaxError {
77
0
        match self {
78
0
            Self::Outside => SyntaxError::UnclosedTag,
79
0
            Self::SingleQ => SyntaxError::UnclosedSingleQuotedAttributeValue,
80
0
            Self::DoubleQ => SyntaxError::UnclosedDoubleQuotedAttributeValue,
81
        }
82
0
    }
83
}
84
85
impl Default for ElementParser {
86
    #[inline]
87
0
    fn default() -> Self {
88
0
        Self::Outside
89
0
    }
90
}
91
92
#[test]
93
fn parse() {
94
    use pretty_assertions::assert_eq;
95
    use ElementParser::*;
96
97
    /// Returns `Ok(pos)` with the position in the buffer where element is ended.
98
    ///
99
    /// Returns `Err(internal_state)` if parsing does not done yet.
100
    fn parse_element(bytes: &[u8], mut parser: ElementParser) -> Result<usize, ElementParser> {
101
        match parser.feed(bytes) {
102
            Some(i) => Ok(i),
103
            None => Err(parser),
104
        }
105
    }
106
107
    assert_eq!(parse_element(b"", Outside), Err(Outside));
108
    assert_eq!(parse_element(b"", SingleQ), Err(SingleQ));
109
    assert_eq!(parse_element(b"", DoubleQ), Err(DoubleQ));
110
111
    assert_eq!(parse_element(b"'", Outside), Err(SingleQ));
112
    assert_eq!(parse_element(b"'", SingleQ), Err(Outside));
113
    assert_eq!(parse_element(b"'", DoubleQ), Err(DoubleQ));
114
115
    assert_eq!(parse_element(b"\"", Outside), Err(DoubleQ));
116
    assert_eq!(parse_element(b"\"", SingleQ), Err(SingleQ));
117
    assert_eq!(parse_element(b"\"", DoubleQ), Err(Outside));
118
119
    assert_eq!(parse_element(b">", Outside), Ok(0));
120
    assert_eq!(parse_element(b">", SingleQ), Err(SingleQ));
121
    assert_eq!(parse_element(b">", DoubleQ), Err(DoubleQ));
122
123
    assert_eq!(parse_element(b"''>", Outside), Ok(2));
124
    assert_eq!(parse_element(b"''>", SingleQ), Err(SingleQ));
125
    assert_eq!(parse_element(b"''>", DoubleQ), Err(DoubleQ));
126
}