Skip to main content

quick_xml/events/
attributes.rs

1//! Xml Attributes module
2//!
3//! Provides an iterator over attributes key/value pairs
4
5use crate::XmlVersion;
6use crate::encoding::Decoder;
7use crate::errors::Result as XmlResult;
8use crate::escape::{escape_attribute, resolve_predefined_entity};
9use crate::name::{LocalName, Namespace, NamespaceResolver, QName};
10use crate::utils::is_whitespace;
11
12use std::collections::HashSet;
13use std::fmt::{self, Debug, Display, Formatter};
14use std::hash::{BuildHasherDefault, DefaultHasher, Hasher};
15use std::iter::FusedIterator;
16use std::{borrow::Cow, ops::Range};
17
18/// A struct representing a key/value XML attribute.
19///
20/// Field `value` stores the raw attribute value, possibly containing escape-sequences.
21/// Most users will likely want to access the value using the [`normalized_value`] method.
22///
23/// # Lifetime
24///
25/// `'a` is a lifetime of the owning event from which this attribute is derived.
26///
27/// [`normalized_value`]: Self::normalized_value
28#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct Attribute<'a> {
30    /// The key to uniquely define the attribute.
31    ///
32    /// If [`Attributes::with_checks`] is turned off, the key might not be unique.
33    pub key: QName<'a>,
34    /// The raw value of the attribute.
35    pub value: Cow<'a, str>,
36}
37
38impl<'a> Attribute<'a> {
39    /// Returns the attribute value normalized as per [the XML specification] (or [for 1.0]).
40    ///
41    /// The characters `\t`, `\r`, `\n` are replaced with whitespace characters (`0x20`).
42    ///
43    /// The following escape sequences are replaced with their unescaped equivalents:
44    ///
45    /// | Escape Sequence | Replacement
46    /// |-----------------|------------
47    /// | `&lt;`          | `<`
48    /// | `&gt;`          | `>`
49    /// | `&amp;`         | `&`
50    /// | `&apos;`        | `'`
51    /// | `&quot;`        | `"`
52    ///
53    /// This will allocate unless the raw attribute value does not require normalization.
54    ///
55    /// Note, although you may use this library to parse HTML, you cannot use this
56    /// method to get HTML content, because its returns normalized value: the following
57    /// sequences are translated into a single space (U+0020) character:
58    ///
59    /// - `\r\n`
60    /// - `\r\x85` (only XML 1.1)
61    /// - `\r`
62    /// - `\n`
63    /// - `\t`
64    /// - `\x85` (only XML 1.1)
65    /// - `\x2028` (only XML 1.1)
66    ///
67    /// The text in HTML normally is not normalized in any way; normalization is
68    /// performed only in limited contexts and [only for] `\r\n` and `\r`.
69    ///
70    /// See also [`normalized_value_with()`](Self::normalized_value_with).
71    ///
72    /// [the XML specification]: https://www.w3.org/TR/xml11/#AVNormalize
73    /// [for 1.0]: https://www.w3.org/TR/xml/#AVNormalize
74    /// [only for]: https://html.spec.whatwg.org/#normalize-newlines
75    pub fn normalized_value(&self, version: XmlVersion) -> XmlResult<Cow<'a, str>> {
76        // resolve_predefined_entity returns only non-recursive replacements, so depth=1 is enough
77        self.normalized_value_with(version, 1, resolve_predefined_entity)
78    }
79
80    /// Returns the attribute value normalized as per [the XML specification] (or [for 1.0]),
81    /// using a custom entity resolver.
82    ///
83    /// Do not use this method with HTML attributes.
84    ///
85    /// The characters `\t`, `\r`, `\n` are replaced with whitespace characters (`0x20`).
86    ///
87    /// A function for resolving entities can be provided as `resolve_entity`.
88    /// This method does not resolve any predefined entities, but you can use
89    /// [`resolve_predefined_entity`] in your function.
90    ///
91    /// This will allocate unless the raw attribute value does not require normalization.
92    ///
93    /// Note, although you may use this library to parse HTML, you cannot use this
94    /// method to get HTML content, because its returns normalized value: the following
95    /// sequences are translated into a single space (U+0020) character:
96    ///
97    /// - `\r\n`
98    /// - `\r\x85` (only XML 1.1)
99    /// - `\r`
100    /// - `\n`
101    /// - `\t`
102    /// - `\x85` (only XML 1.1)
103    /// - `\x2028` (only XML 1.1)
104    ///
105    /// The text in HTML normally is not normalized in any way; normalization is
106    /// performed only in limited contexts and [only for] `\r\n` and `\r`.
107    ///
108    /// See also [`normalized_value()`](Self::normalized_value).
109    ///
110    /// # Parameters
111    ///
112    /// - `depth`: maximum number of nested entities that can be expanded. If expansion
113    ///   chain will be more that this value, the function will return [`EscapeError::TooManyNestedEntities`]
114    /// - `resolve_entity`: a function to resolve entity. This function could be called
115    ///   multiple times on the same input and can return different values in each case
116    ///   for the same input, although it is not recommended
117    ///
118    /// [the XML specification]: https://www.w3.org/TR/xml11/#AVNormalize
119    /// [for 1.0]: https://www.w3.org/TR/xml/#AVNormalize
120    /// [only for]: https://html.spec.whatwg.org/#normalize-newlines
121    /// [`EscapeError::TooManyNestedEntities`]: crate::escape::EscapeError::TooManyNestedEntities
122    pub fn normalized_value_with<'entity>(
123        &self,
124        version: XmlVersion,
125        depth: usize,
126        resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
127    ) -> XmlResult<Cow<'a, str>> {
128        match version.normalize_attribute_value(&self.value, depth, resolve_entity)? {
129            // Because result is borrowed, no replacements was done and we can use original string
130            Cow::Borrowed(_) => Ok(self.value.clone()),
131            Cow::Owned(s) => Ok(s.into()),
132        }
133    }
134
135    /// Decodes using a provided reader and returns the attribute value normalized
136    /// as per [the XML specification] (or [for 1.0]).
137    ///
138    /// # Deprecation
139    ///
140    /// Attribute values are now always stored as valid UTF-8 strings, so decoding
141    /// is no longer needed. Use [`normalized_value()`](Self::normalized_value) instead.
142    ///
143    /// Do not use this method with HTML attributes.
144    ///
145    /// The characters `\t`, `\r`, `\n` are replaced with whitespace characters (`0x20`).
146    ///
147    /// The following escape sequences are replaced with their unescaped equivalents:
148    ///
149    /// | Escape Sequence | Replacement
150    /// |-----------------|------------
151    /// | `&lt;`          | `<`
152    /// | `&gt;`          | `>`
153    /// | `&amp;`         | `&`
154    /// | `&apos;`        | `'`
155    /// | `&quot;`        | `"`
156    ///
157    /// This will allocate unless the raw attribute value does not require normalization.
158    ///
159    /// Note, although you may use this library to parse HTML, you cannot use this
160    /// method to get HTML content, because its returns normalized value: the following
161    /// sequences are translated into a single space (U+0020) character:
162    ///
163    /// - `\r\n`
164    /// - `\r\x85` (only XML 1.1)
165    /// - `\r`
166    /// - `\n`
167    /// - `\t`
168    /// - `\x85` (only XML 1.1)
169    /// - `\x2028` (only XML 1.1)
170    ///
171    /// The text in HTML normally is not normalized in any way; normalization is
172    /// performed only in limited contexts and [only for] `\r\n` and `\r`.
173    ///
174    /// See also [`decoded_and_normalized_value_with()`](#method.decoded_and_normalized_value_with)
175    ///
176    /// [the XML specification]: https://www.w3.org/TR/xml11/#AVNormalize
177    /// [for 1.0]: https://www.w3.org/TR/xml/#AVNormalize
178    /// [only for]: https://html.spec.whatwg.org/#normalize-newlines
179    #[deprecated = "decoding is no longer needed, use `normalized_value()` instead"]
180    #[inline]
181    pub fn decoded_and_normalized_value(
182        &self,
183        version: XmlVersion,
184        _decoder: Decoder,
185    ) -> XmlResult<Cow<'a, str>> {
186        self.normalized_value(version)
187    }
188
189    /// Decodes using a provided reader and returns the attribute value normalized
190    /// as per [the XML specification] (or [for 1.0]), using a custom entity resolver.
191    ///
192    /// # Deprecation
193    ///
194    /// Attribute values are now always stored as valid UTF-8 strings, so decoding
195    /// is no longer needed. Use [`normalized_value_with()`](Self::normalized_value_with) instead.
196    ///
197    /// Do not use this method with HTML attributes.
198    ///
199    /// The characters `\t`, `\r`, `\n` are replaced with whitespace characters (`0x20`).
200    ///
201    /// A function for resolving entities can be provided as `resolve_entity`.
202    /// This method does not resolve any predefined entities, but you can use
203    /// [`resolve_predefined_entity`] in your function.
204    ///
205    /// This will allocate unless the raw attribute value does not require normalization.
206    ///
207    /// Note, although you may use this library to parse HTML, you cannot use this
208    /// method to get HTML content, because its returns normalized value: the following
209    /// sequences are translated into a single space (U+0020) character:
210    ///
211    /// - `\r\n`
212    /// - `\r\x85` (only XML 1.1)
213    /// - `\r`
214    /// - `\n`
215    /// - `\t`
216    /// - `\x85` (only XML 1.1)
217    /// - `\x2028` (only XML 1.1)
218    ///
219    /// The text in HTML normally is not normalized in any way; normalization is
220    /// performed only in limited contexts and [only for] `\r\n` and `\r`.
221    ///
222    /// See also [`decoded_and_normalized_value()`](#method.decoded_and_normalized_value)
223    ///
224    /// # Parameters
225    ///
226    /// - `depth`: maximum number of nested entities that can be expanded. If expansion
227    ///   chain will be more that this value, the function will return [`EscapeError::TooManyNestedEntities`]
228    /// - `resolve_entity`: a function to resolve entity. This function could be called
229    ///   multiple times on the same input and can return different values in each case
230    ///   for the same input, although it is not recommended
231    ///
232    /// [the XML specification]: https://www.w3.org/TR/xml11/#AVNormalize
233    /// [for 1.0]: https://www.w3.org/TR/xml/#AVNormalize
234    /// [only for]: https://html.spec.whatwg.org/#normalize-newlines
235    /// [`EscapeError::TooManyNestedEntities`]: crate::escape::EscapeError::TooManyNestedEntities
236    #[deprecated = "decoding is no longer needed, use `normalized_value_with()` instead"]
237    #[inline]
238    pub fn decoded_and_normalized_value_with<'entity>(
239        &self,
240        version: XmlVersion,
241        _decoder: Decoder,
242        depth: usize,
243        resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
244    ) -> XmlResult<Cow<'a, str>> {
245        self.normalized_value_with(version, depth, resolve_entity)
246    }
247
248    /// Returns the unescaped value.
249    ///
250    /// # Deprecation
251    ///
252    /// Use [`normalized_value()`](Self::normalized_value) instead.
253    ///
254    /// Escape sequences such as `&gt;` are replaced with their unescaped
255    /// equivalents such as `>`.
256    ///
257    /// This will allocate if the value contains any escape sequences.
258    ///
259    /// See also [`unescape_value_with()`](Self::unescape_value_with)
260    ///
261    /// [`encoding`]: ../../index.html#encoding
262    #[cfg(any(doc, not(feature = "encoding")))]
263    #[deprecated = "use `Self::normalized_value()`"]
264    pub fn unescape_value(&self) -> XmlResult<Cow<'a, str>> {
265        // resolve_predefined_entity returns only non-recursive replacements, so depth=1 is enough
266        self.normalized_value_with(XmlVersion::Implicit1_0, 1, resolve_predefined_entity)
267    }
268
269    /// Decodes using UTF-8 then unescapes the value, using custom entities.
270    ///
271    /// # Deprecation
272    ///
273    /// Use [`normalized_value_with()`](Self::normalized_value_with) instead.
274    ///
275    /// Escape sequences such as `&gt;` are replaced with their unescaped
276    /// equivalents such as `>`. A fallback resolver for additional custom
277    /// entities can be provided via `resolve_entity`.
278    ///
279    /// This will allocate if the value contains any escape sequences.
280    ///
281    /// See also [`unescape_value()`](Self::unescape_value)
282    ///
283    /// [`encoding`]: ../../index.html#encoding
284    #[cfg(any(doc, not(feature = "encoding")))]
285    #[deprecated = "use `Self::normalized_value_with()`"]
286    #[inline]
287    pub fn unescape_value_with<'entity>(
288        &self,
289        resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
290    ) -> XmlResult<Cow<'a, str>> {
291        self.normalized_value_with(XmlVersion::Implicit1_0, 128, resolve_entity)
292    }
293
294    /// Decodes then unescapes the value.
295    ///
296    /// # Deprecation
297    ///
298    /// Attribute values are now always stored as valid UTF-8 strings, so decoding
299    /// is no longer needed. Use [`normalized_value()`](Self::normalized_value) instead.
300    ///
301    /// This will allocate if the value contains any escape sequences.
302    #[deprecated = "decoding is no longer needed, use `Self::normalized_value()` instead"]
303    #[inline]
304    pub fn decode_and_unescape_value(&self, _decoder: Decoder) -> XmlResult<Cow<'a, str>> {
305        self.normalized_value(XmlVersion::Implicit1_0)
306    }
307
308    /// Decodes then unescapes the value with custom entities.
309    ///
310    /// # Deprecation
311    ///
312    /// Attribute values are now always stored as valid UTF-8 strings, so decoding
313    /// is no longer needed. Use [`normalized_value_with()`](Self::normalized_value_with) instead.
314    ///
315    /// This will allocate if the value contains any escape sequences.
316    #[deprecated = "decoding is no longer needed, use `Self::normalized_value_with()` instead"]
317    #[inline]
318    pub fn decode_and_unescape_value_with<'entity>(
319        &self,
320        _decoder: Decoder,
321        resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
322    ) -> XmlResult<Cow<'a, str>> {
323        self.normalized_value_with(XmlVersion::Implicit1_0, 128, resolve_entity)
324    }
325
326    /// If attribute value [represents] valid boolean values, returns `Some`, otherwise returns `None`.
327    ///
328    /// The valid boolean representations are only `"true"`, `"false"`, `"1"`, and `"0"`.
329    ///
330    /// # Examples
331    ///
332    /// ```
333    /// # use pretty_assertions::assert_eq;
334    /// use quick_xml::events::attributes::Attribute;
335    ///
336    /// let attr = Attribute::from(("attr", "false"));
337    /// assert_eq!(attr.as_bool(), Some(false));
338    ///
339    /// let attr = Attribute::from(("attr", "0"));
340    /// assert_eq!(attr.as_bool(), Some(false));
341    ///
342    /// let attr = Attribute::from(("attr", "true"));
343    /// assert_eq!(attr.as_bool(), Some(true));
344    ///
345    /// let attr = Attribute::from(("attr", "1"));
346    /// assert_eq!(attr.as_bool(), Some(true));
347    ///
348    /// let attr = Attribute::from(("attr", "not bool"));
349    /// assert_eq!(attr.as_bool(), None);
350    /// ```
351    ///
352    /// [represents]: https://www.w3.org/TR/xmlschema11-2/#boolean
353    #[inline]
354    pub fn as_bool(&self) -> Option<bool> {
355        match self.value.as_ref() {
356            "1" | "true" => Some(true),
357            "0" | "false" => Some(false),
358            _ => None,
359        }
360    }
361}
362
363impl<'a> From<(&'a str, &'a str)> for Attribute<'a> {
364    /// Creates new attribute from text representation.
365    /// Key is stored as-is, but the value will be escaped.
366    ///
367    /// # Examples
368    ///
369    /// ```
370    /// # use pretty_assertions::assert_eq;
371    /// use quick_xml::events::attributes::Attribute;
372    ///
373    /// let features = Attribute::from(("features", "Bells & whistles"));
374    /// assert_eq!(features.value.as_ref(), "Bells &amp; whistles");
375    /// ```
376    fn from(val: (&'a str, &'a str)) -> Attribute<'a> {
377        Attribute {
378            key: QName(val.0),
379            value: escape_attribute(val.1),
380        }
381    }
382}
383
384impl<'a> From<(&'a str, Cow<'a, str>)> for Attribute<'a> {
385    /// Creates new attribute from text representation.
386    /// Key is stored as-is, but the value will be escaped.
387    ///
388    /// # Examples
389    ///
390    /// ```
391    /// # use std::borrow::Cow;
392    /// use pretty_assertions::assert_eq;
393    /// use quick_xml::events::attributes::Attribute;
394    ///
395    /// let features = Attribute::from(("features", Cow::Borrowed("Bells & whistles")));
396    /// assert_eq!(features.value.as_ref(), "Bells &amp; whistles");
397    /// ```
398    fn from(val: (&'a str, Cow<'a, str>)) -> Attribute<'a> {
399        Attribute {
400            key: QName(val.0),
401            value: escape_attribute(val.1),
402        }
403    }
404}
405
406impl<'a> From<Attr<&'a str>> for Attribute<'a> {
407    #[inline]
408    fn from(attr: Attr<&'a str>) -> Self {
409        Self {
410            key: attr.key(),
411            value: Cow::Borrowed(attr.value()),
412        }
413    }
414}
415
416////////////////////////////////////////////////////////////////////////////////////////////////////
417
418/// Iterator over XML attributes.
419///
420/// Yields `Result<Attribute>`. An `Err` will be yielded if an attribute is malformed or duplicated.
421/// The duplicate check can be turned off by calling [`with_checks(false)`].
422///
423/// When [`serialize`] feature is enabled, can be converted to serde's deserializer.
424///
425/// # Lifetime
426///
427/// `'a` is a lifetime of the owning event from which this iterator is derived.
428///
429/// [`with_checks(false)`]: Self::with_checks
430/// [`serialize`]: ../../index.html#serialize
431#[derive(Clone)]
432pub struct Attributes<'a> {
433    /// Slice of `BytesStart` corresponding to attributes
434    buf: &'a str,
435    /// Iterator state, independent from the actual source of bytes
436    state: IterState,
437}
438
439impl<'a> Attributes<'a> {
440    /// Internal constructor, used by `BytesStart`. Supplies data in reader's encoding
441    #[inline]
442    pub(crate) const fn wrap(buf: &'a str, pos: usize, html: bool) -> Self {
443        Self {
444            buf,
445            state: IterState::new(pos, html),
446        }
447    }
448
449    /// Creates a new attribute iterator from a buffer, which recognizes only XML-style
450    /// attributes, i. e. those which in the form `name = "value"` or `name = 'value'`.
451    /// HTML style attributes (i. e. without quotes or only name) will return a error.
452    ///
453    /// # Parameters
454    /// - `buf`: a buffer with a tag name and attributes, usually this is the whole
455    ///   string between `<` and `>` (or `/>`) of a tag;
456    /// - `pos`: a position in the `buf` where tag name is finished and attributes
457    ///   is started. It is not necessary to point exactly to the end of a tag name,
458    ///   although that is usually that. If it will be more than the `buf` length,
459    ///   then the iterator will return `None`` immediately.
460    ///
461    /// # Example
462    /// ```
463    /// # use quick_xml::events::attributes::{Attribute, Attributes};
464    /// # use pretty_assertions::assert_eq;
465    /// #
466    /// let mut iter = Attributes::new("tag-name attr1 = 'value1' attr2='value2' ", 9);
467    /// //                              ^0       ^9
468    /// assert_eq!(iter.next(), Some(Ok(Attribute::from(("attr1", "value1")))));
469    /// assert_eq!(iter.next(), Some(Ok(Attribute::from(("attr2", "value2")))));
470    /// assert_eq!(iter.next(), None);
471    /// ```
472    pub const fn new(buf: &'a str, pos: usize) -> Self {
473        Self::wrap(buf, pos, false)
474    }
475
476    /// Creates a new attribute iterator from a buffer, allowing HTML attribute syntax.
477    ///
478    /// # Parameters
479    /// - `buf`: a buffer with a tag name and attributes, usually this is the whole
480    ///   string between `<` and `>` (or `/>`) of a tag;
481    /// - `pos`: a position in the `buf` where tag name is finished and attributes
482    ///   is started. It is not necessary to point exactly to the end of a tag name,
483    ///   although that is usually that. If it will be more than the `buf` length,
484    ///   then the iterator will return `None`` immediately.
485    ///
486    /// # Example
487    /// ```
488    /// # use quick_xml::events::attributes::{Attribute, Attributes};
489    /// # use pretty_assertions::assert_eq;
490    /// #
491    /// let mut iter = Attributes::html("tag-name attr1 = value1 attr2 ", 9);
492    /// //                               ^0       ^9
493    /// assert_eq!(iter.next(), Some(Ok(Attribute::from(("attr1", "value1")))));
494    /// assert_eq!(iter.next(), Some(Ok(Attribute::from(("attr2", "")))));
495    /// assert_eq!(iter.next(), None);
496    /// ```
497    pub const fn html(buf: &'a str, pos: usize) -> Self {
498        Self::wrap(buf, pos, true)
499    }
500
501    /// Changes whether attributes should be checked for uniqueness.
502    ///
503    /// The XML specification requires attribute keys in the same element to be unique. This check
504    /// can be disabled to improve performance slightly.
505    ///
506    /// (`true` by default)
507    pub fn with_checks(&mut self, val: bool) -> &mut Attributes<'a> {
508        self.state.check_duplicates = val;
509        self
510    }
511
512    /// Checks if the current tag has a [`xsi:nil`] attribute. This method ignores any errors in
513    /// attributes.
514    ///
515    /// # Examples
516    ///
517    /// ```
518    /// # use pretty_assertions::assert_eq;
519    /// use quick_xml::events::Event;
520    /// use quick_xml::name::QName;
521    /// use quick_xml::reader::NsReader;
522    ///
523    /// let mut reader = NsReader::from_str("
524    ///     <root xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
525    ///         <true xsi:nil='true'/>
526    ///         <false xsi:nil='false'/>
527    ///         <none/>
528    ///         <non-xsi xsi:nil='true' xmlns:xsi='namespace'/>
529    ///         <unbound-nil nil='true' xmlns='http://www.w3.org/2001/XMLSchema-instance'/>
530    ///         <another-xmlns f:nil='true' xmlns:f='http://www.w3.org/2001/XMLSchema-instance'/>
531    ///     </root>
532    /// ");
533    /// reader.config_mut().trim_text(true);
534    ///
535    /// macro_rules! check {
536    ///     ($reader:expr, $name:literal, $value:literal) => {
537    ///         let event = match $reader.read_event().unwrap() {
538    ///             Event::Empty(e) => e,
539    ///             e => panic!("Unexpected event {:?}", e),
540    ///         };
541    ///         assert_eq!(
542    ///             (event.name(), event.attributes().has_nil($reader.resolver())),
543    ///             (QName($name), $value),
544    ///         );
545    ///     };
546    /// }
547    ///
548    /// let root = match reader.read_event().unwrap() {
549    ///     Event::Start(e) => e,
550    ///     e => panic!("Unexpected event {:?}", e),
551    /// };
552    /// assert_eq!(root.attributes().has_nil(reader.resolver()), false);
553    ///
554    /// // definitely true
555    /// check!(reader, "true",          true);
556    /// // definitely false
557    /// check!(reader, "false",         false);
558    /// // absence of the attribute means that attribute is not set
559    /// check!(reader, "none",          false);
560    /// // attribute not bound to the correct namespace
561    /// check!(reader, "non-xsi",       false);
562    /// // attributes without prefix not bound to any namespace
563    /// check!(reader, "unbound-nil",   false);
564    /// // prefix can be any while it is bound to the correct namespace
565    /// check!(reader, "another-xmlns", true);
566    /// ```
567    ///
568    /// [`xsi:nil`]: https://www.w3.org/TR/xmlschema-1/#xsi_nil
569    pub fn has_nil(&mut self, resolver: &NamespaceResolver) -> bool {
570        use crate::name::ResolveResult::*;
571
572        self.any(|attr| {
573            if let Ok(attr) = attr {
574                match resolver.resolve_attribute(attr.key) {
575                    (
576                        Bound(Namespace("http://www.w3.org/2001/XMLSchema-instance")),
577                        LocalName("nil"),
578                    ) => attr.as_bool().unwrap_or_default(),
579                    _ => false,
580                }
581            } else {
582                false
583            }
584        })
585    }
586}
587
588impl<'a> Debug for Attributes<'a> {
589    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
590        f.debug_struct("Attributes")
591            .field("buf", &self.buf)
592            .field("state", &self.state)
593            .finish()
594    }
595}
596
597impl<'a> Iterator for Attributes<'a> {
598    type Item = Result<Attribute<'a>, AttrError>;
599
600    #[inline]
601    fn next(&mut self) -> Option<Self::Item> {
602        match self.state.next(self.buf.as_bytes()) {
603            None => None,
604            Some(Ok(a)) => Some(Ok(a.map(|range| &self.buf[range]).into())),
605            Some(Err(e)) => Some(Err(e)),
606        }
607    }
608}
609
610impl<'a> FusedIterator for Attributes<'a> {}
611
612////////////////////////////////////////////////////////////////////////////////////////////////////
613
614/// Errors that can be raised during parsing attributes.
615///
616/// Recovery position in examples shows the position from which parsing of the
617/// next attribute will be attempted.
618#[derive(Clone, Debug, PartialEq, Eq)]
619pub enum AttrError {
620    /// Attribute key was not followed by `=`, position relative to the start of
621    /// the owning tag is provided.
622    ///
623    /// Example of input that raises this error:
624    ///
625    /// ```xml
626    /// <tag key another="attribute"/>
627    /// <!--     ^~~ error position, recovery position (8) -->
628    /// ```
629    ///
630    /// This error can be raised only when the iterator is in XML mode.
631    ExpectedEq(usize),
632    /// Attribute value was not found after `=`, position relative to the start
633    /// of the owning tag is provided.
634    ///
635    /// Example of input that raises this error:
636    ///
637    /// ```xml
638    /// <tag key = />
639    /// <!--       ^~~ error position, recovery position (10) -->
640    /// ```
641    ///
642    /// This error can be returned only for the last attribute in the list,
643    /// because otherwise any content after `=` will be treated as a value.
644    /// The XML
645    ///
646    /// ```xml
647    /// <tag key = another-key = "value"/>
648    /// <!--                   ^ ^- recovery position (24) -->
649    /// <!--                   '~~ error position (22) -->
650    /// ```
651    ///
652    /// will be treated as `Attribute { key = b"key", value = b"another-key" }`
653    /// and or [`Attribute`] is returned, or [`AttrError::UnquotedValue`] is raised,
654    /// depending on the parsing mode.
655    ExpectedValue(usize),
656    /// Attribute value is not quoted, position relative to the start of the
657    /// owning tag is provided.
658    ///
659    /// Example of input that raises this error:
660    ///
661    /// ```xml
662    /// <tag key = value />
663    /// <!--       ^    ^~~ recovery position (15) -->
664    /// <!--       '~~ error position (10) -->
665    /// ```
666    ///
667    /// This error can be raised only when the iterator is in XML mode.
668    UnquotedValue(usize),
669    /// Attribute value was not finished with a matching quote, position relative
670    /// to the start of owning tag and a quote is provided. That position is always
671    /// a last character in the tag content.
672    ///
673    /// Example of input that raises this error:
674    ///
675    /// ```xml
676    /// <tag key = "value  />
677    /// <tag key = 'value  />
678    /// <!--               ^~~ error position, recovery position (18) -->
679    /// ```
680    ///
681    /// This error can be returned only for the last attribute in the list,
682    /// because all input was consumed during scanning for a quote.
683    ExpectedQuote(usize, u8),
684    /// An attribute with the same name was already encountered. Two parameters
685    /// define (1) the error position relative to the start of the owning tag
686    /// for a new attribute and (2) the start position of a previously encountered
687    /// attribute with the same name.
688    ///
689    /// Example of input that raises this error:
690    ///
691    /// ```xml
692    /// <tag key = 'value'  key="value2" attr3='value3' />
693    /// <!-- ^              ^            ^~~ recovery position (32) -->
694    /// <!-- |              '~~ error position (19) -->
695    /// <!-- '~~ previous position (4) -->
696    /// ```
697    ///
698    /// This error is returned only when [`Attributes::with_checks()`] is set
699    /// to `true` (that is default behavior).
700    Duplicated(usize, usize),
701}
702
703impl Display for AttrError {
704    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
705        match self {
706            Self::ExpectedEq(pos) => write!(
707                f,
708                r#"position {}: attribute key must be directly followed by `=` or space"#,
709                pos
710            ),
711            Self::ExpectedValue(pos) => write!(
712                f,
713                r#"position {}: `=` must be followed by an attribute value"#,
714                pos
715            ),
716            Self::UnquotedValue(pos) => write!(
717                f,
718                r#"position {}: attribute value must be enclosed in `"` or `'`"#,
719                pos
720            ),
721            Self::ExpectedQuote(pos, quote) => write!(
722                f,
723                r#"position {}: missing closing quote `{}` in attribute value"#,
724                pos, *quote as char
725            ),
726            Self::Duplicated(pos1, pos2) => write!(
727                f,
728                r#"position {}: duplicated attribute, previous declaration at position {}"#,
729                pos1, pos2
730            ),
731        }
732    }
733}
734
735impl std::error::Error for AttrError {}
736
737////////////////////////////////////////////////////////////////////////////////////////////////////
738
739/// A struct representing a key/value XML or HTML [attribute].
740///
741/// [attribute]: https://www.w3.org/TR/xml11/#NT-Attribute
742#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
743pub enum Attr<T> {
744    /// Attribute with value enclosed in double quotes (`"`). Attribute key and
745    /// value provided. This is a canonical XML-style attribute.
746    DoubleQ(T, T),
747    /// Attribute with value enclosed in single quotes (`'`). Attribute key and
748    /// value provided. This is an XML-style attribute.
749    SingleQ(T, T),
750    /// Attribute with value not enclosed in quotes. Attribute key and value
751    /// provided. This is HTML-style attribute, it can be returned in HTML-mode
752    /// parsing only. In an XML mode [`AttrError::UnquotedValue`] will be raised
753    /// instead.
754    ///
755    /// Attribute value can be invalid according to the [HTML specification],
756    /// in particular, it can contain `"`, `'`, `=`, `<`, and <code>&#96;</code>
757    /// characters. The absence of the `>` character is nevertheless guaranteed,
758    /// since the parser extracts [events] based on them even before the start
759    /// of parsing attributes.
760    ///
761    /// [HTML specification]: https://html.spec.whatwg.org/#unquoted
762    /// [events]: crate::events::Event::Start
763    Unquoted(T, T),
764    /// Attribute without value. Attribute key provided. This is HTML-style attribute,
765    /// it can be returned in HTML-mode parsing only. In XML mode
766    /// [`AttrError::ExpectedEq`] will be raised instead.
767    Empty(T),
768}
769
770impl<T> Attr<T> {
771    /// Maps an `Attr<T>` to `Attr<U>` by applying a function to a contained key and value.
772    #[inline]
773    pub fn map<U, F>(self, mut f: F) -> Attr<U>
774    where
775        F: FnMut(T) -> U,
776    {
777        match self {
778            Attr::DoubleQ(key, value) => Attr::DoubleQ(f(key), f(value)),
779            Attr::SingleQ(key, value) => Attr::SingleQ(f(key), f(value)),
780            Attr::Empty(key) => Attr::Empty(f(key)),
781            Attr::Unquoted(key, value) => Attr::Unquoted(f(key), f(value)),
782        }
783    }
784}
785
786impl<'a> Attr<&'a str> {
787    /// Returns the key value
788    #[inline]
789    pub const fn key(&self) -> QName<'a> {
790        QName(match self {
791            Attr::DoubleQ(key, _) => *key,
792            Attr::SingleQ(key, _) => *key,
793            Attr::Empty(key) => *key,
794            Attr::Unquoted(key, _) => *key,
795        })
796    }
797    /// Returns the attribute value. For [`Self::Empty`] variant an empty string
798    /// is returned according to the [HTML specification].
799    ///
800    /// [HTML specification]: https://www.w3.org/TR/2012/WD-html-markup-20120329/syntax.html#syntax-attr-empty
801    #[inline]
802    pub const fn value(&self) -> &'a str {
803        match self {
804            Attr::DoubleQ(_, value) => *value,
805            Attr::SingleQ(_, value) => *value,
806            Attr::Empty(_) => "",
807            Attr::Unquoted(_, value) => *value,
808        }
809    }
810}
811
812impl<T: Debug> Debug for Attr<T> {
813    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
814        match self {
815            Attr::DoubleQ(key, value) => f
816                .debug_tuple("Attr::DoubleQ")
817                .field(key)
818                .field(value)
819                .finish(),
820            Attr::SingleQ(key, value) => f
821                .debug_tuple("Attr::SingleQ")
822                .field(key)
823                .field(value)
824                .finish(),
825            Attr::Empty(key) => f.debug_tuple("Attr::Empty").field(key).finish(),
826            Attr::Unquoted(key, value) => f
827                .debug_tuple("Attr::Unquoted")
828                .field(key)
829                .field(value)
830                .finish(),
831        }
832    }
833}
834
835/// Unpacks attribute key and value into tuple of this two elements.
836/// `None` value element is returned only for [`Attr::Empty`] variant.
837impl<T> From<Attr<T>> for (T, Option<T>) {
838    #[inline]
839    fn from(attr: Attr<T>) -> Self {
840        match attr {
841            Attr::DoubleQ(key, value) => (key, Some(value)),
842            Attr::SingleQ(key, value) => (key, Some(value)),
843            Attr::Empty(key) => (key, None),
844            Attr::Unquoted(key, value) => (key, Some(value)),
845        }
846    }
847}
848
849////////////////////////////////////////////////////////////////////////////////////////////////////
850
851type AttrResult = Result<Attr<Range<usize>>, AttrError>;
852
853#[derive(Clone, Copy, Debug)]
854enum State {
855    /// Iteration finished, iterator will return `None` to all [`IterState::next`]
856    /// requests.
857    Done,
858    /// The last attribute returned was deserialized successfully. Contains an
859    /// offset from which next attribute should be searched.
860    Next(usize),
861    /// The last attribute returns [`AttrError::UnquotedValue`], offset pointed
862    /// to the beginning of the value. Recover should skip a value
863    SkipValue(usize),
864    /// The last attribute returns [`AttrError::Duplicated`], offset pointed to
865    /// the equal (`=`) sign. Recover should skip it and a value
866    SkipEqValue(usize),
867}
868
869/// Number of attributes a start tag may have before the duplicate-name check
870/// switches from a direct linear scan of the previously seen names to a hash
871/// pre-filter (see [`IterState::check_for_duplicates`]).
872///
873/// Real-world start tags carry only a handful of attributes -- the busiest
874/// element in our benchmark corpus (`tests/documents/players.xml`) has 22 --
875/// where the scan is faster than hashing and needs no allocation. Larger tags
876/// are where the scan became the O(N²) CPU-DoS of [#969], so above this count we
877/// pay for a hash set to keep the whole tag O(N). The value sits just above the
878/// measured linear-vs-hash crossover.
879///
880/// [#969]: https://github.com/tafia/quick-xml/issues/969
881const SMALL_ATTRIBUTE_COUNT: usize = 32;
882
883/// A no-op [`Hasher`] for the `key_hashes` set, whose values are already 64-bit
884/// hashes of attribute names; re-hashing them with the default SipHash would be
885/// wasted work. Only `write_u64` is ever exercised (via `u64`'s `Hash` impl).
886#[derive(Default)]
887struct IdentityHasher(u64);
888
889impl Hasher for IdentityHasher {
890    #[inline]
891    fn finish(&self) -> u64 {
892        self.0
893    }
894
895    #[inline]
896    fn write(&mut self, _: &[u8]) {
897        // The set only ever stores `u64` keys, which route through `write_u64`.
898        unreachable!("IdentityHasher only supports u64 keys")
899    }
900
901    #[inline]
902    fn write_u64(&mut self, n: u64) {
903        self.0 = n;
904    }
905}
906
907/// Hashes a single attribute name. A fresh [`DefaultHasher`] per name keeps each
908/// hash independent (so it is also DoS-resistant on untrusted input).
909#[inline]
910fn hash_name(name: &[u8]) -> u64 {
911    let mut hasher = DefaultHasher::new();
912    hasher.write(name);
913    hasher.finish()
914}
915
916/// External iterator over spans of attribute key and value
917#[derive(Clone, Debug)]
918pub(crate) struct IterState {
919    /// Iteration state that determines what actions should be done before the
920    /// actual parsing of the next attribute
921    state: State,
922    /// If `true`, enables ability to parse unquoted values and key-only (empty)
923    /// attributes
924    html: bool,
925    /// If `true`, checks for duplicate names
926    check_duplicates: bool,
927    /// If `check_duplicates` is set, contains the ranges of already parsed attribute
928    /// names. We store a ranges instead of slices to able to report a previous
929    /// attribute position
930    keys: Vec<Range<usize>>,
931    /// 64-bit hashes of the byte content of `keys`, used as an O(1) pre-filter
932    /// once a start tag declares more than `SMALL_ATTRIBUTE_COUNT` attributes, so
933    /// the duplicate check stays O(N) over the whole tag instead of O(N²). The
934    /// values are already hashes, so the set stores them with `IdentityHasher`
935    /// instead of re-hashing. Allocated only when the threshold is crossed, so
936    /// small tags (and [`IterState::new`]) stay allocation-free and `const`.
937    key_hashes: Option<HashSet<u64, BuildHasherDefault<IdentityHasher>>>,
938}
939
940impl IterState {
941    pub const fn new(offset: usize, html: bool) -> Self {
942        Self {
943            state: State::Next(offset),
944            html,
945            check_duplicates: true,
946            keys: Vec::new(),
947            key_hashes: None,
948        }
949    }
950
951    /// Recover from an error that could have been made on a previous step.
952    /// Returns an offset from which parsing should continue.
953    /// If there no input left, returns `None`.
954    fn recover(&self, slice: &[u8]) -> Option<usize> {
955        match self.state {
956            State::Done => None,
957            State::Next(offset) if offset <= slice.len() => Some(offset),
958            State::Next(_) => None,
959            State::SkipValue(offset) => self.skip_value(slice, offset),
960            State::SkipEqValue(offset) => self.skip_eq_value(slice, offset),
961        }
962    }
963
964    /// Skip all characters up to first space symbol or end-of-input
965    #[inline]
966    #[allow(clippy::manual_map)]
967    fn skip_value(&self, slice: &[u8], offset: usize) -> Option<usize> {
968        let mut iter = (offset..).zip(slice[offset..].iter());
969
970        match iter.find(|&(_, &b)| is_whitespace(b)) {
971            // Input: `    key  =  value `
972            //                     |    ^
973            //                offset    e
974            Some((e, _)) => Some(e),
975            // Input: `    key  =  value`
976            //                     |    ^
977            //                offset    e = len()
978            None => None,
979        }
980    }
981
982    /// Skip all characters up to first space symbol or end-of-input
983    #[inline]
984    fn skip_eq_value(&self, slice: &[u8], offset: usize) -> Option<usize> {
985        let mut iter = (offset..).zip(slice[offset..].iter());
986
987        // Skip all up to the quote and get the quote type
988        let quote = match iter.find(|&(_, &b)| !is_whitespace(b)) {
989            // Input: `    key  =  "`
990            //                  |  ^
991            //             offset
992            Some((_, b'"')) => b'"',
993            // Input: `    key  =  '`
994            //                  |  ^
995            //             offset
996            Some((_, b'\'')) => b'\'',
997
998            // Input: `    key  =  x`
999            //                  |  ^
1000            //             offset
1001            Some((offset, _)) => return self.skip_value(slice, offset),
1002            // Input: `    key  =  `
1003            //                  |  ^
1004            //             offset
1005            None => return None,
1006        };
1007
1008        match iter.find(|&(_, &b)| b == quote) {
1009            // Input: `    key  =  "   "`
1010            //                         ^
1011            Some((e, b'"')) => Some(e),
1012            // Input: `    key  =  '   '`
1013            //                         ^
1014            Some((e, _)) => Some(e),
1015
1016            // Input: `    key  =  "   `
1017            // Input: `    key  =  '   `
1018            //                         ^
1019            // Closing quote not found
1020            None => None,
1021        }
1022    }
1023
1024    /// Checks that the attribute name `key` (a range into `slice`) was not seen
1025    /// earlier in the same start tag, recording it for subsequent checks.
1026    ///
1027    /// Small tags use a direct linear scan of [`Self::keys`]: for a handful of
1028    /// attributes that beats hashing and needs no allocation, which is the
1029    /// overwhelmingly common case. Once a tag declares more than
1030    /// `SMALL_ATTRIBUTE_COUNT` attributes -- where the scan would become the
1031    /// O(N²) CPU-DoS of [#969] -- it switches to a hash pre-filter that keeps the
1032    /// whole tag O(N).
1033    ///
1034    /// [#969]: https://github.com/tafia/quick-xml/issues/969
1035    #[inline]
1036    fn check_for_duplicates(
1037        &mut self,
1038        slice: &[u8],
1039        key: Range<usize>,
1040    ) -> Result<Range<usize>, AttrError> {
1041        if self.check_duplicates {
1042            if self.keys.len() >= SMALL_ATTRIBUTE_COUNT {
1043                return self.check_for_duplicates_hashed(slice, key);
1044            }
1045            if let Some(prev) = self
1046                .keys
1047                .iter()
1048                .find(|r| slice[(*r).clone()] == slice[key.clone()])
1049            {
1050                return Err(AttrError::Duplicated(key.start, prev.start));
1051            }
1052            self.keys.push(key.clone());
1053        }
1054        Ok(key)
1055    }
1056
1057    /// Cold path of [`Self::check_for_duplicates`] for start tags with many
1058    /// attributes: a [`HashSet`] of 64-bit name hashes acts as an O(1) pre-filter
1059    /// so iterating N attributes is O(N) rather than O(N²).
1060    #[cold]
1061    fn check_for_duplicates_hashed(
1062        &mut self,
1063        slice: &[u8],
1064        key: Range<usize>,
1065    ) -> Result<Range<usize>, AttrError> {
1066        let keys = &self.keys;
1067        let key_hashes = self.key_hashes.get_or_insert_with(|| {
1068            // First time over the threshold: seed the set with the names already
1069            // collected during the linear phase so the pre-filter knows them.
1070            let mut set = HashSet::with_capacity_and_hasher(
1071                keys.len() * 2,
1072                BuildHasherDefault::<IdentityHasher>::default(),
1073            );
1074            for r in keys {
1075                set.insert(hash_name(&slice[r.clone()]));
1076            }
1077            set
1078        });
1079        // A fresh hash proves the name is new. On a hit (a real duplicate, or the
1080        // astronomically rare 64-bit collision) fall back to the linear scan to
1081        // recover the exact previous position for `AttrError::Duplicated`.
1082        if !key_hashes.insert(hash_name(&slice[key.clone()])) {
1083            if let Some(prev) = self
1084                .keys
1085                .iter()
1086                .find(|r| slice[(*r).clone()] == slice[key.clone()])
1087            {
1088                return Err(AttrError::Duplicated(key.start, prev.start));
1089            }
1090        }
1091        self.keys.push(key.clone());
1092        Ok(key)
1093    }
1094
1095    /// # Parameters
1096    ///
1097    /// - `slice`: content of the tag, used for checking for duplicates
1098    /// - `key`: Range of key in slice, if iterator in HTML mode
1099    /// - `offset`: Position of error if iterator in XML mode
1100    #[inline]
1101    fn key_only(&mut self, slice: &[u8], key: Range<usize>, offset: usize) -> Option<AttrResult> {
1102        Some(if self.html {
1103            self.check_for_duplicates(slice, key).map(Attr::Empty)
1104        } else {
1105            Err(AttrError::ExpectedEq(offset))
1106        })
1107    }
1108
1109    #[inline]
1110    fn double_q(&mut self, key: Range<usize>, value: Range<usize>) -> Option<AttrResult> {
1111        self.state = State::Next(value.end + 1); // +1 for `"`
1112
1113        Some(Ok(Attr::DoubleQ(key, value)))
1114    }
1115
1116    #[inline]
1117    fn single_q(&mut self, key: Range<usize>, value: Range<usize>) -> Option<AttrResult> {
1118        self.state = State::Next(value.end + 1); // +1 for `'`
1119
1120        Some(Ok(Attr::SingleQ(key, value)))
1121    }
1122
1123    pub fn next(&mut self, slice: &[u8]) -> Option<AttrResult> {
1124        let mut iter = match self.recover(slice) {
1125            Some(offset) => (offset..).zip(slice[offset..].iter()),
1126            None => return None,
1127        };
1128
1129        // Index where next key started
1130        let start_key = match iter.find(|&(_, &b)| !is_whitespace(b)) {
1131            // Input: `    key`
1132            //             ^
1133            Some((s, _)) => s,
1134            // Input: `    `
1135            //             ^
1136            None => {
1137                // Because we reach end-of-input, stop iteration on next call
1138                self.state = State::Done;
1139                return None;
1140            }
1141        };
1142        // Span of a key
1143        let (key, offset) = match iter.find(|&(_, &b)| b == b'=' || is_whitespace(b)) {
1144            // Input: `    key=`
1145            //             |  ^
1146            //             s  e
1147            Some((e, b'=')) => (start_key..e, e),
1148
1149            // Input: `    key `
1150            //                ^
1151            Some((e, _)) => match iter.find(|&(_, &b)| !is_whitespace(b)) {
1152                // Input: `    key  =`
1153                //             |  | ^
1154                //     start_key  e
1155                Some((offset, b'=')) => (start_key..e, offset),
1156                // Input: `    key  x`
1157                //             |  | ^
1158                //     start_key  e
1159                // If HTML-like attributes is allowed, this is the result, otherwise error
1160                Some((offset, _)) => {
1161                    // In any case, recovering is not required
1162                    self.state = State::Next(offset);
1163                    return self.key_only(slice, start_key..e, offset);
1164                }
1165                // Input: `    key  `
1166                //             |  | ^
1167                //     start_key  e
1168                // If HTML-like attributes is allowed, this is the result, otherwise error
1169                None => {
1170                    // Because we reach end-of-input, stop iteration on next call
1171                    self.state = State::Done;
1172                    return self.key_only(slice, start_key..e, slice.len());
1173                }
1174            },
1175
1176            // Input: `    key`
1177            //             |  ^
1178            //             s  e = len()
1179            // If HTML-like attributes is allowed, this is the result, otherwise error
1180            None => {
1181                // Because we reach end-of-input, stop iteration on next call
1182                self.state = State::Done;
1183                let e = slice.len();
1184                return self.key_only(slice, start_key..e, e);
1185            }
1186        };
1187
1188        let key = match self.check_for_duplicates(slice, key) {
1189            Err(e) => {
1190                self.state = State::SkipEqValue(offset);
1191                return Some(Err(e));
1192            }
1193            Ok(key) => key,
1194        };
1195
1196        ////////////////////////////////////////////////////////////////////////
1197
1198        // Gets the position of quote and quote type
1199        let (start_value, quote) = match iter.find(|&(_, &b)| !is_whitespace(b)) {
1200            // Input: `    key  =  "`
1201            //                     ^
1202            Some((s, b'"')) => (s + 1, b'"'),
1203            // Input: `    key  =  '`
1204            //                     ^
1205            Some((s, b'\'')) => (s + 1, b'\''),
1206
1207            // Input: `    key  =  x`
1208            //                     ^
1209            // If HTML-like attributes is allowed, this is the start of the value
1210            Some((s, _)) if self.html => {
1211                // We do not check validity of attribute value characters as required
1212                // according to https://html.spec.whatwg.org/#unquoted. It can be done
1213                // during validation phase
1214                let end = match iter.find(|&(_, &b)| is_whitespace(b)) {
1215                    // Input: `    key  =  value `
1216                    //                     |    ^
1217                    //                     s    e
1218                    Some((e, _)) => e,
1219                    // Input: `    key  =  value`
1220                    //                     |    ^
1221                    //                     s    e = len()
1222                    None => slice.len(),
1223                };
1224                self.state = State::Next(end);
1225                return Some(Ok(Attr::Unquoted(key, s..end)));
1226            }
1227            // Input: `    key  =  x`
1228            //                     ^
1229            Some((s, _)) => {
1230                self.state = State::SkipValue(s);
1231                return Some(Err(AttrError::UnquotedValue(s)));
1232            }
1233
1234            // Input: `    key  =  `
1235            //                     ^
1236            None => {
1237                // Because we reach end-of-input, stop iteration on next call
1238                self.state = State::Done;
1239                return Some(Err(AttrError::ExpectedValue(slice.len())));
1240            }
1241        };
1242
1243        match iter.find(|&(_, &b)| b == quote) {
1244            // Input: `    key  =  "   "`
1245            //                         ^
1246            Some((e, b'"')) => self.double_q(key, start_value..e),
1247            // Input: `    key  =  '   '`
1248            //                         ^
1249            Some((e, _)) => self.single_q(key, start_value..e),
1250
1251            // Input: `    key  =  "   `
1252            // Input: `    key  =  '   `
1253            //                         ^
1254            // Closing quote not found
1255            None => {
1256                // Because we reach end-of-input, stop iteration on next call
1257                self.state = State::Done;
1258                Some(Err(AttrError::ExpectedQuote(slice.len(), quote)))
1259            }
1260        }
1261    }
1262}
1263
1264////////////////////////////////////////////////////////////////////////////////////////////////////
1265
1266/// Checks, how parsing of XML-style attributes works. Each attribute should
1267/// have a value, enclosed in single or double quotes.
1268#[cfg(test)]
1269mod xml {
1270    use super::*;
1271    use pretty_assertions::assert_eq;
1272
1273    #[test]
1274    fn start_position_at_end_is_empty() {
1275        let mut attributes = Attributes::new("a", 1);
1276        assert_eq!(attributes.next(), None);
1277
1278        let mut attributes = Attributes::html("a", 1);
1279        assert_eq!(attributes.next(), None);
1280
1281        let mut attributes = Attributes::new("a", 1);
1282        assert!(!attributes.has_nil(&NamespaceResolver::default()));
1283    }
1284
1285    #[test]
1286    fn start_position_past_end_is_empty() {
1287        let mut attributes = Attributes::new("a", 2);
1288        assert_eq!(attributes.next(), None);
1289
1290        let mut attributes = Attributes::html("a", 2);
1291        assert_eq!(attributes.next(), None);
1292
1293        let mut attributes = Attributes::new("a", 2);
1294        assert!(!attributes.has_nil(&NamespaceResolver::default()));
1295    }
1296
1297    mod attribute_value_normalization {
1298        use super::*;
1299        use crate::XmlVersion::*;
1300        use crate::errors::Error;
1301        use crate::escape::EscapeError::*;
1302        use pretty_assertions::assert_eq;
1303
1304        /// Empty values returned are unchanged
1305        #[test]
1306        fn empty() {
1307            let raw_value = "";
1308            let attr = Attribute {
1309                key: QName("foo"),
1310                value: Cow::Borrowed(raw_value),
1311            };
1312
1313            let value = attr.normalized_value(Implicit1_0).unwrap();
1314            assert_eq!(value, "");
1315            // assert_eq! does not check if value is borrowed, but this is important
1316            assert!(matches!(value, Cow::Borrowed(_)));
1317
1318            let value = attr.normalized_value(Explicit1_0).unwrap();
1319            assert_eq!(value, "");
1320            // assert_eq! does not check if value is borrowed, but this is important
1321            assert!(matches!(value, Cow::Borrowed(_)));
1322
1323            let value = attr.normalized_value(Explicit1_1).unwrap();
1324            assert_eq!(value, "");
1325            // assert_eq! does not check if value is borrowed, but this is important
1326            assert!(matches!(value, Cow::Borrowed(_)));
1327        }
1328
1329        /// Already normalized values are returned unchanged
1330        #[test]
1331        fn already_normalized() {
1332            let raw_value = "foobar123";
1333            let attr = Attribute {
1334                key: QName("foo"),
1335                value: Cow::Borrowed(raw_value),
1336            };
1337
1338            let value = attr.normalized_value(Implicit1_0).unwrap();
1339            assert_eq!(value, "foobar123");
1340            // assert_eq! does not check if value is borrowed, but this is important
1341            assert!(matches!(value, Cow::Borrowed(_)));
1342
1343            let value = attr.normalized_value(Explicit1_0).unwrap();
1344            assert_eq!(value, "foobar123");
1345            // assert_eq! does not check if value is borrowed, but this is important
1346            assert!(matches!(value, Cow::Borrowed(_)));
1347
1348            let value = attr.normalized_value(Explicit1_1).unwrap();
1349            assert_eq!(value, "foobar123");
1350            // assert_eq! does not check if value is borrowed, but this is important
1351            assert!(matches!(value, Cow::Borrowed(_)));
1352        }
1353
1354        /// Return, tab, and newline characters (0xD, 0x9, 0xA) must be substituted with
1355        /// a space character, \r\n and \r\u{85} should be replaced by one space in 1.1
1356        #[test]
1357        fn space_replacement() {
1358            let raw_value = "\r\nfoo\u{85}\u{2028}\rbar\tbaz\n\ndelta\n\r\u{85}";
1359            let attr = Attribute {
1360                key: QName("foo"),
1361                value: Cow::Borrowed(raw_value),
1362            };
1363
1364            assert_eq!(
1365                attr.normalized_value(Implicit1_0).unwrap(),
1366                " foo\u{85}\u{2028} bar baz  delta  \u{85}"
1367            );
1368            assert_eq!(
1369                attr.normalized_value(Explicit1_0).unwrap(),
1370                " foo\u{85}\u{2028} bar baz  delta  \u{85}"
1371            );
1372            assert_eq!(
1373                attr.normalized_value(Explicit1_1).unwrap(),
1374                " foo   bar baz  delta  "
1375            );
1376        }
1377
1378        /// Entities must be terminated
1379        #[test]
1380        fn unterminated_entity() {
1381            let raw_value = "abc&quotdef";
1382            let attr = Attribute {
1383                key: QName("foo"),
1384                value: Cow::Borrowed(raw_value),
1385            };
1386
1387            match attr.normalized_value(Implicit1_0) {
1388                Err(Error::Escape(err)) => assert_eq!(err, UnterminatedEntity(3..11)),
1389                x => panic!("Expected Err(Escape(_)), got {:?}", x),
1390            }
1391
1392            match attr.normalized_value(Explicit1_0) {
1393                Err(Error::Escape(err)) => assert_eq!(err, UnterminatedEntity(3..11)),
1394                x => panic!("Expected Err(Escape(_)), got {:?}", x),
1395            }
1396
1397            match attr.normalized_value(Explicit1_1) {
1398                Err(Error::Escape(err)) => assert_eq!(err, UnterminatedEntity(3..11)),
1399                x => panic!("Expected Err(Escape(_)), got {:?}", x),
1400            }
1401        }
1402
1403        /// Unknown entities raise error
1404        #[test]
1405        fn unrecognized_entity() {
1406            let raw_value = "abc&unkn;def";
1407            let attr = Attribute {
1408                key: QName("foo"),
1409                value: Cow::Borrowed(raw_value),
1410            };
1411
1412            match attr.normalized_value(Implicit1_0) {
1413                // TODO: is this divergence between range behavior of UnterminatedEntity
1414                // and UnrecognizedEntity appropriate? existing unescape code behaves the same.  (see: start index)
1415                Err(Error::Escape(err)) => {
1416                    assert_eq!(err, UnrecognizedEntity(4..8, "unkn".to_owned()))
1417                }
1418                x => panic!("Expected Err(Escape(err)), got {:?}", x),
1419            }
1420            match attr.normalized_value(Explicit1_0) {
1421                // TODO: is this divergence between range behavior of UnterminatedEntity
1422                // and UnrecognizedEntity appropriate? existing unescape code behaves the same.  (see: start index)
1423                Err(Error::Escape(err)) => {
1424                    assert_eq!(err, UnrecognizedEntity(4..8, "unkn".to_owned()))
1425                }
1426                x => panic!("Expected Err(Escape(err)), got {:?}", x),
1427            }
1428            match attr.normalized_value(Explicit1_1) {
1429                // TODO: is this divergence between range behavior of UnterminatedEntity
1430                // and UnrecognizedEntity appropriate? existing unescape code behaves the same.  (see: start index)
1431                Err(Error::Escape(err)) => {
1432                    assert_eq!(err, UnrecognizedEntity(4..8, "unkn".to_owned()))
1433                }
1434                x => panic!("Expected Err(Escape(err)), got {:?}", x),
1435            }
1436        }
1437
1438        /// custom entity replacement works, entity replacement text processed recursively
1439        #[test]
1440        fn entity_replacement() {
1441            let raw_value = "&d;&d;A&a;&#x20;&a;B&da;";
1442            let attr = Attribute {
1443                key: QName("foo"),
1444                value: Cow::Borrowed(raw_value),
1445            };
1446            fn custom_resolver(ent: &str) -> Option<&'static str> {
1447                match ent {
1448                    "d" => Some("&#xD;"),
1449                    "a" => Some("&#xA;"),
1450                    "da" => Some("&#xD;&#xA;"),
1451                    _ => None,
1452                }
1453            }
1454
1455            assert_eq!(
1456                attr.normalized_value_with(Implicit1_0, 5, &custom_resolver)
1457                    .unwrap(),
1458                "\r\rA\n \nB\r\n"
1459            );
1460            assert_eq!(
1461                attr.normalized_value_with(Explicit1_0, 5, &custom_resolver)
1462                    .unwrap(),
1463                "\r\rA\n \nB\r\n"
1464            );
1465            assert_eq!(
1466                attr.normalized_value_with(Explicit1_1, 5, &custom_resolver)
1467                    .unwrap(),
1468                "\r\rA\n \nB\r\n"
1469            );
1470        }
1471
1472        #[test]
1473        fn char_references() {
1474            // character literal references are substituted without being replaced by spaces
1475            let raw_value = "&#xd;&#xd;A&#xa;&#xa;B&#xd;&#xa;";
1476            let attr = Attribute {
1477                key: QName("foo"),
1478                value: Cow::Borrowed(raw_value),
1479            };
1480
1481            assert_eq!(
1482                attr.normalized_value(Implicit1_0).unwrap(),
1483                "\r\rA\n\nB\r\n"
1484            );
1485            assert_eq!(
1486                attr.normalized_value(Explicit1_0).unwrap(),
1487                "\r\rA\n\nB\r\n"
1488            );
1489            assert_eq!(
1490                attr.normalized_value(Explicit1_1).unwrap(),
1491                "\r\rA\n\nB\r\n"
1492            );
1493        }
1494    }
1495
1496    /// Checked attribute is the single attribute
1497    mod single {
1498        use super::*;
1499        use pretty_assertions::assert_eq;
1500
1501        /// Attribute have a value enclosed in single quotes
1502        #[test]
1503        fn single_quoted() {
1504            let mut iter = Attributes::new(r#"tag key='value'"#, 3);
1505
1506            assert_eq!(
1507                iter.next(),
1508                Some(Ok(Attribute {
1509                    key: QName("key"),
1510                    value: Cow::Borrowed("value"),
1511                }))
1512            );
1513            assert_eq!(iter.next(), None);
1514            assert_eq!(iter.next(), None);
1515        }
1516
1517        /// Attribute have a value enclosed in double quotes
1518        #[test]
1519        fn double_quoted() {
1520            let mut iter = Attributes::new(r#"tag key="value""#, 3);
1521
1522            assert_eq!(
1523                iter.next(),
1524                Some(Ok(Attribute {
1525                    key: QName("key"),
1526                    value: Cow::Borrowed("value"),
1527                }))
1528            );
1529            assert_eq!(iter.next(), None);
1530            assert_eq!(iter.next(), None);
1531        }
1532
1533        /// Attribute have a value, not enclosed in quotes
1534        #[test]
1535        fn unquoted() {
1536            let mut iter = Attributes::new(r#"tag key=value"#, 3);
1537            //                                0       ^ = 8
1538
1539            assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(8))));
1540            assert_eq!(iter.next(), None);
1541            assert_eq!(iter.next(), None);
1542        }
1543
1544        /// Only attribute key is present
1545        #[test]
1546        fn key_only() {
1547            let mut iter = Attributes::new(r#"tag key"#, 3);
1548            //                                0      ^ = 7
1549
1550            assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(7))));
1551            assert_eq!(iter.next(), None);
1552            assert_eq!(iter.next(), None);
1553        }
1554
1555        /// Key is started with an invalid symbol (a single quote in this test).
1556        /// Because we do not check validity of keys and values during parsing,
1557        /// that invalid attribute will be returned
1558        #[test]
1559        fn key_start_invalid() {
1560            let mut iter = Attributes::new(r#"tag 'key'='value'"#, 3);
1561
1562            assert_eq!(
1563                iter.next(),
1564                Some(Ok(Attribute {
1565                    key: QName("'key'"),
1566                    value: Cow::Borrowed("value"),
1567                }))
1568            );
1569            assert_eq!(iter.next(), None);
1570            assert_eq!(iter.next(), None);
1571        }
1572
1573        /// Key contains an invalid symbol (an ampersand in this test).
1574        /// Because we do not check validity of keys and values during parsing,
1575        /// that invalid attribute will be returned
1576        #[test]
1577        fn key_contains_invalid() {
1578            let mut iter = Attributes::new(r#"tag key&jey='value'"#, 3);
1579
1580            assert_eq!(
1581                iter.next(),
1582                Some(Ok(Attribute {
1583                    key: QName("key&jey"),
1584                    value: Cow::Borrowed("value"),
1585                }))
1586            );
1587            assert_eq!(iter.next(), None);
1588            assert_eq!(iter.next(), None);
1589        }
1590
1591        /// Attribute value is missing after `=`
1592        #[test]
1593        fn missed_value() {
1594            let mut iter = Attributes::new(r#"tag key="#, 3);
1595            //                                0       ^ = 8
1596
1597            assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(8))));
1598            assert_eq!(iter.next(), None);
1599            assert_eq!(iter.next(), None);
1600        }
1601    }
1602
1603    /// Checked attribute is the first attribute in the list of many attributes
1604    mod first {
1605        use super::*;
1606        use pretty_assertions::assert_eq;
1607
1608        /// Attribute have a value enclosed in single quotes
1609        #[test]
1610        fn single_quoted() {
1611            let mut iter = Attributes::new(r#"tag key='value' regular='attribute'"#, 3);
1612
1613            assert_eq!(
1614                iter.next(),
1615                Some(Ok(Attribute {
1616                    key: QName("key"),
1617                    value: Cow::Borrowed("value"),
1618                }))
1619            );
1620            assert_eq!(
1621                iter.next(),
1622                Some(Ok(Attribute {
1623                    key: QName("regular"),
1624                    value: Cow::Borrowed("attribute"),
1625                }))
1626            );
1627            assert_eq!(iter.next(), None);
1628            assert_eq!(iter.next(), None);
1629        }
1630
1631        /// Attribute have a value enclosed in double quotes
1632        #[test]
1633        fn double_quoted() {
1634            let mut iter = Attributes::new(r#"tag key="value" regular='attribute'"#, 3);
1635
1636            assert_eq!(
1637                iter.next(),
1638                Some(Ok(Attribute {
1639                    key: QName("key"),
1640                    value: Cow::Borrowed("value"),
1641                }))
1642            );
1643            assert_eq!(
1644                iter.next(),
1645                Some(Ok(Attribute {
1646                    key: QName("regular"),
1647                    value: Cow::Borrowed("attribute"),
1648                }))
1649            );
1650            assert_eq!(iter.next(), None);
1651            assert_eq!(iter.next(), None);
1652        }
1653
1654        /// Attribute have a value, not enclosed in quotes
1655        #[test]
1656        fn unquoted() {
1657            let mut iter = Attributes::new(r#"tag key=value regular='attribute'"#, 3);
1658            //                                0       ^ = 8
1659
1660            assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(8))));
1661            // check error recovery
1662            assert_eq!(
1663                iter.next(),
1664                Some(Ok(Attribute {
1665                    key: QName("regular"),
1666                    value: Cow::Borrowed("attribute"),
1667                }))
1668            );
1669            assert_eq!(iter.next(), None);
1670            assert_eq!(iter.next(), None);
1671        }
1672
1673        /// Only attribute key is present
1674        #[test]
1675        fn key_only() {
1676            let mut iter = Attributes::new(r#"tag key regular='attribute'"#, 3);
1677            //                                0       ^ = 8
1678
1679            assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(8))));
1680            // check error recovery
1681            assert_eq!(
1682                iter.next(),
1683                Some(Ok(Attribute {
1684                    key: QName("regular"),
1685                    value: Cow::Borrowed("attribute"),
1686                }))
1687            );
1688            assert_eq!(iter.next(), None);
1689            assert_eq!(iter.next(), None);
1690        }
1691
1692        /// Key is started with an invalid symbol (a single quote in this test).
1693        /// Because we do not check validity of keys and values during parsing,
1694        /// that invalid attribute will be returned
1695        #[test]
1696        fn key_start_invalid() {
1697            let mut iter = Attributes::new(r#"tag 'key'='value' regular='attribute'"#, 3);
1698
1699            assert_eq!(
1700                iter.next(),
1701                Some(Ok(Attribute {
1702                    key: QName("'key'"),
1703                    value: Cow::Borrowed("value"),
1704                }))
1705            );
1706            assert_eq!(
1707                iter.next(),
1708                Some(Ok(Attribute {
1709                    key: QName("regular"),
1710                    value: Cow::Borrowed("attribute"),
1711                }))
1712            );
1713            assert_eq!(iter.next(), None);
1714            assert_eq!(iter.next(), None);
1715        }
1716
1717        /// Key contains an invalid symbol (an ampersand in this test).
1718        /// Because we do not check validity of keys and values during parsing,
1719        /// that invalid attribute will be returned
1720        #[test]
1721        fn key_contains_invalid() {
1722            let mut iter = Attributes::new(r#"tag key&jey='value' regular='attribute'"#, 3);
1723
1724            assert_eq!(
1725                iter.next(),
1726                Some(Ok(Attribute {
1727                    key: QName("key&jey"),
1728                    value: Cow::Borrowed("value"),
1729                }))
1730            );
1731            assert_eq!(
1732                iter.next(),
1733                Some(Ok(Attribute {
1734                    key: QName("regular"),
1735                    value: Cow::Borrowed("attribute"),
1736                }))
1737            );
1738            assert_eq!(iter.next(), None);
1739            assert_eq!(iter.next(), None);
1740        }
1741
1742        /// Attribute value is missing after `=`.
1743        #[test]
1744        fn missed_value() {
1745            let mut iter = Attributes::new(r#"tag key= regular='attribute'"#, 3);
1746            //                                0        ^ = 9
1747
1748            assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1749            // Because we do not check validity of keys and values during parsing,
1750            // "error='recovery'" is considered, as unquoted attribute value and
1751            // skipped during recovery and iteration finished
1752            assert_eq!(iter.next(), None);
1753            assert_eq!(iter.next(), None);
1754
1755            ////////////////////////////////////////////////////////////////////
1756
1757            let mut iter = Attributes::new(r#"tag key= regular= 'attribute'"#, 3);
1758            //                                0        ^ = 9               ^ = 29
1759
1760            // In that case "regular=" considered as unquoted value
1761            assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1762            // In that case "'attribute'" considered as a key, because we do not check
1763            // validity of key names
1764            assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(29))));
1765            assert_eq!(iter.next(), None);
1766            assert_eq!(iter.next(), None);
1767
1768            ////////////////////////////////////////////////////////////////////
1769
1770            let mut iter = Attributes::new(r#"tag key= regular ='attribute'"#, 3);
1771            //                                0        ^ = 9               ^ = 29
1772
1773            // In that case "regular" considered as unquoted value
1774            assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1775            // In that case "='attribute'" considered as a key, because we do not check
1776            // validity of key names
1777            assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(29))));
1778            assert_eq!(iter.next(), None);
1779            assert_eq!(iter.next(), None);
1780
1781            ////////////////////////////////////////////////////////////////////
1782
1783            let mut iter = Attributes::new(r#"tag key= regular = 'attribute'"#, 3);
1784            //                                0        ^ = 9     ^ = 19     ^ = 30
1785
1786            assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1787            // In that case second "=" considered as a key, because we do not check
1788            // validity of key names
1789            assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(19))));
1790            // In that case "'attribute'" considered as a key, because we do not check
1791            // validity of key names
1792            assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(30))));
1793            assert_eq!(iter.next(), None);
1794            assert_eq!(iter.next(), None);
1795        }
1796    }
1797
1798    /// Copy of single, but with additional spaces in markup
1799    mod sparsed {
1800        use super::*;
1801        use pretty_assertions::assert_eq;
1802
1803        /// Attribute have a value enclosed in single quotes
1804        #[test]
1805        fn single_quoted() {
1806            let mut iter = Attributes::new(r#"tag key = 'value' "#, 3);
1807
1808            assert_eq!(
1809                iter.next(),
1810                Some(Ok(Attribute {
1811                    key: QName("key"),
1812                    value: Cow::Borrowed("value"),
1813                }))
1814            );
1815            assert_eq!(iter.next(), None);
1816            assert_eq!(iter.next(), None);
1817        }
1818
1819        /// Attribute have a value enclosed in double quotes
1820        #[test]
1821        fn double_quoted() {
1822            let mut iter = Attributes::new(r#"tag key = "value" "#, 3);
1823
1824            assert_eq!(
1825                iter.next(),
1826                Some(Ok(Attribute {
1827                    key: QName("key"),
1828                    value: Cow::Borrowed("value"),
1829                }))
1830            );
1831            assert_eq!(iter.next(), None);
1832            assert_eq!(iter.next(), None);
1833        }
1834
1835        /// Attribute have a value, not enclosed in quotes
1836        #[test]
1837        fn unquoted() {
1838            let mut iter = Attributes::new(r#"tag key = value "#, 3);
1839            //                                0         ^ = 10
1840
1841            assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(10))));
1842            assert_eq!(iter.next(), None);
1843            assert_eq!(iter.next(), None);
1844        }
1845
1846        /// Only attribute key is present
1847        #[test]
1848        fn key_only() {
1849            let mut iter = Attributes::new(r#"tag key "#, 3);
1850            //                                0       ^ = 8
1851
1852            assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(8))));
1853            assert_eq!(iter.next(), None);
1854            assert_eq!(iter.next(), None);
1855        }
1856
1857        /// Key is started with an invalid symbol (a single quote in this test).
1858        /// Because we do not check validity of keys and values during parsing,
1859        /// that invalid attribute will be returned
1860        #[test]
1861        fn key_start_invalid() {
1862            let mut iter = Attributes::new(r#"tag 'key' = 'value' "#, 3);
1863
1864            assert_eq!(
1865                iter.next(),
1866                Some(Ok(Attribute {
1867                    key: QName("'key'"),
1868                    value: Cow::Borrowed("value"),
1869                }))
1870            );
1871            assert_eq!(iter.next(), None);
1872            assert_eq!(iter.next(), None);
1873        }
1874
1875        /// Key contains an invalid symbol (an ampersand in this test).
1876        /// Because we do not check validity of keys and values during parsing,
1877        /// that invalid attribute will be returned
1878        #[test]
1879        fn key_contains_invalid() {
1880            let mut iter = Attributes::new(r#"tag key&jey = 'value' "#, 3);
1881
1882            assert_eq!(
1883                iter.next(),
1884                Some(Ok(Attribute {
1885                    key: QName("key&jey"),
1886                    value: Cow::Borrowed("value"),
1887                }))
1888            );
1889            assert_eq!(iter.next(), None);
1890            assert_eq!(iter.next(), None);
1891        }
1892
1893        /// Attribute value is missing after `=`
1894        #[test]
1895        fn missed_value() {
1896            let mut iter = Attributes::new(r#"tag key = "#, 3);
1897            //                                0         ^ = 10
1898
1899            assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(10))));
1900            assert_eq!(iter.next(), None);
1901            assert_eq!(iter.next(), None);
1902        }
1903    }
1904
1905    /// Checks that duplicated attributes correctly reported and recovering is
1906    /// possible after that
1907    mod duplicated {
1908        use super::*;
1909
1910        mod with_check {
1911            use super::*;
1912            use pretty_assertions::assert_eq;
1913
1914            /// Attribute have a value enclosed in single quotes
1915            #[test]
1916            fn single_quoted() {
1917                let mut iter = Attributes::new(r#"tag key='value' key='dup' another=''"#, 3);
1918                //                                0   ^ = 4       ^ = 16
1919
1920                assert_eq!(
1921                    iter.next(),
1922                    Some(Ok(Attribute {
1923                        key: QName("key"),
1924                        value: Cow::Borrowed("value"),
1925                    }))
1926                );
1927                assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
1928                assert_eq!(
1929                    iter.next(),
1930                    Some(Ok(Attribute {
1931                        key: QName("another"),
1932                        value: Cow::Borrowed(""),
1933                    }))
1934                );
1935                assert_eq!(iter.next(), None);
1936                assert_eq!(iter.next(), None);
1937            }
1938
1939            /// Attribute have a value enclosed in double quotes
1940            #[test]
1941            fn double_quoted() {
1942                let mut iter = Attributes::new(r#"tag key='value' key="dup" another=''"#, 3);
1943                //                                0   ^ = 4       ^ = 16
1944
1945                assert_eq!(
1946                    iter.next(),
1947                    Some(Ok(Attribute {
1948                        key: QName("key"),
1949                        value: Cow::Borrowed("value"),
1950                    }))
1951                );
1952                assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
1953                assert_eq!(
1954                    iter.next(),
1955                    Some(Ok(Attribute {
1956                        key: QName("another"),
1957                        value: Cow::Borrowed(""),
1958                    }))
1959                );
1960                assert_eq!(iter.next(), None);
1961                assert_eq!(iter.next(), None);
1962            }
1963
1964            /// Attribute have a value, not enclosed in quotes
1965            #[test]
1966            fn unquoted() {
1967                let mut iter = Attributes::new(r#"tag key='value' key=dup another=''"#, 3);
1968                //                                0   ^ = 4       ^ = 16
1969
1970                assert_eq!(
1971                    iter.next(),
1972                    Some(Ok(Attribute {
1973                        key: QName("key"),
1974                        value: Cow::Borrowed("value"),
1975                    }))
1976                );
1977                assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
1978                assert_eq!(
1979                    iter.next(),
1980                    Some(Ok(Attribute {
1981                        key: QName("another"),
1982                        value: Cow::Borrowed(""),
1983                    }))
1984                );
1985                assert_eq!(iter.next(), None);
1986                assert_eq!(iter.next(), None);
1987            }
1988
1989            /// Only attribute key is present
1990            #[test]
1991            fn key_only() {
1992                let mut iter = Attributes::new(r#"tag key='value' key another=''"#, 3);
1993                //                                0                   ^ = 20
1994
1995                assert_eq!(
1996                    iter.next(),
1997                    Some(Ok(Attribute {
1998                        key: QName("key"),
1999                        value: Cow::Borrowed("value"),
2000                    }))
2001                );
2002                assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(20))));
2003                assert_eq!(
2004                    iter.next(),
2005                    Some(Ok(Attribute {
2006                        key: QName("another"),
2007                        value: Cow::Borrowed(""),
2008                    }))
2009                );
2010                assert_eq!(iter.next(), None);
2011                assert_eq!(iter.next(), None);
2012            }
2013
2014            /// Once a start tag declares more than `SMALL_ATTRIBUTE_COUNT`
2015            /// attributes the duplicate check switches to its hash-based path. A
2016            /// duplicate of a name first seen during the earlier linear phase must
2017            /// still be detected, with the original position reported. Regression
2018            /// cover for the cold path of [#969].
2019            ///
2020            /// [#969]: https://github.com/tafia/quick-xml/issues/969
2021            #[test]
2022            fn duplicate_past_hash_threshold() {
2023                let dup = SMALL_ATTRIBUTE_COUNT / 2;
2024                let n = SMALL_ATTRIBUTE_COUNT + 8;
2025
2026                let mut source = String::from("tag");
2027                let mut positions = Vec::with_capacity(n);
2028                for i in 0..n {
2029                    source.push(' ');
2030                    positions.push(source.len());
2031                    source.push_str(&format!("k{:04}=''", i));
2032                }
2033                // Repeat the name first seen at `positions[dup]` (linear phase).
2034                source.push(' ');
2035                let dup_pos = source.len();
2036                source.push_str(&format!("k{:04}=''", dup));
2037
2038                let mut iter = Attributes::new(&source, 3);
2039                for _ in 0..n {
2040                    assert!(matches!(iter.next(), Some(Ok(_))));
2041                }
2042                assert_eq!(
2043                    iter.next(),
2044                    Some(Err(AttrError::Duplicated(dup_pos, positions[dup])))
2045                );
2046            }
2047        }
2048
2049        /// Check for duplicated names is disabled
2050        mod without_check {
2051            use super::*;
2052            use pretty_assertions::assert_eq;
2053
2054            /// Attribute have a value enclosed in single quotes
2055            #[test]
2056            fn single_quoted() {
2057                let mut iter = Attributes::new(r#"tag key='value' key='dup' another=''"#, 3);
2058                iter.with_checks(false);
2059
2060                assert_eq!(
2061                    iter.next(),
2062                    Some(Ok(Attribute {
2063                        key: QName("key"),
2064                        value: Cow::Borrowed("value"),
2065                    }))
2066                );
2067                assert_eq!(
2068                    iter.next(),
2069                    Some(Ok(Attribute {
2070                        key: QName("key"),
2071                        value: Cow::Borrowed("dup"),
2072                    }))
2073                );
2074                assert_eq!(
2075                    iter.next(),
2076                    Some(Ok(Attribute {
2077                        key: QName("another"),
2078                        value: Cow::Borrowed(""),
2079                    }))
2080                );
2081                assert_eq!(iter.next(), None);
2082                assert_eq!(iter.next(), None);
2083            }
2084
2085            /// Attribute have a value enclosed in double quotes
2086            #[test]
2087            fn double_quoted() {
2088                let mut iter = Attributes::new(r#"tag key='value' key="dup" another=''"#, 3);
2089                iter.with_checks(false);
2090
2091                assert_eq!(
2092                    iter.next(),
2093                    Some(Ok(Attribute {
2094                        key: QName("key"),
2095                        value: Cow::Borrowed("value"),
2096                    }))
2097                );
2098                assert_eq!(
2099                    iter.next(),
2100                    Some(Ok(Attribute {
2101                        key: QName("key"),
2102                        value: Cow::Borrowed("dup"),
2103                    }))
2104                );
2105                assert_eq!(
2106                    iter.next(),
2107                    Some(Ok(Attribute {
2108                        key: QName("another"),
2109                        value: Cow::Borrowed(""),
2110                    }))
2111                );
2112                assert_eq!(iter.next(), None);
2113                assert_eq!(iter.next(), None);
2114            }
2115
2116            /// Attribute have a value, not enclosed in quotes
2117            #[test]
2118            fn unquoted() {
2119                let mut iter = Attributes::new(r#"tag key='value' key=dup another=''"#, 3);
2120                //                                0                   ^ = 20
2121                iter.with_checks(false);
2122
2123                assert_eq!(
2124                    iter.next(),
2125                    Some(Ok(Attribute {
2126                        key: QName("key"),
2127                        value: Cow::Borrowed("value"),
2128                    }))
2129                );
2130                assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(20))));
2131                assert_eq!(
2132                    iter.next(),
2133                    Some(Ok(Attribute {
2134                        key: QName("another"),
2135                        value: Cow::Borrowed(""),
2136                    }))
2137                );
2138                assert_eq!(iter.next(), None);
2139                assert_eq!(iter.next(), None);
2140            }
2141
2142            /// Only attribute key is present
2143            #[test]
2144            fn key_only() {
2145                let mut iter = Attributes::new(r#"tag key='value' key another=''"#, 3);
2146                //                                0                   ^ = 20
2147                iter.with_checks(false);
2148
2149                assert_eq!(
2150                    iter.next(),
2151                    Some(Ok(Attribute {
2152                        key: QName("key"),
2153                        value: Cow::Borrowed("value"),
2154                    }))
2155                );
2156                assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(20))));
2157                assert_eq!(
2158                    iter.next(),
2159                    Some(Ok(Attribute {
2160                        key: QName("another"),
2161                        value: Cow::Borrowed(""),
2162                    }))
2163                );
2164                assert_eq!(iter.next(), None);
2165                assert_eq!(iter.next(), None);
2166            }
2167        }
2168    }
2169
2170    #[test]
2171    fn mixed_quote() {
2172        let mut iter = Attributes::new(r#"tag a='a' b = "b" c='cc"cc' d="dd'dd""#, 3);
2173
2174        assert_eq!(
2175            iter.next(),
2176            Some(Ok(Attribute {
2177                key: QName("a"),
2178                value: Cow::Borrowed("a"),
2179            }))
2180        );
2181        assert_eq!(
2182            iter.next(),
2183            Some(Ok(Attribute {
2184                key: QName("b"),
2185                value: Cow::Borrowed("b"),
2186            }))
2187        );
2188        assert_eq!(
2189            iter.next(),
2190            Some(Ok(Attribute {
2191                key: QName("c"),
2192                value: Cow::Borrowed(r#"cc"cc"#),
2193            }))
2194        );
2195        assert_eq!(
2196            iter.next(),
2197            Some(Ok(Attribute {
2198                key: QName("d"),
2199                value: Cow::Borrowed("dd'dd"),
2200            }))
2201        );
2202        assert_eq!(iter.next(), None);
2203        assert_eq!(iter.next(), None);
2204    }
2205}
2206
2207/// Checks, how parsing of HTML-style attributes works. Each attribute can be
2208/// in three forms:
2209/// - XML-like: have a value, enclosed in single or double quotes
2210/// - have a value, do not enclosed in quotes
2211/// - without value, key only
2212#[cfg(test)]
2213mod html {
2214    use super::*;
2215    use pretty_assertions::assert_eq;
2216
2217    /// Checked attribute is the single attribute
2218    mod single {
2219        use super::*;
2220        use pretty_assertions::assert_eq;
2221
2222        /// Attribute have a value enclosed in single quotes
2223        #[test]
2224        fn single_quoted() {
2225            let mut iter = Attributes::html(r#"tag key='value'"#, 3);
2226
2227            assert_eq!(
2228                iter.next(),
2229                Some(Ok(Attribute {
2230                    key: QName("key"),
2231                    value: Cow::Borrowed("value"),
2232                }))
2233            );
2234            assert_eq!(iter.next(), None);
2235            assert_eq!(iter.next(), None);
2236        }
2237
2238        /// Attribute have a value enclosed in double quotes
2239        #[test]
2240        fn double_quoted() {
2241            let mut iter = Attributes::html(r#"tag key="value""#, 3);
2242
2243            assert_eq!(
2244                iter.next(),
2245                Some(Ok(Attribute {
2246                    key: QName("key"),
2247                    value: Cow::Borrowed("value"),
2248                }))
2249            );
2250            assert_eq!(iter.next(), None);
2251            assert_eq!(iter.next(), None);
2252        }
2253
2254        /// Attribute have a value, not enclosed in quotes
2255        #[test]
2256        fn unquoted() {
2257            let mut iter = Attributes::html(r#"tag key=value"#, 3);
2258
2259            assert_eq!(
2260                iter.next(),
2261                Some(Ok(Attribute {
2262                    key: QName("key"),
2263                    value: Cow::Borrowed("value"),
2264                }))
2265            );
2266            assert_eq!(iter.next(), None);
2267            assert_eq!(iter.next(), None);
2268        }
2269
2270        /// Only attribute key is present
2271        #[test]
2272        fn key_only() {
2273            let mut iter = Attributes::html(r#"tag key"#, 3);
2274
2275            assert_eq!(
2276                iter.next(),
2277                Some(Ok(Attribute {
2278                    key: QName("key"),
2279                    value: Cow::Borrowed(""),
2280                }))
2281            );
2282            assert_eq!(iter.next(), None);
2283            assert_eq!(iter.next(), None);
2284        }
2285
2286        /// Key is started with an invalid symbol (a single quote in this test).
2287        /// Because we do not check validity of keys and values during parsing,
2288        /// that invalid attribute will be returned
2289        #[test]
2290        fn key_start_invalid() {
2291            let mut iter = Attributes::html(r#"tag 'key'='value'"#, 3);
2292
2293            assert_eq!(
2294                iter.next(),
2295                Some(Ok(Attribute {
2296                    key: QName("'key'"),
2297                    value: Cow::Borrowed("value"),
2298                }))
2299            );
2300            assert_eq!(iter.next(), None);
2301            assert_eq!(iter.next(), None);
2302        }
2303
2304        /// Key contains an invalid symbol (an ampersand in this test).
2305        /// Because we do not check validity of keys and values during parsing,
2306        /// that invalid attribute will be returned
2307        #[test]
2308        fn key_contains_invalid() {
2309            let mut iter = Attributes::html(r#"tag key&jey='value'"#, 3);
2310
2311            assert_eq!(
2312                iter.next(),
2313                Some(Ok(Attribute {
2314                    key: QName("key&jey"),
2315                    value: Cow::Borrowed("value"),
2316                }))
2317            );
2318            assert_eq!(iter.next(), None);
2319            assert_eq!(iter.next(), None);
2320        }
2321
2322        /// Attribute value is missing after `=`
2323        #[test]
2324        fn missed_value() {
2325            let mut iter = Attributes::html(r#"tag key="#, 3);
2326            //                                0       ^ = 8
2327
2328            assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(8))));
2329            assert_eq!(iter.next(), None);
2330            assert_eq!(iter.next(), None);
2331        }
2332    }
2333
2334    /// Checked attribute is the first attribute in the list of many attributes
2335    mod first {
2336        use super::*;
2337        use pretty_assertions::assert_eq;
2338
2339        /// Attribute have a value enclosed in single quotes
2340        #[test]
2341        fn single_quoted() {
2342            let mut iter = Attributes::html(r#"tag key='value' regular='attribute'"#, 3);
2343
2344            assert_eq!(
2345                iter.next(),
2346                Some(Ok(Attribute {
2347                    key: QName("key"),
2348                    value: Cow::Borrowed("value"),
2349                }))
2350            );
2351            assert_eq!(
2352                iter.next(),
2353                Some(Ok(Attribute {
2354                    key: QName("regular"),
2355                    value: Cow::Borrowed("attribute"),
2356                }))
2357            );
2358            assert_eq!(iter.next(), None);
2359            assert_eq!(iter.next(), None);
2360        }
2361
2362        /// Attribute have a value enclosed in double quotes
2363        #[test]
2364        fn double_quoted() {
2365            let mut iter = Attributes::html(r#"tag key="value" regular='attribute'"#, 3);
2366
2367            assert_eq!(
2368                iter.next(),
2369                Some(Ok(Attribute {
2370                    key: QName("key"),
2371                    value: Cow::Borrowed("value"),
2372                }))
2373            );
2374            assert_eq!(
2375                iter.next(),
2376                Some(Ok(Attribute {
2377                    key: QName("regular"),
2378                    value: Cow::Borrowed("attribute"),
2379                }))
2380            );
2381            assert_eq!(iter.next(), None);
2382            assert_eq!(iter.next(), None);
2383        }
2384
2385        /// Attribute have a value, not enclosed in quotes
2386        #[test]
2387        fn unquoted() {
2388            let mut iter = Attributes::html(r#"tag key=value regular='attribute'"#, 3);
2389
2390            assert_eq!(
2391                iter.next(),
2392                Some(Ok(Attribute {
2393                    key: QName("key"),
2394                    value: Cow::Borrowed("value"),
2395                }))
2396            );
2397            assert_eq!(
2398                iter.next(),
2399                Some(Ok(Attribute {
2400                    key: QName("regular"),
2401                    value: Cow::Borrowed("attribute"),
2402                }))
2403            );
2404            assert_eq!(iter.next(), None);
2405            assert_eq!(iter.next(), None);
2406        }
2407
2408        /// Only attribute key is present
2409        #[test]
2410        fn key_only() {
2411            let mut iter = Attributes::html(r#"tag key regular='attribute'"#, 3);
2412
2413            assert_eq!(
2414                iter.next(),
2415                Some(Ok(Attribute {
2416                    key: QName("key"),
2417                    value: Cow::Borrowed(""),
2418                }))
2419            );
2420            assert_eq!(
2421                iter.next(),
2422                Some(Ok(Attribute {
2423                    key: QName("regular"),
2424                    value: Cow::Borrowed("attribute"),
2425                }))
2426            );
2427            assert_eq!(iter.next(), None);
2428            assert_eq!(iter.next(), None);
2429        }
2430
2431        /// Key is started with an invalid symbol (a single quote in this test).
2432        /// Because we do not check validity of keys and values during parsing,
2433        /// that invalid attribute will be returned
2434        #[test]
2435        fn key_start_invalid() {
2436            let mut iter = Attributes::html(r#"tag 'key'='value' regular='attribute'"#, 3);
2437
2438            assert_eq!(
2439                iter.next(),
2440                Some(Ok(Attribute {
2441                    key: QName("'key'"),
2442                    value: Cow::Borrowed("value"),
2443                }))
2444            );
2445            assert_eq!(
2446                iter.next(),
2447                Some(Ok(Attribute {
2448                    key: QName("regular"),
2449                    value: Cow::Borrowed("attribute"),
2450                }))
2451            );
2452            assert_eq!(iter.next(), None);
2453            assert_eq!(iter.next(), None);
2454        }
2455
2456        /// Key contains an invalid symbol (an ampersand in this test).
2457        /// Because we do not check validity of keys and values during parsing,
2458        /// that invalid attribute will be returned
2459        #[test]
2460        fn key_contains_invalid() {
2461            let mut iter = Attributes::html(r#"tag key&jey='value' regular='attribute'"#, 3);
2462
2463            assert_eq!(
2464                iter.next(),
2465                Some(Ok(Attribute {
2466                    key: QName("key&jey"),
2467                    value: Cow::Borrowed("value"),
2468                }))
2469            );
2470            assert_eq!(
2471                iter.next(),
2472                Some(Ok(Attribute {
2473                    key: QName("regular"),
2474                    value: Cow::Borrowed("attribute"),
2475                }))
2476            );
2477            assert_eq!(iter.next(), None);
2478            assert_eq!(iter.next(), None);
2479        }
2480
2481        /// Attribute value is missing after `=`
2482        #[test]
2483        fn missed_value() {
2484            let mut iter = Attributes::html(r#"tag key= regular='attribute'"#, 3);
2485
2486            // Because we do not check validity of keys and values during parsing,
2487            // "regular='attribute'" is considered as unquoted attribute value
2488            assert_eq!(
2489                iter.next(),
2490                Some(Ok(Attribute {
2491                    key: QName("key"),
2492                    value: Cow::Borrowed("regular='attribute'"),
2493                }))
2494            );
2495            assert_eq!(iter.next(), None);
2496            assert_eq!(iter.next(), None);
2497
2498            ////////////////////////////////////////////////////////////////////
2499
2500            let mut iter = Attributes::html(r#"tag key= regular= 'attribute'"#, 3);
2501
2502            // Because we do not check validity of keys and values during parsing,
2503            // "regular=" is considered as unquoted attribute value
2504            assert_eq!(
2505                iter.next(),
2506                Some(Ok(Attribute {
2507                    key: QName("key"),
2508                    value: Cow::Borrowed("regular="),
2509                }))
2510            );
2511            // Because we do not check validity of keys and values during parsing,
2512            // "'attribute'" is considered as key-only attribute
2513            assert_eq!(
2514                iter.next(),
2515                Some(Ok(Attribute {
2516                    key: QName("'attribute'"),
2517                    value: Cow::Borrowed(""),
2518                }))
2519            );
2520            assert_eq!(iter.next(), None);
2521            assert_eq!(iter.next(), None);
2522
2523            ////////////////////////////////////////////////////////////////////
2524
2525            let mut iter = Attributes::html(r#"tag key= regular ='attribute'"#, 3);
2526
2527            // Because we do not check validity of keys and values during parsing,
2528            // "regular" is considered as unquoted attribute value
2529            assert_eq!(
2530                iter.next(),
2531                Some(Ok(Attribute {
2532                    key: QName("key"),
2533                    value: Cow::Borrowed("regular"),
2534                }))
2535            );
2536            // Because we do not check validity of keys and values during parsing,
2537            // "='attribute'" is considered as key-only attribute
2538            assert_eq!(
2539                iter.next(),
2540                Some(Ok(Attribute {
2541                    key: QName("='attribute'"),
2542                    value: Cow::Borrowed(""),
2543                }))
2544            );
2545            assert_eq!(iter.next(), None);
2546            assert_eq!(iter.next(), None);
2547
2548            ////////////////////////////////////////////////////////////////////
2549
2550            let mut iter = Attributes::html(r#"tag key= regular = 'attribute'"#, 3);
2551            //                                 0        ^ = 9     ^ = 19     ^ = 30
2552
2553            // Because we do not check validity of keys and values during parsing,
2554            // "regular" is considered as unquoted attribute value
2555            assert_eq!(
2556                iter.next(),
2557                Some(Ok(Attribute {
2558                    key: QName("key"),
2559                    value: Cow::Borrowed("regular"),
2560                }))
2561            );
2562            // Because we do not check validity of keys and values during parsing,
2563            // "=" is considered as key-only attribute
2564            assert_eq!(
2565                iter.next(),
2566                Some(Ok(Attribute {
2567                    key: QName("="),
2568                    value: Cow::Borrowed(""),
2569                }))
2570            );
2571            // Because we do not check validity of keys and values during parsing,
2572            // "'attribute'" is considered as key-only attribute
2573            assert_eq!(
2574                iter.next(),
2575                Some(Ok(Attribute {
2576                    key: QName("'attribute'"),
2577                    value: Cow::Borrowed(""),
2578                }))
2579            );
2580            assert_eq!(iter.next(), None);
2581            assert_eq!(iter.next(), None);
2582        }
2583    }
2584
2585    /// Copy of single, but with additional spaces in markup
2586    mod sparsed {
2587        use super::*;
2588        use pretty_assertions::assert_eq;
2589
2590        /// Attribute have a value enclosed in single quotes
2591        #[test]
2592        fn single_quoted() {
2593            let mut iter = Attributes::html(r#"tag key = 'value' "#, 3);
2594
2595            assert_eq!(
2596                iter.next(),
2597                Some(Ok(Attribute {
2598                    key: QName("key"),
2599                    value: Cow::Borrowed("value"),
2600                }))
2601            );
2602            assert_eq!(iter.next(), None);
2603            assert_eq!(iter.next(), None);
2604        }
2605
2606        /// Attribute have a value enclosed in double quotes
2607        #[test]
2608        fn double_quoted() {
2609            let mut iter = Attributes::html(r#"tag key = "value" "#, 3);
2610
2611            assert_eq!(
2612                iter.next(),
2613                Some(Ok(Attribute {
2614                    key: QName("key"),
2615                    value: Cow::Borrowed("value"),
2616                }))
2617            );
2618            assert_eq!(iter.next(), None);
2619            assert_eq!(iter.next(), None);
2620        }
2621
2622        /// Attribute have a value, not enclosed in quotes
2623        #[test]
2624        fn unquoted() {
2625            let mut iter = Attributes::html(r#"tag key = value "#, 3);
2626
2627            assert_eq!(
2628                iter.next(),
2629                Some(Ok(Attribute {
2630                    key: QName("key"),
2631                    value: Cow::Borrowed("value"),
2632                }))
2633            );
2634            assert_eq!(iter.next(), None);
2635            assert_eq!(iter.next(), None);
2636        }
2637
2638        /// Only attribute key is present
2639        #[test]
2640        fn key_only() {
2641            let mut iter = Attributes::html(r#"tag key "#, 3);
2642
2643            assert_eq!(
2644                iter.next(),
2645                Some(Ok(Attribute {
2646                    key: QName("key"),
2647                    value: Cow::Borrowed(""),
2648                }))
2649            );
2650            assert_eq!(iter.next(), None);
2651            assert_eq!(iter.next(), None);
2652        }
2653
2654        /// Key is started with an invalid symbol (a single quote in this test).
2655        /// Because we do not check validity of keys and values during parsing,
2656        /// that invalid attribute will be returned
2657        #[test]
2658        fn key_start_invalid() {
2659            let mut iter = Attributes::html(r#"tag 'key' = 'value' "#, 3);
2660
2661            assert_eq!(
2662                iter.next(),
2663                Some(Ok(Attribute {
2664                    key: QName("'key'"),
2665                    value: Cow::Borrowed("value"),
2666                }))
2667            );
2668            assert_eq!(iter.next(), None);
2669            assert_eq!(iter.next(), None);
2670        }
2671
2672        /// Key contains an invalid symbol (an ampersand in this test).
2673        /// Because we do not check validity of keys and values during parsing,
2674        /// that invalid attribute will be returned
2675        #[test]
2676        fn key_contains_invalid() {
2677            let mut iter = Attributes::html(r#"tag key&jey = 'value' "#, 3);
2678
2679            assert_eq!(
2680                iter.next(),
2681                Some(Ok(Attribute {
2682                    key: QName("key&jey"),
2683                    value: Cow::Borrowed("value"),
2684                }))
2685            );
2686            assert_eq!(iter.next(), None);
2687            assert_eq!(iter.next(), None);
2688        }
2689
2690        /// Attribute value is missing after `=`
2691        #[test]
2692        fn missed_value() {
2693            let mut iter = Attributes::html(r#"tag key = "#, 3);
2694            //                                 0         ^ = 10
2695
2696            assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(10))));
2697            assert_eq!(iter.next(), None);
2698            assert_eq!(iter.next(), None);
2699        }
2700    }
2701
2702    /// Checks that duplicated attributes correctly reported and recovering is
2703    /// possible after that
2704    mod duplicated {
2705        use super::*;
2706
2707        mod with_check {
2708            use super::*;
2709            use pretty_assertions::assert_eq;
2710
2711            /// Attribute have a value enclosed in single quotes
2712            #[test]
2713            fn single_quoted() {
2714                let mut iter = Attributes::html(r#"tag key='value' key='dup' another=''"#, 3);
2715                //                                 0   ^ = 4       ^ = 16
2716
2717                assert_eq!(
2718                    iter.next(),
2719                    Some(Ok(Attribute {
2720                        key: QName("key"),
2721                        value: Cow::Borrowed("value"),
2722                    }))
2723                );
2724                assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2725                assert_eq!(
2726                    iter.next(),
2727                    Some(Ok(Attribute {
2728                        key: QName("another"),
2729                        value: Cow::Borrowed(""),
2730                    }))
2731                );
2732                assert_eq!(iter.next(), None);
2733                assert_eq!(iter.next(), None);
2734            }
2735
2736            /// Attribute have a value enclosed in double quotes
2737            #[test]
2738            fn double_quoted() {
2739                let mut iter = Attributes::html(r#"tag key='value' key="dup" another=''"#, 3);
2740                //                                 0   ^ = 4       ^ = 16
2741
2742                assert_eq!(
2743                    iter.next(),
2744                    Some(Ok(Attribute {
2745                        key: QName("key"),
2746                        value: Cow::Borrowed("value"),
2747                    }))
2748                );
2749                assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2750                assert_eq!(
2751                    iter.next(),
2752                    Some(Ok(Attribute {
2753                        key: QName("another"),
2754                        value: Cow::Borrowed(""),
2755                    }))
2756                );
2757                assert_eq!(iter.next(), None);
2758                assert_eq!(iter.next(), None);
2759            }
2760
2761            /// Attribute have a value, not enclosed in quotes
2762            #[test]
2763            fn unquoted() {
2764                let mut iter = Attributes::html(r#"tag key='value' key=dup another=''"#, 3);
2765                //                                 0   ^ = 4       ^ = 16
2766
2767                assert_eq!(
2768                    iter.next(),
2769                    Some(Ok(Attribute {
2770                        key: QName("key"),
2771                        value: Cow::Borrowed("value"),
2772                    }))
2773                );
2774                assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2775                assert_eq!(
2776                    iter.next(),
2777                    Some(Ok(Attribute {
2778                        key: QName("another"),
2779                        value: Cow::Borrowed(""),
2780                    }))
2781                );
2782                assert_eq!(iter.next(), None);
2783                assert_eq!(iter.next(), None);
2784            }
2785
2786            /// Only attribute key is present
2787            #[test]
2788            fn key_only() {
2789                let mut iter = Attributes::html(r#"tag key='value' key another=''"#, 3);
2790                //                                 0   ^ = 4       ^ = 16
2791
2792                assert_eq!(
2793                    iter.next(),
2794                    Some(Ok(Attribute {
2795                        key: QName("key"),
2796                        value: Cow::Borrowed("value"),
2797                    }))
2798                );
2799                assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2800                assert_eq!(
2801                    iter.next(),
2802                    Some(Ok(Attribute {
2803                        key: QName("another"),
2804                        value: Cow::Borrowed(""),
2805                    }))
2806                );
2807                assert_eq!(iter.next(), None);
2808                assert_eq!(iter.next(), None);
2809            }
2810        }
2811
2812        /// Check for duplicated names is disabled
2813        mod without_check {
2814            use super::*;
2815            use pretty_assertions::assert_eq;
2816
2817            /// Attribute have a value enclosed in single quotes
2818            #[test]
2819            fn single_quoted() {
2820                let mut iter = Attributes::html(r#"tag key='value' key='dup' another=''"#, 3);
2821                iter.with_checks(false);
2822
2823                assert_eq!(
2824                    iter.next(),
2825                    Some(Ok(Attribute {
2826                        key: QName("key"),
2827                        value: Cow::Borrowed("value"),
2828                    }))
2829                );
2830                assert_eq!(
2831                    iter.next(),
2832                    Some(Ok(Attribute {
2833                        key: QName("key"),
2834                        value: Cow::Borrowed("dup"),
2835                    }))
2836                );
2837                assert_eq!(
2838                    iter.next(),
2839                    Some(Ok(Attribute {
2840                        key: QName("another"),
2841                        value: Cow::Borrowed(""),
2842                    }))
2843                );
2844                assert_eq!(iter.next(), None);
2845                assert_eq!(iter.next(), None);
2846            }
2847
2848            /// Attribute have a value enclosed in double quotes
2849            #[test]
2850            fn double_quoted() {
2851                let mut iter = Attributes::html(r#"tag key='value' key="dup" another=''"#, 3);
2852                iter.with_checks(false);
2853
2854                assert_eq!(
2855                    iter.next(),
2856                    Some(Ok(Attribute {
2857                        key: QName("key"),
2858                        value: Cow::Borrowed("value"),
2859                    }))
2860                );
2861                assert_eq!(
2862                    iter.next(),
2863                    Some(Ok(Attribute {
2864                        key: QName("key"),
2865                        value: Cow::Borrowed("dup"),
2866                    }))
2867                );
2868                assert_eq!(
2869                    iter.next(),
2870                    Some(Ok(Attribute {
2871                        key: QName("another"),
2872                        value: Cow::Borrowed(""),
2873                    }))
2874                );
2875                assert_eq!(iter.next(), None);
2876                assert_eq!(iter.next(), None);
2877            }
2878
2879            /// Attribute have a value, not enclosed in quotes
2880            #[test]
2881            fn unquoted() {
2882                let mut iter = Attributes::html(r#"tag key='value' key=dup another=''"#, 3);
2883                iter.with_checks(false);
2884
2885                assert_eq!(
2886                    iter.next(),
2887                    Some(Ok(Attribute {
2888                        key: QName("key"),
2889                        value: Cow::Borrowed("value"),
2890                    }))
2891                );
2892                assert_eq!(
2893                    iter.next(),
2894                    Some(Ok(Attribute {
2895                        key: QName("key"),
2896                        value: Cow::Borrowed("dup"),
2897                    }))
2898                );
2899                assert_eq!(
2900                    iter.next(),
2901                    Some(Ok(Attribute {
2902                        key: QName("another"),
2903                        value: Cow::Borrowed(""),
2904                    }))
2905                );
2906                assert_eq!(iter.next(), None);
2907                assert_eq!(iter.next(), None);
2908            }
2909
2910            /// Only attribute key is present
2911            #[test]
2912            fn key_only() {
2913                let mut iter = Attributes::html(r#"tag key='value' key another=''"#, 3);
2914                iter.with_checks(false);
2915
2916                assert_eq!(
2917                    iter.next(),
2918                    Some(Ok(Attribute {
2919                        key: QName("key"),
2920                        value: Cow::Borrowed("value"),
2921                    }))
2922                );
2923                assert_eq!(
2924                    iter.next(),
2925                    Some(Ok(Attribute {
2926                        key: QName("key"),
2927                        value: Cow::Borrowed(""),
2928                    }))
2929                );
2930                assert_eq!(
2931                    iter.next(),
2932                    Some(Ok(Attribute {
2933                        key: QName("another"),
2934                        value: Cow::Borrowed(""),
2935                    }))
2936                );
2937                assert_eq!(iter.next(), None);
2938                assert_eq!(iter.next(), None);
2939            }
2940        }
2941    }
2942
2943    #[test]
2944    fn mixed_quote() {
2945        let mut iter = Attributes::html(r#"tag a='a' b = "b" c='cc"cc' d="dd'dd""#, 3);
2946
2947        assert_eq!(
2948            iter.next(),
2949            Some(Ok(Attribute {
2950                key: QName("a"),
2951                value: Cow::Borrowed("a"),
2952            }))
2953        );
2954        assert_eq!(
2955            iter.next(),
2956            Some(Ok(Attribute {
2957                key: QName("b"),
2958                value: Cow::Borrowed("b"),
2959            }))
2960        );
2961        assert_eq!(
2962            iter.next(),
2963            Some(Ok(Attribute {
2964                key: QName("c"),
2965                value: Cow::Borrowed(r#"cc"cc"#),
2966            }))
2967        );
2968        assert_eq!(
2969            iter.next(),
2970            Some(Ok(Attribute {
2971                key: QName("d"),
2972                value: Cow::Borrowed("dd'dd"),
2973            }))
2974        );
2975        assert_eq!(iter.next(), None);
2976        assert_eq!(iter.next(), None);
2977    }
2978}