Coverage Report

Created: 2026-11-12 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/quick-xml-0.42.0/src/events/attributes.rs
Line
Count
Source
1
//! Xml Attributes module
2
//!
3
//! Provides an iterator over attributes key/value pairs
4
5
use crate::XmlVersion;
6
use crate::encoding::Decoder;
7
use crate::errors::Result as XmlResult;
8
use crate::escape::{escape_attribute, resolve_predefined_entity};
9
use crate::name::{LocalName, Namespace, NamespaceResolver, QName};
10
use crate::utils::is_whitespace;
11
12
use std::collections::HashSet;
13
use std::fmt::{self, Debug, Display, Formatter};
14
use std::hash::{BuildHasherDefault, DefaultHasher, Hasher};
15
use std::iter::FusedIterator;
16
use 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)]
29
pub 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
38
impl<'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
0
    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
0
        self.normalized_value_with(version, 1, resolve_predefined_entity)
78
0
    }
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
0
    pub fn normalized_value_with<'entity>(
123
0
        &self,
124
0
        version: XmlVersion,
125
0
        depth: usize,
126
0
        resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
127
0
    ) -> XmlResult<Cow<'a, str>> {
128
0
        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
0
            Cow::Borrowed(_) => Ok(self.value.clone()),
131
0
            Cow::Owned(s) => Ok(s.into()),
132
        }
133
0
    }
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
0
    pub fn decoded_and_normalized_value(
182
0
        &self,
183
0
        version: XmlVersion,
184
0
        _decoder: Decoder,
185
0
    ) -> XmlResult<Cow<'a, str>> {
186
0
        self.normalized_value(version)
187
0
    }
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
0
    pub fn decoded_and_normalized_value_with<'entity>(
239
0
        &self,
240
0
        version: XmlVersion,
241
0
        _decoder: Decoder,
242
0
        depth: usize,
243
0
        resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
244
0
    ) -> XmlResult<Cow<'a, str>> {
245
0
        self.normalized_value_with(version, depth, resolve_entity)
246
0
    }
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
0
    pub fn unescape_value(&self) -> XmlResult<Cow<'a, str>> {
265
        // resolve_predefined_entity returns only non-recursive replacements, so depth=1 is enough
266
0
        self.normalized_value_with(XmlVersion::Implicit1_0, 1, resolve_predefined_entity)
267
0
    }
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
0
    pub fn unescape_value_with<'entity>(
288
0
        &self,
289
0
        resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
290
0
    ) -> XmlResult<Cow<'a, str>> {
291
0
        self.normalized_value_with(XmlVersion::Implicit1_0, 128, resolve_entity)
292
0
    }
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
0
    pub fn decode_and_unescape_value(&self, _decoder: Decoder) -> XmlResult<Cow<'a, str>> {
305
0
        self.normalized_value(XmlVersion::Implicit1_0)
306
0
    }
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
0
    pub fn decode_and_unescape_value_with<'entity>(
319
0
        &self,
320
0
        _decoder: Decoder,
321
0
        resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
322
0
    ) -> XmlResult<Cow<'a, str>> {
323
0
        self.normalized_value_with(XmlVersion::Implicit1_0, 128, resolve_entity)
324
0
    }
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
0
    pub fn as_bool(&self) -> Option<bool> {
355
0
        match self.value.as_ref() {
356
0
            "1" | "true" => Some(true),
357
0
            "0" | "false" => Some(false),
358
0
            _ => None,
359
        }
360
0
    }
361
}
362
363
impl<'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
0
    fn from(val: (&'a str, &'a str)) -> Attribute<'a> {
377
0
        Attribute {
378
0
            key: QName(val.0),
379
0
            value: escape_attribute(val.1),
380
0
        }
381
0
    }
382
}
383
384
impl<'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
0
    fn from(val: (&'a str, Cow<'a, str>)) -> Attribute<'a> {
399
0
        Attribute {
400
0
            key: QName(val.0),
401
0
            value: escape_attribute(val.1),
402
0
        }
403
0
    }
404
}
405
406
impl<'a> From<Attr<&'a str>> for Attribute<'a> {
407
    #[inline]
408
0
    fn from(attr: Attr<&'a str>) -> Self {
409
0
        Self {
410
0
            key: attr.key(),
411
0
            value: Cow::Borrowed(attr.value()),
412
0
        }
413
0
    }
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)]
432
pub 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
439
impl<'a> Attributes<'a> {
440
    /// Internal constructor, used by `BytesStart`. Supplies data in reader's encoding
441
    #[inline]
442
0
    pub(crate) const fn wrap(buf: &'a str, pos: usize, html: bool) -> Self {
443
0
        Self {
444
0
            buf,
445
0
            state: IterState::new(pos, html),
446
0
        }
447
0
    }
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
0
    pub const fn new(buf: &'a str, pos: usize) -> Self {
473
0
        Self::wrap(buf, pos, false)
474
0
    }
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
0
    pub const fn html(buf: &'a str, pos: usize) -> Self {
498
0
        Self::wrap(buf, pos, true)
499
0
    }
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
0
    pub fn with_checks(&mut self, val: bool) -> &mut Attributes<'a> {
508
0
        self.state.check_duplicates = val;
509
0
        self
510
0
    }
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
0
    pub fn has_nil(&mut self, resolver: &NamespaceResolver) -> bool {
570
        use crate::name::ResolveResult::*;
571
572
0
        self.any(|attr| {
573
0
            if let Ok(attr) = attr {
574
0
                match resolver.resolve_attribute(attr.key) {
575
                    (
576
0
                        Bound(Namespace("http://www.w3.org/2001/XMLSchema-instance")),
577
0
                        LocalName("nil"),
578
0
                    ) => attr.as_bool().unwrap_or_default(),
579
0
                    _ => false,
580
                }
581
            } else {
582
0
                false
583
            }
584
0
        })
585
0
    }
586
}
587
588
impl<'a> Debug for Attributes<'a> {
589
0
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
590
0
        f.debug_struct("Attributes")
591
0
            .field("buf", &self.buf)
592
0
            .field("state", &self.state)
593
0
            .finish()
594
0
    }
595
}
596
597
impl<'a> Iterator for Attributes<'a> {
598
    type Item = Result<Attribute<'a>, AttrError>;
599
600
    #[inline]
601
0
    fn next(&mut self) -> Option<Self::Item> {
602
0
        match self.state.next(self.buf.as_bytes()) {
603
0
            None => None,
604
0
            Some(Ok(a)) => Some(Ok(a.map(|range| &self.buf[range]).into())),
605
0
            Some(Err(e)) => Some(Err(e)),
606
        }
607
0
    }
608
}
609
610
impl<'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)]
619
pub 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
703
impl Display for AttrError {
704
0
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
705
0
        match self {
706
0
            Self::ExpectedEq(pos) => write!(
707
0
                f,
708
0
                r#"position {}: attribute key must be directly followed by `=` or space"#,
709
                pos
710
            ),
711
0
            Self::ExpectedValue(pos) => write!(
712
0
                f,
713
0
                r#"position {}: `=` must be followed by an attribute value"#,
714
                pos
715
            ),
716
0
            Self::UnquotedValue(pos) => write!(
717
0
                f,
718
0
                r#"position {}: attribute value must be enclosed in `"` or `'`"#,
719
                pos
720
            ),
721
0
            Self::ExpectedQuote(pos, quote) => write!(
722
0
                f,
723
0
                r#"position {}: missing closing quote `{}` in attribute value"#,
724
0
                pos, *quote as char
725
            ),
726
0
            Self::Duplicated(pos1, pos2) => write!(
727
0
                f,
728
0
                r#"position {}: duplicated attribute, previous declaration at position {}"#,
729
                pos1, pos2
730
            ),
731
        }
732
0
    }
733
}
734
735
impl 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)]
743
pub 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
770
impl<T> Attr<T> {
771
    /// Maps an `Attr<T>` to `Attr<U>` by applying a function to a contained key and value.
772
    #[inline]
773
0
    pub fn map<U, F>(self, mut f: F) -> Attr<U>
774
0
    where
775
0
        F: FnMut(T) -> U,
776
    {
777
0
        match self {
778
0
            Attr::DoubleQ(key, value) => Attr::DoubleQ(f(key), f(value)),
779
0
            Attr::SingleQ(key, value) => Attr::SingleQ(f(key), f(value)),
780
0
            Attr::Empty(key) => Attr::Empty(f(key)),
781
0
            Attr::Unquoted(key, value) => Attr::Unquoted(f(key), f(value)),
782
        }
783
0
    }
784
}
785
786
impl<'a> Attr<&'a str> {
787
    /// Returns the key value
788
    #[inline]
789
0
    pub const fn key(&self) -> QName<'a> {
790
0
        QName(match self {
791
0
            Attr::DoubleQ(key, _) => *key,
792
0
            Attr::SingleQ(key, _) => *key,
793
0
            Attr::Empty(key) => *key,
794
0
            Attr::Unquoted(key, _) => *key,
795
        })
796
0
    }
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
0
    pub const fn value(&self) -> &'a str {
803
0
        match self {
804
0
            Attr::DoubleQ(_, value) => *value,
805
0
            Attr::SingleQ(_, value) => *value,
806
0
            Attr::Empty(_) => "",
807
0
            Attr::Unquoted(_, value) => *value,
808
        }
809
0
    }
810
}
811
812
impl<T: Debug> Debug for Attr<T> {
813
0
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
814
0
        match self {
815
0
            Attr::DoubleQ(key, value) => f
816
0
                .debug_tuple("Attr::DoubleQ")
817
0
                .field(key)
818
0
                .field(value)
819
0
                .finish(),
820
0
            Attr::SingleQ(key, value) => f
821
0
                .debug_tuple("Attr::SingleQ")
822
0
                .field(key)
823
0
                .field(value)
824
0
                .finish(),
825
0
            Attr::Empty(key) => f.debug_tuple("Attr::Empty").field(key).finish(),
826
0
            Attr::Unquoted(key, value) => f
827
0
                .debug_tuple("Attr::Unquoted")
828
0
                .field(key)
829
0
                .field(value)
830
0
                .finish(),
831
        }
832
0
    }
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.
837
impl<T> From<Attr<T>> for (T, Option<T>) {
838
    #[inline]
839
0
    fn from(attr: Attr<T>) -> Self {
840
0
        match attr {
841
0
            Attr::DoubleQ(key, value) => (key, Some(value)),
842
0
            Attr::SingleQ(key, value) => (key, Some(value)),
843
0
            Attr::Empty(key) => (key, None),
844
0
            Attr::Unquoted(key, value) => (key, Some(value)),
845
        }
846
0
    }
847
}
848
849
////////////////////////////////////////////////////////////////////////////////////////////////////
850
851
type AttrResult = Result<Attr<Range<usize>>, AttrError>;
852
853
#[derive(Clone, Copy, Debug)]
854
enum 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
881
const 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)]
887
struct IdentityHasher(u64);
888
889
impl Hasher for IdentityHasher {
890
    #[inline]
891
0
    fn finish(&self) -> u64 {
892
0
        self.0
893
0
    }
894
895
    #[inline]
896
0
    fn write(&mut self, _: &[u8]) {
897
        // The set only ever stores `u64` keys, which route through `write_u64`.
898
0
        unreachable!("IdentityHasher only supports u64 keys")
899
    }
900
901
    #[inline]
902
0
    fn write_u64(&mut self, n: u64) {
903
0
        self.0 = n;
904
0
    }
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]
910
0
fn hash_name(name: &[u8]) -> u64 {
911
0
    let mut hasher = DefaultHasher::new();
912
0
    hasher.write(name);
913
0
    hasher.finish()
914
0
}
915
916
/// External iterator over spans of attribute key and value
917
#[derive(Clone, Debug)]
918
pub(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
940
impl IterState {
941
0
    pub const fn new(offset: usize, html: bool) -> Self {
942
0
        Self {
943
0
            state: State::Next(offset),
944
0
            html,
945
0
            check_duplicates: true,
946
0
            keys: Vec::new(),
947
0
            key_hashes: None,
948
0
        }
949
0
    }
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
0
    fn recover(&self, slice: &[u8]) -> Option<usize> {
955
0
        match self.state {
956
0
            State::Done => None,
957
0
            State::Next(offset) if offset <= slice.len() => Some(offset),
958
0
            State::Next(_) => None,
959
0
            State::SkipValue(offset) => self.skip_value(slice, offset),
960
0
            State::SkipEqValue(offset) => self.skip_eq_value(slice, offset),
961
        }
962
0
    }
963
964
    /// Skip all characters up to first space symbol or end-of-input
965
    #[inline]
966
    #[allow(clippy::manual_map)]
967
0
    fn skip_value(&self, slice: &[u8], offset: usize) -> Option<usize> {
968
0
        let mut iter = (offset..).zip(slice[offset..].iter());
969
970
0
        match iter.find(|&(_, &b)| is_whitespace(b)) {
971
            // Input: `    key  =  value `
972
            //                     |    ^
973
            //                offset    e
974
0
            Some((e, _)) => Some(e),
975
            // Input: `    key  =  value`
976
            //                     |    ^
977
            //                offset    e = len()
978
0
            None => None,
979
        }
980
0
    }
981
982
    /// Skip all characters up to first space symbol or end-of-input
983
    #[inline]
984
0
    fn skip_eq_value(&self, slice: &[u8], offset: usize) -> Option<usize> {
985
0
        let mut iter = (offset..).zip(slice[offset..].iter());
986
987
        // Skip all up to the quote and get the quote type
988
0
        let quote = match iter.find(|&(_, &b)| !is_whitespace(b)) {
989
            // Input: `    key  =  "`
990
            //                  |  ^
991
            //             offset
992
0
            Some((_, b'"')) => b'"',
993
            // Input: `    key  =  '`
994
            //                  |  ^
995
            //             offset
996
0
            Some((_, b'\'')) => b'\'',
997
998
            // Input: `    key  =  x`
999
            //                  |  ^
1000
            //             offset
1001
0
            Some((offset, _)) => return self.skip_value(slice, offset),
1002
            // Input: `    key  =  `
1003
            //                  |  ^
1004
            //             offset
1005
0
            None => return None,
1006
        };
1007
1008
0
        match iter.find(|&(_, &b)| b == quote) {
1009
            // Input: `    key  =  "   "`
1010
            //                         ^
1011
0
            Some((e, b'"')) => Some(e),
1012
            // Input: `    key  =  '   '`
1013
            //                         ^
1014
0
            Some((e, _)) => Some(e),
1015
1016
            // Input: `    key  =  "   `
1017
            // Input: `    key  =  '   `
1018
            //                         ^
1019
            // Closing quote not found
1020
0
            None => None,
1021
        }
1022
0
    }
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
0
    fn check_for_duplicates(
1037
0
        &mut self,
1038
0
        slice: &[u8],
1039
0
        key: Range<usize>,
1040
0
    ) -> Result<Range<usize>, AttrError> {
1041
0
        if self.check_duplicates {
1042
0
            if self.keys.len() >= SMALL_ATTRIBUTE_COUNT {
1043
0
                return self.check_for_duplicates_hashed(slice, key);
1044
0
            }
1045
0
            if let Some(prev) = self
1046
0
                .keys
1047
0
                .iter()
1048
0
                .find(|r| slice[(*r).clone()] == slice[key.clone()])
1049
            {
1050
0
                return Err(AttrError::Duplicated(key.start, prev.start));
1051
0
            }
1052
0
            self.keys.push(key.clone());
1053
0
        }
1054
0
        Ok(key)
1055
0
    }
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
0
    fn check_for_duplicates_hashed(
1062
0
        &mut self,
1063
0
        slice: &[u8],
1064
0
        key: Range<usize>,
1065
0
    ) -> Result<Range<usize>, AttrError> {
1066
0
        let keys = &self.keys;
1067
0
        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
0
            let mut set = HashSet::with_capacity_and_hasher(
1071
0
                keys.len() * 2,
1072
0
                BuildHasherDefault::<IdentityHasher>::default(),
1073
            );
1074
0
            for r in keys {
1075
0
                set.insert(hash_name(&slice[r.clone()]));
1076
0
            }
1077
0
            set
1078
0
        });
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
0
        if !key_hashes.insert(hash_name(&slice[key.clone()])) {
1083
0
            if let Some(prev) = self
1084
0
                .keys
1085
0
                .iter()
1086
0
                .find(|r| slice[(*r).clone()] == slice[key.clone()])
1087
            {
1088
0
                return Err(AttrError::Duplicated(key.start, prev.start));
1089
0
            }
1090
0
        }
1091
0
        self.keys.push(key.clone());
1092
0
        Ok(key)
1093
0
    }
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
0
    fn key_only(&mut self, slice: &[u8], key: Range<usize>, offset: usize) -> Option<AttrResult> {
1102
0
        Some(if self.html {
1103
0
            self.check_for_duplicates(slice, key).map(Attr::Empty)
1104
        } else {
1105
0
            Err(AttrError::ExpectedEq(offset))
1106
        })
1107
0
    }
1108
1109
    #[inline]
1110
0
    fn double_q(&mut self, key: Range<usize>, value: Range<usize>) -> Option<AttrResult> {
1111
0
        self.state = State::Next(value.end + 1); // +1 for `"`
1112
1113
0
        Some(Ok(Attr::DoubleQ(key, value)))
1114
0
    }
1115
1116
    #[inline]
1117
0
    fn single_q(&mut self, key: Range<usize>, value: Range<usize>) -> Option<AttrResult> {
1118
0
        self.state = State::Next(value.end + 1); // +1 for `'`
1119
1120
0
        Some(Ok(Attr::SingleQ(key, value)))
1121
0
    }
1122
1123
0
    pub fn next(&mut self, slice: &[u8]) -> Option<AttrResult> {
1124
0
        let mut iter = match self.recover(slice) {
1125
0
            Some(offset) => (offset..).zip(slice[offset..].iter()),
1126
0
            None => return None,
1127
        };
1128
1129
        // Index where next key started
1130
0
        let start_key = match iter.find(|&(_, &b)| !is_whitespace(b)) {
1131
            // Input: `    key`
1132
            //             ^
1133
0
            Some((s, _)) => s,
1134
            // Input: `    `
1135
            //             ^
1136
            None => {
1137
                // Because we reach end-of-input, stop iteration on next call
1138
0
                self.state = State::Done;
1139
0
                return None;
1140
            }
1141
        };
1142
        // Span of a key
1143
0
        let (key, offset) = match iter.find(|&(_, &b)| b == b'=' || is_whitespace(b)) {
1144
            // Input: `    key=`
1145
            //             |  ^
1146
            //             s  e
1147
0
            Some((e, b'=')) => (start_key..e, e),
1148
1149
            // Input: `    key `
1150
            //                ^
1151
0
            Some((e, _)) => match iter.find(|&(_, &b)| !is_whitespace(b)) {
1152
                // Input: `    key  =`
1153
                //             |  | ^
1154
                //     start_key  e
1155
0
                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
0
                Some((offset, _)) => {
1161
                    // In any case, recovering is not required
1162
0
                    self.state = State::Next(offset);
1163
0
                    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
0
                    self.state = State::Done;
1172
0
                    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
0
                self.state = State::Done;
1183
0
                let e = slice.len();
1184
0
                return self.key_only(slice, start_key..e, e);
1185
            }
1186
        };
1187
1188
0
        let key = match self.check_for_duplicates(slice, key) {
1189
0
            Err(e) => {
1190
0
                self.state = State::SkipEqValue(offset);
1191
0
                return Some(Err(e));
1192
            }
1193
0
            Ok(key) => key,
1194
        };
1195
1196
        ////////////////////////////////////////////////////////////////////////
1197
1198
        // Gets the position of quote and quote type
1199
0
        let (start_value, quote) = match iter.find(|&(_, &b)| !is_whitespace(b)) {
1200
            // Input: `    key  =  "`
1201
            //                     ^
1202
0
            Some((s, b'"')) => (s + 1, b'"'),
1203
            // Input: `    key  =  '`
1204
            //                     ^
1205
0
            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
0
            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
0
                let end = match iter.find(|&(_, &b)| is_whitespace(b)) {
1215
                    // Input: `    key  =  value `
1216
                    //                     |    ^
1217
                    //                     s    e
1218
0
                    Some((e, _)) => e,
1219
                    // Input: `    key  =  value`
1220
                    //                     |    ^
1221
                    //                     s    e = len()
1222
0
                    None => slice.len(),
1223
                };
1224
0
                self.state = State::Next(end);
1225
0
                return Some(Ok(Attr::Unquoted(key, s..end)));
1226
            }
1227
            // Input: `    key  =  x`
1228
            //                     ^
1229
0
            Some((s, _)) => {
1230
0
                self.state = State::SkipValue(s);
1231
0
                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
0
                self.state = State::Done;
1239
0
                return Some(Err(AttrError::ExpectedValue(slice.len())));
1240
            }
1241
        };
1242
1243
0
        match iter.find(|&(_, &b)| b == quote) {
1244
            // Input: `    key  =  "   "`
1245
            //                         ^
1246
0
            Some((e, b'"')) => self.double_q(key, start_value..e),
1247
            // Input: `    key  =  '   '`
1248
            //                         ^
1249
0
            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
0
                self.state = State::Done;
1258
0
                Some(Err(AttrError::ExpectedQuote(slice.len(), quote)))
1259
            }
1260
        }
1261
0
    }
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)]
1269
mod 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)]
2213
mod 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
}