quick_xml/events/attributes.rs
1//! Xml Attributes module
2//!
3//! Provides an iterator over attributes key/value pairs
4
5use crate::XmlVersion;
6use crate::encoding::Decoder;
7use crate::errors::Result as XmlResult;
8use crate::escape::{escape_attribute, resolve_predefined_entity};
9use crate::name::{LocalName, Namespace, NamespaceResolver, QName};
10use crate::utils::is_whitespace;
11
12use std::collections::HashSet;
13use std::fmt::{self, Debug, Display, Formatter};
14use std::hash::{BuildHasherDefault, DefaultHasher, Hasher};
15use std::iter::FusedIterator;
16use std::{borrow::Cow, ops::Range};
17
18/// A struct representing a key/value XML attribute.
19///
20/// Field `value` stores the raw attribute value, possibly containing escape-sequences.
21/// Most users will likely want to access the value using the [`normalized_value`] method.
22///
23/// # Lifetime
24///
25/// `'a` is a lifetime of the owning event from which this attribute is derived.
26///
27/// [`normalized_value`]: Self::normalized_value
28#[derive(Clone, Debug, Eq, PartialEq)]
29pub struct Attribute<'a> {
30 /// The key to uniquely define the attribute.
31 ///
32 /// If [`Attributes::with_checks`] is turned off, the key might not be unique.
33 pub key: QName<'a>,
34 /// The raw value of the attribute.
35 pub value: Cow<'a, str>,
36}
37
38impl<'a> Attribute<'a> {
39 /// Returns the attribute value normalized as per [the XML specification] (or [for 1.0]).
40 ///
41 /// The characters `\t`, `\r`, `\n` are replaced with whitespace characters (`0x20`).
42 ///
43 /// The following escape sequences are replaced with their unescaped equivalents:
44 ///
45 /// | Escape Sequence | Replacement
46 /// |-----------------|------------
47 /// | `<` | `<`
48 /// | `>` | `>`
49 /// | `&` | `&`
50 /// | `'` | `'`
51 /// | `"` | `"`
52 ///
53 /// This will allocate unless the raw attribute value does not require normalization.
54 ///
55 /// Note, although you may use this library to parse HTML, you cannot use this
56 /// method to get HTML content, because its returns normalized value: the following
57 /// sequences are translated into a single space (U+0020) character:
58 ///
59 /// - `\r\n`
60 /// - `\r\x85` (only XML 1.1)
61 /// - `\r`
62 /// - `\n`
63 /// - `\t`
64 /// - `\x85` (only XML 1.1)
65 /// - `\x2028` (only XML 1.1)
66 ///
67 /// The text in HTML normally is not normalized in any way; normalization is
68 /// performed only in limited contexts and [only for] `\r\n` and `\r`.
69 ///
70 /// See also [`normalized_value_with()`](Self::normalized_value_with).
71 ///
72 /// [the XML specification]: https://www.w3.org/TR/xml11/#AVNormalize
73 /// [for 1.0]: https://www.w3.org/TR/xml/#AVNormalize
74 /// [only for]: https://html.spec.whatwg.org/#normalize-newlines
75 pub fn normalized_value(&self, version: XmlVersion) -> XmlResult<Cow<'a, str>> {
76 // resolve_predefined_entity returns only non-recursive replacements, so depth=1 is enough
77 self.normalized_value_with(version, 1, resolve_predefined_entity)
78 }
79
80 /// Returns the attribute value normalized as per [the XML specification] (or [for 1.0]),
81 /// using a custom entity resolver.
82 ///
83 /// Do not use this method with HTML attributes.
84 ///
85 /// The characters `\t`, `\r`, `\n` are replaced with whitespace characters (`0x20`).
86 ///
87 /// A function for resolving entities can be provided as `resolve_entity`.
88 /// This method does not resolve any predefined entities, but you can use
89 /// [`resolve_predefined_entity`] in your function.
90 ///
91 /// This will allocate unless the raw attribute value does not require normalization.
92 ///
93 /// Note, although you may use this library to parse HTML, you cannot use this
94 /// method to get HTML content, because its returns normalized value: the following
95 /// sequences are translated into a single space (U+0020) character:
96 ///
97 /// - `\r\n`
98 /// - `\r\x85` (only XML 1.1)
99 /// - `\r`
100 /// - `\n`
101 /// - `\t`
102 /// - `\x85` (only XML 1.1)
103 /// - `\x2028` (only XML 1.1)
104 ///
105 /// The text in HTML normally is not normalized in any way; normalization is
106 /// performed only in limited contexts and [only for] `\r\n` and `\r`.
107 ///
108 /// See also [`normalized_value()`](Self::normalized_value).
109 ///
110 /// # Parameters
111 ///
112 /// - `depth`: maximum number of nested entities that can be expanded. If expansion
113 /// chain will be more that this value, the function will return [`EscapeError::TooManyNestedEntities`]
114 /// - `resolve_entity`: a function to resolve entity. This function could be called
115 /// multiple times on the same input and can return different values in each case
116 /// for the same input, although it is not recommended
117 ///
118 /// [the XML specification]: https://www.w3.org/TR/xml11/#AVNormalize
119 /// [for 1.0]: https://www.w3.org/TR/xml/#AVNormalize
120 /// [only for]: https://html.spec.whatwg.org/#normalize-newlines
121 /// [`EscapeError::TooManyNestedEntities`]: crate::escape::EscapeError::TooManyNestedEntities
122 pub fn normalized_value_with<'entity>(
123 &self,
124 version: XmlVersion,
125 depth: usize,
126 resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
127 ) -> XmlResult<Cow<'a, str>> {
128 match version.normalize_attribute_value(&self.value, depth, resolve_entity)? {
129 // Because result is borrowed, no replacements was done and we can use original string
130 Cow::Borrowed(_) => Ok(self.value.clone()),
131 Cow::Owned(s) => Ok(s.into()),
132 }
133 }
134
135 /// Decodes using a provided reader and returns the attribute value normalized
136 /// as per [the XML specification] (or [for 1.0]).
137 ///
138 /// # Deprecation
139 ///
140 /// Attribute values are now always stored as valid UTF-8 strings, so decoding
141 /// is no longer needed. Use [`normalized_value()`](Self::normalized_value) instead.
142 ///
143 /// Do not use this method with HTML attributes.
144 ///
145 /// The characters `\t`, `\r`, `\n` are replaced with whitespace characters (`0x20`).
146 ///
147 /// The following escape sequences are replaced with their unescaped equivalents:
148 ///
149 /// | Escape Sequence | Replacement
150 /// |-----------------|------------
151 /// | `<` | `<`
152 /// | `>` | `>`
153 /// | `&` | `&`
154 /// | `'` | `'`
155 /// | `"` | `"`
156 ///
157 /// This will allocate unless the raw attribute value does not require normalization.
158 ///
159 /// Note, although you may use this library to parse HTML, you cannot use this
160 /// method to get HTML content, because its returns normalized value: the following
161 /// sequences are translated into a single space (U+0020) character:
162 ///
163 /// - `\r\n`
164 /// - `\r\x85` (only XML 1.1)
165 /// - `\r`
166 /// - `\n`
167 /// - `\t`
168 /// - `\x85` (only XML 1.1)
169 /// - `\x2028` (only XML 1.1)
170 ///
171 /// The text in HTML normally is not normalized in any way; normalization is
172 /// performed only in limited contexts and [only for] `\r\n` and `\r`.
173 ///
174 /// See also [`decoded_and_normalized_value_with()`](#method.decoded_and_normalized_value_with)
175 ///
176 /// [the XML specification]: https://www.w3.org/TR/xml11/#AVNormalize
177 /// [for 1.0]: https://www.w3.org/TR/xml/#AVNormalize
178 /// [only for]: https://html.spec.whatwg.org/#normalize-newlines
179 #[deprecated = "decoding is no longer needed, use `normalized_value()` instead"]
180 #[inline]
181 pub fn decoded_and_normalized_value(
182 &self,
183 version: XmlVersion,
184 _decoder: Decoder,
185 ) -> XmlResult<Cow<'a, str>> {
186 self.normalized_value(version)
187 }
188
189 /// Decodes using a provided reader and returns the attribute value normalized
190 /// as per [the XML specification] (or [for 1.0]), using a custom entity resolver.
191 ///
192 /// # Deprecation
193 ///
194 /// Attribute values are now always stored as valid UTF-8 strings, so decoding
195 /// is no longer needed. Use [`normalized_value_with()`](Self::normalized_value_with) instead.
196 ///
197 /// Do not use this method with HTML attributes.
198 ///
199 /// The characters `\t`, `\r`, `\n` are replaced with whitespace characters (`0x20`).
200 ///
201 /// A function for resolving entities can be provided as `resolve_entity`.
202 /// This method does not resolve any predefined entities, but you can use
203 /// [`resolve_predefined_entity`] in your function.
204 ///
205 /// This will allocate unless the raw attribute value does not require normalization.
206 ///
207 /// Note, although you may use this library to parse HTML, you cannot use this
208 /// method to get HTML content, because its returns normalized value: the following
209 /// sequences are translated into a single space (U+0020) character:
210 ///
211 /// - `\r\n`
212 /// - `\r\x85` (only XML 1.1)
213 /// - `\r`
214 /// - `\n`
215 /// - `\t`
216 /// - `\x85` (only XML 1.1)
217 /// - `\x2028` (only XML 1.1)
218 ///
219 /// The text in HTML normally is not normalized in any way; normalization is
220 /// performed only in limited contexts and [only for] `\r\n` and `\r`.
221 ///
222 /// See also [`decoded_and_normalized_value()`](#method.decoded_and_normalized_value)
223 ///
224 /// # Parameters
225 ///
226 /// - `depth`: maximum number of nested entities that can be expanded. If expansion
227 /// chain will be more that this value, the function will return [`EscapeError::TooManyNestedEntities`]
228 /// - `resolve_entity`: a function to resolve entity. This function could be called
229 /// multiple times on the same input and can return different values in each case
230 /// for the same input, although it is not recommended
231 ///
232 /// [the XML specification]: https://www.w3.org/TR/xml11/#AVNormalize
233 /// [for 1.0]: https://www.w3.org/TR/xml/#AVNormalize
234 /// [only for]: https://html.spec.whatwg.org/#normalize-newlines
235 /// [`EscapeError::TooManyNestedEntities`]: crate::escape::EscapeError::TooManyNestedEntities
236 #[deprecated = "decoding is no longer needed, use `normalized_value_with()` instead"]
237 #[inline]
238 pub fn decoded_and_normalized_value_with<'entity>(
239 &self,
240 version: XmlVersion,
241 _decoder: Decoder,
242 depth: usize,
243 resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
244 ) -> XmlResult<Cow<'a, str>> {
245 self.normalized_value_with(version, depth, resolve_entity)
246 }
247
248 /// Returns the unescaped value.
249 ///
250 /// # Deprecation
251 ///
252 /// Use [`normalized_value()`](Self::normalized_value) instead.
253 ///
254 /// Escape sequences such as `>` are replaced with their unescaped
255 /// equivalents such as `>`.
256 ///
257 /// This will allocate if the value contains any escape sequences.
258 ///
259 /// See also [`unescape_value_with()`](Self::unescape_value_with)
260 ///
261 /// [`encoding`]: ../../index.html#encoding
262 #[cfg(any(doc, not(feature = "encoding")))]
263 #[deprecated = "use `Self::normalized_value()`"]
264 pub fn unescape_value(&self) -> XmlResult<Cow<'a, str>> {
265 // resolve_predefined_entity returns only non-recursive replacements, so depth=1 is enough
266 self.normalized_value_with(XmlVersion::Implicit1_0, 1, resolve_predefined_entity)
267 }
268
269 /// Decodes using UTF-8 then unescapes the value, using custom entities.
270 ///
271 /// # Deprecation
272 ///
273 /// Use [`normalized_value_with()`](Self::normalized_value_with) instead.
274 ///
275 /// Escape sequences such as `>` are replaced with their unescaped
276 /// equivalents such as `>`. A fallback resolver for additional custom
277 /// entities can be provided via `resolve_entity`.
278 ///
279 /// This will allocate if the value contains any escape sequences.
280 ///
281 /// See also [`unescape_value()`](Self::unescape_value)
282 ///
283 /// [`encoding`]: ../../index.html#encoding
284 #[cfg(any(doc, not(feature = "encoding")))]
285 #[deprecated = "use `Self::normalized_value_with()`"]
286 #[inline]
287 pub fn unescape_value_with<'entity>(
288 &self,
289 resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
290 ) -> XmlResult<Cow<'a, str>> {
291 self.normalized_value_with(XmlVersion::Implicit1_0, 128, resolve_entity)
292 }
293
294 /// Decodes then unescapes the value.
295 ///
296 /// # Deprecation
297 ///
298 /// Attribute values are now always stored as valid UTF-8 strings, so decoding
299 /// is no longer needed. Use [`normalized_value()`](Self::normalized_value) instead.
300 ///
301 /// This will allocate if the value contains any escape sequences.
302 #[deprecated = "decoding is no longer needed, use `Self::normalized_value()` instead"]
303 #[inline]
304 pub fn decode_and_unescape_value(&self, _decoder: Decoder) -> XmlResult<Cow<'a, str>> {
305 self.normalized_value(XmlVersion::Implicit1_0)
306 }
307
308 /// Decodes then unescapes the value with custom entities.
309 ///
310 /// # Deprecation
311 ///
312 /// Attribute values are now always stored as valid UTF-8 strings, so decoding
313 /// is no longer needed. Use [`normalized_value_with()`](Self::normalized_value_with) instead.
314 ///
315 /// This will allocate if the value contains any escape sequences.
316 #[deprecated = "decoding is no longer needed, use `Self::normalized_value_with()` instead"]
317 #[inline]
318 pub fn decode_and_unescape_value_with<'entity>(
319 &self,
320 _decoder: Decoder,
321 resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
322 ) -> XmlResult<Cow<'a, str>> {
323 self.normalized_value_with(XmlVersion::Implicit1_0, 128, resolve_entity)
324 }
325
326 /// If attribute value [represents] valid boolean values, returns `Some`, otherwise returns `None`.
327 ///
328 /// The valid boolean representations are only `"true"`, `"false"`, `"1"`, and `"0"`.
329 ///
330 /// # Examples
331 ///
332 /// ```
333 /// # use pretty_assertions::assert_eq;
334 /// use quick_xml::events::attributes::Attribute;
335 ///
336 /// let attr = Attribute::from(("attr", "false"));
337 /// assert_eq!(attr.as_bool(), Some(false));
338 ///
339 /// let attr = Attribute::from(("attr", "0"));
340 /// assert_eq!(attr.as_bool(), Some(false));
341 ///
342 /// let attr = Attribute::from(("attr", "true"));
343 /// assert_eq!(attr.as_bool(), Some(true));
344 ///
345 /// let attr = Attribute::from(("attr", "1"));
346 /// assert_eq!(attr.as_bool(), Some(true));
347 ///
348 /// let attr = Attribute::from(("attr", "not bool"));
349 /// assert_eq!(attr.as_bool(), None);
350 /// ```
351 ///
352 /// [represents]: https://www.w3.org/TR/xmlschema11-2/#boolean
353 #[inline]
354 pub fn as_bool(&self) -> Option<bool> {
355 match self.value.as_ref() {
356 "1" | "true" => Some(true),
357 "0" | "false" => Some(false),
358 _ => None,
359 }
360 }
361}
362
363impl<'a> From<(&'a str, &'a str)> for Attribute<'a> {
364 /// Creates new attribute from text representation.
365 /// Key is stored as-is, but the value will be escaped.
366 ///
367 /// # Examples
368 ///
369 /// ```
370 /// # use pretty_assertions::assert_eq;
371 /// use quick_xml::events::attributes::Attribute;
372 ///
373 /// let features = Attribute::from(("features", "Bells & whistles"));
374 /// assert_eq!(features.value.as_ref(), "Bells & whistles");
375 /// ```
376 fn from(val: (&'a str, &'a str)) -> Attribute<'a> {
377 Attribute {
378 key: QName(val.0),
379 value: escape_attribute(val.1),
380 }
381 }
382}
383
384impl<'a> From<(&'a str, Cow<'a, str>)> for Attribute<'a> {
385 /// Creates new attribute from text representation.
386 /// Key is stored as-is, but the value will be escaped.
387 ///
388 /// # Examples
389 ///
390 /// ```
391 /// # use std::borrow::Cow;
392 /// use pretty_assertions::assert_eq;
393 /// use quick_xml::events::attributes::Attribute;
394 ///
395 /// let features = Attribute::from(("features", Cow::Borrowed("Bells & whistles")));
396 /// assert_eq!(features.value.as_ref(), "Bells & whistles");
397 /// ```
398 fn from(val: (&'a str, Cow<'a, str>)) -> Attribute<'a> {
399 Attribute {
400 key: QName(val.0),
401 value: escape_attribute(val.1),
402 }
403 }
404}
405
406impl<'a> From<Attr<&'a str>> for Attribute<'a> {
407 #[inline]
408 fn from(attr: Attr<&'a str>) -> Self {
409 Self {
410 key: attr.key(),
411 value: Cow::Borrowed(attr.value()),
412 }
413 }
414}
415
416////////////////////////////////////////////////////////////////////////////////////////////////////
417
418/// Iterator over XML attributes.
419///
420/// Yields `Result<Attribute>`. An `Err` will be yielded if an attribute is malformed or duplicated.
421/// The duplicate check can be turned off by calling [`with_checks(false)`].
422///
423/// When [`serialize`] feature is enabled, can be converted to serde's deserializer.
424///
425/// # Lifetime
426///
427/// `'a` is a lifetime of the owning event from which this iterator is derived.
428///
429/// [`with_checks(false)`]: Self::with_checks
430/// [`serialize`]: ../../index.html#serialize
431#[derive(Clone)]
432pub struct Attributes<'a> {
433 /// Slice of `BytesStart` corresponding to attributes
434 buf: &'a str,
435 /// Iterator state, independent from the actual source of bytes
436 state: IterState,
437}
438
439impl<'a> Attributes<'a> {
440 /// Internal constructor, used by `BytesStart`. Supplies data in reader's encoding
441 #[inline]
442 pub(crate) const fn wrap(buf: &'a str, pos: usize, html: bool) -> Self {
443 Self {
444 buf,
445 state: IterState::new(pos, html),
446 }
447 }
448
449 /// Creates a new attribute iterator from a buffer, which recognizes only XML-style
450 /// attributes, i. e. those which in the form `name = "value"` or `name = 'value'`.
451 /// HTML style attributes (i. e. without quotes or only name) will return a error.
452 ///
453 /// # Parameters
454 /// - `buf`: a buffer with a tag name and attributes, usually this is the whole
455 /// string between `<` and `>` (or `/>`) of a tag;
456 /// - `pos`: a position in the `buf` where tag name is finished and attributes
457 /// is started. It is not necessary to point exactly to the end of a tag name,
458 /// although that is usually that. If it will be more than the `buf` length,
459 /// then the iterator will return `None`` immediately.
460 ///
461 /// # Example
462 /// ```
463 /// # use quick_xml::events::attributes::{Attribute, Attributes};
464 /// # use pretty_assertions::assert_eq;
465 /// #
466 /// let mut iter = Attributes::new("tag-name attr1 = 'value1' attr2='value2' ", 9);
467 /// // ^0 ^9
468 /// assert_eq!(iter.next(), Some(Ok(Attribute::from(("attr1", "value1")))));
469 /// assert_eq!(iter.next(), Some(Ok(Attribute::from(("attr2", "value2")))));
470 /// assert_eq!(iter.next(), None);
471 /// ```
472 pub const fn new(buf: &'a str, pos: usize) -> Self {
473 Self::wrap(buf, pos, false)
474 }
475
476 /// Creates a new attribute iterator from a buffer, allowing HTML attribute syntax.
477 ///
478 /// # Parameters
479 /// - `buf`: a buffer with a tag name and attributes, usually this is the whole
480 /// string between `<` and `>` (or `/>`) of a tag;
481 /// - `pos`: a position in the `buf` where tag name is finished and attributes
482 /// is started. It is not necessary to point exactly to the end of a tag name,
483 /// although that is usually that. If it will be more than the `buf` length,
484 /// then the iterator will return `None`` immediately.
485 ///
486 /// # Example
487 /// ```
488 /// # use quick_xml::events::attributes::{Attribute, Attributes};
489 /// # use pretty_assertions::assert_eq;
490 /// #
491 /// let mut iter = Attributes::html("tag-name attr1 = value1 attr2 ", 9);
492 /// // ^0 ^9
493 /// assert_eq!(iter.next(), Some(Ok(Attribute::from(("attr1", "value1")))));
494 /// assert_eq!(iter.next(), Some(Ok(Attribute::from(("attr2", "")))));
495 /// assert_eq!(iter.next(), None);
496 /// ```
497 pub const fn html(buf: &'a str, pos: usize) -> Self {
498 Self::wrap(buf, pos, true)
499 }
500
501 /// Changes whether attributes should be checked for uniqueness.
502 ///
503 /// The XML specification requires attribute keys in the same element to be unique. This check
504 /// can be disabled to improve performance slightly.
505 ///
506 /// (`true` by default)
507 pub fn with_checks(&mut self, val: bool) -> &mut Attributes<'a> {
508 self.state.check_duplicates = val;
509 self
510 }
511
512 /// Checks if the current tag has a [`xsi:nil`] attribute. This method ignores any errors in
513 /// attributes.
514 ///
515 /// # Examples
516 ///
517 /// ```
518 /// # use pretty_assertions::assert_eq;
519 /// use quick_xml::events::Event;
520 /// use quick_xml::name::QName;
521 /// use quick_xml::reader::NsReader;
522 ///
523 /// let mut reader = NsReader::from_str("
524 /// <root xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
525 /// <true xsi:nil='true'/>
526 /// <false xsi:nil='false'/>
527 /// <none/>
528 /// <non-xsi xsi:nil='true' xmlns:xsi='namespace'/>
529 /// <unbound-nil nil='true' xmlns='http://www.w3.org/2001/XMLSchema-instance'/>
530 /// <another-xmlns f:nil='true' xmlns:f='http://www.w3.org/2001/XMLSchema-instance'/>
531 /// </root>
532 /// ");
533 /// reader.config_mut().trim_text(true);
534 ///
535 /// macro_rules! check {
536 /// ($reader:expr, $name:literal, $value:literal) => {
537 /// let event = match $reader.read_event().unwrap() {
538 /// Event::Empty(e) => e,
539 /// e => panic!("Unexpected event {:?}", e),
540 /// };
541 /// assert_eq!(
542 /// (event.name(), event.attributes().has_nil($reader.resolver())),
543 /// (QName($name), $value),
544 /// );
545 /// };
546 /// }
547 ///
548 /// let root = match reader.read_event().unwrap() {
549 /// Event::Start(e) => e,
550 /// e => panic!("Unexpected event {:?}", e),
551 /// };
552 /// assert_eq!(root.attributes().has_nil(reader.resolver()), false);
553 ///
554 /// // definitely true
555 /// check!(reader, "true", true);
556 /// // definitely false
557 /// check!(reader, "false", false);
558 /// // absence of the attribute means that attribute is not set
559 /// check!(reader, "none", false);
560 /// // attribute not bound to the correct namespace
561 /// check!(reader, "non-xsi", false);
562 /// // attributes without prefix not bound to any namespace
563 /// check!(reader, "unbound-nil", false);
564 /// // prefix can be any while it is bound to the correct namespace
565 /// check!(reader, "another-xmlns", true);
566 /// ```
567 ///
568 /// [`xsi:nil`]: https://www.w3.org/TR/xmlschema-1/#xsi_nil
569 pub fn has_nil(&mut self, resolver: &NamespaceResolver) -> bool {
570 use crate::name::ResolveResult::*;
571
572 self.any(|attr| {
573 if let Ok(attr) = attr {
574 match resolver.resolve_attribute(attr.key) {
575 (
576 Bound(Namespace("http://www.w3.org/2001/XMLSchema-instance")),
577 LocalName("nil"),
578 ) => attr.as_bool().unwrap_or_default(),
579 _ => false,
580 }
581 } else {
582 false
583 }
584 })
585 }
586}
587
588impl<'a> Debug for Attributes<'a> {
589 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
590 f.debug_struct("Attributes")
591 .field("buf", &self.buf)
592 .field("state", &self.state)
593 .finish()
594 }
595}
596
597impl<'a> Iterator for Attributes<'a> {
598 type Item = Result<Attribute<'a>, AttrError>;
599
600 #[inline]
601 fn next(&mut self) -> Option<Self::Item> {
602 match self.state.next(self.buf.as_bytes()) {
603 None => None,
604 Some(Ok(a)) => Some(Ok(a.map(|range| &self.buf[range]).into())),
605 Some(Err(e)) => Some(Err(e)),
606 }
607 }
608}
609
610impl<'a> FusedIterator for Attributes<'a> {}
611
612////////////////////////////////////////////////////////////////////////////////////////////////////
613
614/// Errors that can be raised during parsing attributes.
615///
616/// Recovery position in examples shows the position from which parsing of the
617/// next attribute will be attempted.
618#[derive(Clone, Debug, PartialEq, Eq)]
619pub enum AttrError {
620 /// Attribute key was not followed by `=`, position relative to the start of
621 /// the owning tag is provided.
622 ///
623 /// Example of input that raises this error:
624 ///
625 /// ```xml
626 /// <tag key another="attribute"/>
627 /// <!-- ^~~ error position, recovery position (8) -->
628 /// ```
629 ///
630 /// This error can be raised only when the iterator is in XML mode.
631 ExpectedEq(usize),
632 /// Attribute value was not found after `=`, position relative to the start
633 /// of the owning tag is provided.
634 ///
635 /// Example of input that raises this error:
636 ///
637 /// ```xml
638 /// <tag key = />
639 /// <!-- ^~~ error position, recovery position (10) -->
640 /// ```
641 ///
642 /// This error can be returned only for the last attribute in the list,
643 /// because otherwise any content after `=` will be treated as a value.
644 /// The XML
645 ///
646 /// ```xml
647 /// <tag key = another-key = "value"/>
648 /// <!-- ^ ^- recovery position (24) -->
649 /// <!-- '~~ error position (22) -->
650 /// ```
651 ///
652 /// will be treated as `Attribute { key = b"key", value = b"another-key" }`
653 /// and or [`Attribute`] is returned, or [`AttrError::UnquotedValue`] is raised,
654 /// depending on the parsing mode.
655 ExpectedValue(usize),
656 /// Attribute value is not quoted, position relative to the start of the
657 /// owning tag is provided.
658 ///
659 /// Example of input that raises this error:
660 ///
661 /// ```xml
662 /// <tag key = value />
663 /// <!-- ^ ^~~ recovery position (15) -->
664 /// <!-- '~~ error position (10) -->
665 /// ```
666 ///
667 /// This error can be raised only when the iterator is in XML mode.
668 UnquotedValue(usize),
669 /// Attribute value was not finished with a matching quote, position relative
670 /// to the start of owning tag and a quote is provided. That position is always
671 /// a last character in the tag content.
672 ///
673 /// Example of input that raises this error:
674 ///
675 /// ```xml
676 /// <tag key = "value />
677 /// <tag key = 'value />
678 /// <!-- ^~~ error position, recovery position (18) -->
679 /// ```
680 ///
681 /// This error can be returned only for the last attribute in the list,
682 /// because all input was consumed during scanning for a quote.
683 ExpectedQuote(usize, u8),
684 /// An attribute with the same name was already encountered. Two parameters
685 /// define (1) the error position relative to the start of the owning tag
686 /// for a new attribute and (2) the start position of a previously encountered
687 /// attribute with the same name.
688 ///
689 /// Example of input that raises this error:
690 ///
691 /// ```xml
692 /// <tag key = 'value' key="value2" attr3='value3' />
693 /// <!-- ^ ^ ^~~ recovery position (32) -->
694 /// <!-- | '~~ error position (19) -->
695 /// <!-- '~~ previous position (4) -->
696 /// ```
697 ///
698 /// This error is returned only when [`Attributes::with_checks()`] is set
699 /// to `true` (that is default behavior).
700 Duplicated(usize, usize),
701}
702
703impl Display for AttrError {
704 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
705 match self {
706 Self::ExpectedEq(pos) => write!(
707 f,
708 r#"position {}: attribute key must be directly followed by `=` or space"#,
709 pos
710 ),
711 Self::ExpectedValue(pos) => write!(
712 f,
713 r#"position {}: `=` must be followed by an attribute value"#,
714 pos
715 ),
716 Self::UnquotedValue(pos) => write!(
717 f,
718 r#"position {}: attribute value must be enclosed in `"` or `'`"#,
719 pos
720 ),
721 Self::ExpectedQuote(pos, quote) => write!(
722 f,
723 r#"position {}: missing closing quote `{}` in attribute value"#,
724 pos, *quote as char
725 ),
726 Self::Duplicated(pos1, pos2) => write!(
727 f,
728 r#"position {}: duplicated attribute, previous declaration at position {}"#,
729 pos1, pos2
730 ),
731 }
732 }
733}
734
735impl std::error::Error for AttrError {}
736
737////////////////////////////////////////////////////////////////////////////////////////////////////
738
739/// A struct representing a key/value XML or HTML [attribute].
740///
741/// [attribute]: https://www.w3.org/TR/xml11/#NT-Attribute
742#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
743pub enum Attr<T> {
744 /// Attribute with value enclosed in double quotes (`"`). Attribute key and
745 /// value provided. This is a canonical XML-style attribute.
746 DoubleQ(T, T),
747 /// Attribute with value enclosed in single quotes (`'`). Attribute key and
748 /// value provided. This is an XML-style attribute.
749 SingleQ(T, T),
750 /// Attribute with value not enclosed in quotes. Attribute key and value
751 /// provided. This is HTML-style attribute, it can be returned in HTML-mode
752 /// parsing only. In an XML mode [`AttrError::UnquotedValue`] will be raised
753 /// instead.
754 ///
755 /// Attribute value can be invalid according to the [HTML specification],
756 /// in particular, it can contain `"`, `'`, `=`, `<`, and <code>`</code>
757 /// characters. The absence of the `>` character is nevertheless guaranteed,
758 /// since the parser extracts [events] based on them even before the start
759 /// of parsing attributes.
760 ///
761 /// [HTML specification]: https://html.spec.whatwg.org/#unquoted
762 /// [events]: crate::events::Event::Start
763 Unquoted(T, T),
764 /// Attribute without value. Attribute key provided. This is HTML-style attribute,
765 /// it can be returned in HTML-mode parsing only. In XML mode
766 /// [`AttrError::ExpectedEq`] will be raised instead.
767 Empty(T),
768}
769
770impl<T> Attr<T> {
771 /// Maps an `Attr<T>` to `Attr<U>` by applying a function to a contained key and value.
772 #[inline]
773 pub fn map<U, F>(self, mut f: F) -> Attr<U>
774 where
775 F: FnMut(T) -> U,
776 {
777 match self {
778 Attr::DoubleQ(key, value) => Attr::DoubleQ(f(key), f(value)),
779 Attr::SingleQ(key, value) => Attr::SingleQ(f(key), f(value)),
780 Attr::Empty(key) => Attr::Empty(f(key)),
781 Attr::Unquoted(key, value) => Attr::Unquoted(f(key), f(value)),
782 }
783 }
784}
785
786impl<'a> Attr<&'a str> {
787 /// Returns the key value
788 #[inline]
789 pub const fn key(&self) -> QName<'a> {
790 QName(match self {
791 Attr::DoubleQ(key, _) => *key,
792 Attr::SingleQ(key, _) => *key,
793 Attr::Empty(key) => *key,
794 Attr::Unquoted(key, _) => *key,
795 })
796 }
797 /// Returns the attribute value. For [`Self::Empty`] variant an empty string
798 /// is returned according to the [HTML specification].
799 ///
800 /// [HTML specification]: https://www.w3.org/TR/2012/WD-html-markup-20120329/syntax.html#syntax-attr-empty
801 #[inline]
802 pub const fn value(&self) -> &'a str {
803 match self {
804 Attr::DoubleQ(_, value) => *value,
805 Attr::SingleQ(_, value) => *value,
806 Attr::Empty(_) => "",
807 Attr::Unquoted(_, value) => *value,
808 }
809 }
810}
811
812impl<T: Debug> Debug for Attr<T> {
813 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
814 match self {
815 Attr::DoubleQ(key, value) => f
816 .debug_tuple("Attr::DoubleQ")
817 .field(key)
818 .field(value)
819 .finish(),
820 Attr::SingleQ(key, value) => f
821 .debug_tuple("Attr::SingleQ")
822 .field(key)
823 .field(value)
824 .finish(),
825 Attr::Empty(key) => f.debug_tuple("Attr::Empty").field(key).finish(),
826 Attr::Unquoted(key, value) => f
827 .debug_tuple("Attr::Unquoted")
828 .field(key)
829 .field(value)
830 .finish(),
831 }
832 }
833}
834
835/// Unpacks attribute key and value into tuple of this two elements.
836/// `None` value element is returned only for [`Attr::Empty`] variant.
837impl<T> From<Attr<T>> for (T, Option<T>) {
838 #[inline]
839 fn from(attr: Attr<T>) -> Self {
840 match attr {
841 Attr::DoubleQ(key, value) => (key, Some(value)),
842 Attr::SingleQ(key, value) => (key, Some(value)),
843 Attr::Empty(key) => (key, None),
844 Attr::Unquoted(key, value) => (key, Some(value)),
845 }
846 }
847}
848
849////////////////////////////////////////////////////////////////////////////////////////////////////
850
851type AttrResult = Result<Attr<Range<usize>>, AttrError>;
852
853#[derive(Clone, Copy, Debug)]
854enum State {
855 /// Iteration finished, iterator will return `None` to all [`IterState::next`]
856 /// requests.
857 Done,
858 /// The last attribute returned was deserialized successfully. Contains an
859 /// offset from which next attribute should be searched.
860 Next(usize),
861 /// The last attribute returns [`AttrError::UnquotedValue`], offset pointed
862 /// to the beginning of the value. Recover should skip a value
863 SkipValue(usize),
864 /// The last attribute returns [`AttrError::Duplicated`], offset pointed to
865 /// the equal (`=`) sign. Recover should skip it and a value
866 SkipEqValue(usize),
867}
868
869/// Number of attributes a start tag may have before the duplicate-name check
870/// switches from a direct linear scan of the previously seen names to a hash
871/// pre-filter (see [`IterState::check_for_duplicates`]).
872///
873/// Real-world start tags carry only a handful of attributes -- the busiest
874/// element in our benchmark corpus (`tests/documents/players.xml`) has 22 --
875/// where the scan is faster than hashing and needs no allocation. Larger tags
876/// are where the scan became the O(N²) CPU-DoS of [#969], so above this count we
877/// pay for a hash set to keep the whole tag O(N). The value sits just above the
878/// measured linear-vs-hash crossover.
879///
880/// [#969]: https://github.com/tafia/quick-xml/issues/969
881const SMALL_ATTRIBUTE_COUNT: usize = 32;
882
883/// A no-op [`Hasher`] for the `key_hashes` set, whose values are already 64-bit
884/// hashes of attribute names; re-hashing them with the default SipHash would be
885/// wasted work. Only `write_u64` is ever exercised (via `u64`'s `Hash` impl).
886#[derive(Default)]
887struct IdentityHasher(u64);
888
889impl Hasher for IdentityHasher {
890 #[inline]
891 fn finish(&self) -> u64 {
892 self.0
893 }
894
895 #[inline]
896 fn write(&mut self, _: &[u8]) {
897 // The set only ever stores `u64` keys, which route through `write_u64`.
898 unreachable!("IdentityHasher only supports u64 keys")
899 }
900
901 #[inline]
902 fn write_u64(&mut self, n: u64) {
903 self.0 = n;
904 }
905}
906
907/// Hashes a single attribute name. A fresh [`DefaultHasher`] per name keeps each
908/// hash independent (so it is also DoS-resistant on untrusted input).
909#[inline]
910fn hash_name(name: &[u8]) -> u64 {
911 let mut hasher = DefaultHasher::new();
912 hasher.write(name);
913 hasher.finish()
914}
915
916/// External iterator over spans of attribute key and value
917#[derive(Clone, Debug)]
918pub(crate) struct IterState {
919 /// Iteration state that determines what actions should be done before the
920 /// actual parsing of the next attribute
921 state: State,
922 /// If `true`, enables ability to parse unquoted values and key-only (empty)
923 /// attributes
924 html: bool,
925 /// If `true`, checks for duplicate names
926 check_duplicates: bool,
927 /// If `check_duplicates` is set, contains the ranges of already parsed attribute
928 /// names. We store a ranges instead of slices to able to report a previous
929 /// attribute position
930 keys: Vec<Range<usize>>,
931 /// 64-bit hashes of the byte content of `keys`, used as an O(1) pre-filter
932 /// once a start tag declares more than `SMALL_ATTRIBUTE_COUNT` attributes, so
933 /// the duplicate check stays O(N) over the whole tag instead of O(N²). The
934 /// values are already hashes, so the set stores them with `IdentityHasher`
935 /// instead of re-hashing. Allocated only when the threshold is crossed, so
936 /// small tags (and [`IterState::new`]) stay allocation-free and `const`.
937 key_hashes: Option<HashSet<u64, BuildHasherDefault<IdentityHasher>>>,
938}
939
940impl IterState {
941 pub const fn new(offset: usize, html: bool) -> Self {
942 Self {
943 state: State::Next(offset),
944 html,
945 check_duplicates: true,
946 keys: Vec::new(),
947 key_hashes: None,
948 }
949 }
950
951 /// Recover from an error that could have been made on a previous step.
952 /// Returns an offset from which parsing should continue.
953 /// If there no input left, returns `None`.
954 fn recover(&self, slice: &[u8]) -> Option<usize> {
955 match self.state {
956 State::Done => None,
957 State::Next(offset) if offset <= slice.len() => Some(offset),
958 State::Next(_) => None,
959 State::SkipValue(offset) => self.skip_value(slice, offset),
960 State::SkipEqValue(offset) => self.skip_eq_value(slice, offset),
961 }
962 }
963
964 /// Skip all characters up to first space symbol or end-of-input
965 #[inline]
966 #[allow(clippy::manual_map)]
967 fn skip_value(&self, slice: &[u8], offset: usize) -> Option<usize> {
968 let mut iter = (offset..).zip(slice[offset..].iter());
969
970 match iter.find(|&(_, &b)| is_whitespace(b)) {
971 // Input: ` key = value `
972 // | ^
973 // offset e
974 Some((e, _)) => Some(e),
975 // Input: ` key = value`
976 // | ^
977 // offset e = len()
978 None => None,
979 }
980 }
981
982 /// Skip all characters up to first space symbol or end-of-input
983 #[inline]
984 fn skip_eq_value(&self, slice: &[u8], offset: usize) -> Option<usize> {
985 let mut iter = (offset..).zip(slice[offset..].iter());
986
987 // Skip all up to the quote and get the quote type
988 let quote = match iter.find(|&(_, &b)| !is_whitespace(b)) {
989 // Input: ` key = "`
990 // | ^
991 // offset
992 Some((_, b'"')) => b'"',
993 // Input: ` key = '`
994 // | ^
995 // offset
996 Some((_, b'\'')) => b'\'',
997
998 // Input: ` key = x`
999 // | ^
1000 // offset
1001 Some((offset, _)) => return self.skip_value(slice, offset),
1002 // Input: ` key = `
1003 // | ^
1004 // offset
1005 None => return None,
1006 };
1007
1008 match iter.find(|&(_, &b)| b == quote) {
1009 // Input: ` key = " "`
1010 // ^
1011 Some((e, b'"')) => Some(e),
1012 // Input: ` key = ' '`
1013 // ^
1014 Some((e, _)) => Some(e),
1015
1016 // Input: ` key = " `
1017 // Input: ` key = ' `
1018 // ^
1019 // Closing quote not found
1020 None => None,
1021 }
1022 }
1023
1024 /// Checks that the attribute name `key` (a range into `slice`) was not seen
1025 /// earlier in the same start tag, recording it for subsequent checks.
1026 ///
1027 /// Small tags use a direct linear scan of [`Self::keys`]: for a handful of
1028 /// attributes that beats hashing and needs no allocation, which is the
1029 /// overwhelmingly common case. Once a tag declares more than
1030 /// `SMALL_ATTRIBUTE_COUNT` attributes -- where the scan would become the
1031 /// O(N²) CPU-DoS of [#969] -- it switches to a hash pre-filter that keeps the
1032 /// whole tag O(N).
1033 ///
1034 /// [#969]: https://github.com/tafia/quick-xml/issues/969
1035 #[inline]
1036 fn check_for_duplicates(
1037 &mut self,
1038 slice: &[u8],
1039 key: Range<usize>,
1040 ) -> Result<Range<usize>, AttrError> {
1041 if self.check_duplicates {
1042 if self.keys.len() >= SMALL_ATTRIBUTE_COUNT {
1043 return self.check_for_duplicates_hashed(slice, key);
1044 }
1045 if let Some(prev) = self
1046 .keys
1047 .iter()
1048 .find(|r| slice[(*r).clone()] == slice[key.clone()])
1049 {
1050 return Err(AttrError::Duplicated(key.start, prev.start));
1051 }
1052 self.keys.push(key.clone());
1053 }
1054 Ok(key)
1055 }
1056
1057 /// Cold path of [`Self::check_for_duplicates`] for start tags with many
1058 /// attributes: a [`HashSet`] of 64-bit name hashes acts as an O(1) pre-filter
1059 /// so iterating N attributes is O(N) rather than O(N²).
1060 #[cold]
1061 fn check_for_duplicates_hashed(
1062 &mut self,
1063 slice: &[u8],
1064 key: Range<usize>,
1065 ) -> Result<Range<usize>, AttrError> {
1066 let keys = &self.keys;
1067 let key_hashes = self.key_hashes.get_or_insert_with(|| {
1068 // First time over the threshold: seed the set with the names already
1069 // collected during the linear phase so the pre-filter knows them.
1070 let mut set = HashSet::with_capacity_and_hasher(
1071 keys.len() * 2,
1072 BuildHasherDefault::<IdentityHasher>::default(),
1073 );
1074 for r in keys {
1075 set.insert(hash_name(&slice[r.clone()]));
1076 }
1077 set
1078 });
1079 // A fresh hash proves the name is new. On a hit (a real duplicate, or the
1080 // astronomically rare 64-bit collision) fall back to the linear scan to
1081 // recover the exact previous position for `AttrError::Duplicated`.
1082 if !key_hashes.insert(hash_name(&slice[key.clone()])) {
1083 if let Some(prev) = self
1084 .keys
1085 .iter()
1086 .find(|r| slice[(*r).clone()] == slice[key.clone()])
1087 {
1088 return Err(AttrError::Duplicated(key.start, prev.start));
1089 }
1090 }
1091 self.keys.push(key.clone());
1092 Ok(key)
1093 }
1094
1095 /// # Parameters
1096 ///
1097 /// - `slice`: content of the tag, used for checking for duplicates
1098 /// - `key`: Range of key in slice, if iterator in HTML mode
1099 /// - `offset`: Position of error if iterator in XML mode
1100 #[inline]
1101 fn key_only(&mut self, slice: &[u8], key: Range<usize>, offset: usize) -> Option<AttrResult> {
1102 Some(if self.html {
1103 self.check_for_duplicates(slice, key).map(Attr::Empty)
1104 } else {
1105 Err(AttrError::ExpectedEq(offset))
1106 })
1107 }
1108
1109 #[inline]
1110 fn double_q(&mut self, key: Range<usize>, value: Range<usize>) -> Option<AttrResult> {
1111 self.state = State::Next(value.end + 1); // +1 for `"`
1112
1113 Some(Ok(Attr::DoubleQ(key, value)))
1114 }
1115
1116 #[inline]
1117 fn single_q(&mut self, key: Range<usize>, value: Range<usize>) -> Option<AttrResult> {
1118 self.state = State::Next(value.end + 1); // +1 for `'`
1119
1120 Some(Ok(Attr::SingleQ(key, value)))
1121 }
1122
1123 pub fn next(&mut self, slice: &[u8]) -> Option<AttrResult> {
1124 let mut iter = match self.recover(slice) {
1125 Some(offset) => (offset..).zip(slice[offset..].iter()),
1126 None => return None,
1127 };
1128
1129 // Index where next key started
1130 let start_key = match iter.find(|&(_, &b)| !is_whitespace(b)) {
1131 // Input: ` key`
1132 // ^
1133 Some((s, _)) => s,
1134 // Input: ` `
1135 // ^
1136 None => {
1137 // Because we reach end-of-input, stop iteration on next call
1138 self.state = State::Done;
1139 return None;
1140 }
1141 };
1142 // Span of a key
1143 let (key, offset) = match iter.find(|&(_, &b)| b == b'=' || is_whitespace(b)) {
1144 // Input: ` key=`
1145 // | ^
1146 // s e
1147 Some((e, b'=')) => (start_key..e, e),
1148
1149 // Input: ` key `
1150 // ^
1151 Some((e, _)) => match iter.find(|&(_, &b)| !is_whitespace(b)) {
1152 // Input: ` key =`
1153 // | | ^
1154 // start_key e
1155 Some((offset, b'=')) => (start_key..e, offset),
1156 // Input: ` key x`
1157 // | | ^
1158 // start_key e
1159 // If HTML-like attributes is allowed, this is the result, otherwise error
1160 Some((offset, _)) => {
1161 // In any case, recovering is not required
1162 self.state = State::Next(offset);
1163 return self.key_only(slice, start_key..e, offset);
1164 }
1165 // Input: ` key `
1166 // | | ^
1167 // start_key e
1168 // If HTML-like attributes is allowed, this is the result, otherwise error
1169 None => {
1170 // Because we reach end-of-input, stop iteration on next call
1171 self.state = State::Done;
1172 return self.key_only(slice, start_key..e, slice.len());
1173 }
1174 },
1175
1176 // Input: ` key`
1177 // | ^
1178 // s e = len()
1179 // If HTML-like attributes is allowed, this is the result, otherwise error
1180 None => {
1181 // Because we reach end-of-input, stop iteration on next call
1182 self.state = State::Done;
1183 let e = slice.len();
1184 return self.key_only(slice, start_key..e, e);
1185 }
1186 };
1187
1188 let key = match self.check_for_duplicates(slice, key) {
1189 Err(e) => {
1190 self.state = State::SkipEqValue(offset);
1191 return Some(Err(e));
1192 }
1193 Ok(key) => key,
1194 };
1195
1196 ////////////////////////////////////////////////////////////////////////
1197
1198 // Gets the position of quote and quote type
1199 let (start_value, quote) = match iter.find(|&(_, &b)| !is_whitespace(b)) {
1200 // Input: ` key = "`
1201 // ^
1202 Some((s, b'"')) => (s + 1, b'"'),
1203 // Input: ` key = '`
1204 // ^
1205 Some((s, b'\'')) => (s + 1, b'\''),
1206
1207 // Input: ` key = x`
1208 // ^
1209 // If HTML-like attributes is allowed, this is the start of the value
1210 Some((s, _)) if self.html => {
1211 // We do not check validity of attribute value characters as required
1212 // according to https://html.spec.whatwg.org/#unquoted. It can be done
1213 // during validation phase
1214 let end = match iter.find(|&(_, &b)| is_whitespace(b)) {
1215 // Input: ` key = value `
1216 // | ^
1217 // s e
1218 Some((e, _)) => e,
1219 // Input: ` key = value`
1220 // | ^
1221 // s e = len()
1222 None => slice.len(),
1223 };
1224 self.state = State::Next(end);
1225 return Some(Ok(Attr::Unquoted(key, s..end)));
1226 }
1227 // Input: ` key = x`
1228 // ^
1229 Some((s, _)) => {
1230 self.state = State::SkipValue(s);
1231 return Some(Err(AttrError::UnquotedValue(s)));
1232 }
1233
1234 // Input: ` key = `
1235 // ^
1236 None => {
1237 // Because we reach end-of-input, stop iteration on next call
1238 self.state = State::Done;
1239 return Some(Err(AttrError::ExpectedValue(slice.len())));
1240 }
1241 };
1242
1243 match iter.find(|&(_, &b)| b == quote) {
1244 // Input: ` key = " "`
1245 // ^
1246 Some((e, b'"')) => self.double_q(key, start_value..e),
1247 // Input: ` key = ' '`
1248 // ^
1249 Some((e, _)) => self.single_q(key, start_value..e),
1250
1251 // Input: ` key = " `
1252 // Input: ` key = ' `
1253 // ^
1254 // Closing quote not found
1255 None => {
1256 // Because we reach end-of-input, stop iteration on next call
1257 self.state = State::Done;
1258 Some(Err(AttrError::ExpectedQuote(slice.len(), quote)))
1259 }
1260 }
1261 }
1262}
1263
1264////////////////////////////////////////////////////////////////////////////////////////////////////
1265
1266/// Checks, how parsing of XML-style attributes works. Each attribute should
1267/// have a value, enclosed in single or double quotes.
1268#[cfg(test)]
1269mod xml {
1270 use super::*;
1271 use pretty_assertions::assert_eq;
1272
1273 #[test]
1274 fn start_position_at_end_is_empty() {
1275 let mut attributes = Attributes::new("a", 1);
1276 assert_eq!(attributes.next(), None);
1277
1278 let mut attributes = Attributes::html("a", 1);
1279 assert_eq!(attributes.next(), None);
1280
1281 let mut attributes = Attributes::new("a", 1);
1282 assert!(!attributes.has_nil(&NamespaceResolver::default()));
1283 }
1284
1285 #[test]
1286 fn start_position_past_end_is_empty() {
1287 let mut attributes = Attributes::new("a", 2);
1288 assert_eq!(attributes.next(), None);
1289
1290 let mut attributes = Attributes::html("a", 2);
1291 assert_eq!(attributes.next(), None);
1292
1293 let mut attributes = Attributes::new("a", 2);
1294 assert!(!attributes.has_nil(&NamespaceResolver::default()));
1295 }
1296
1297 mod attribute_value_normalization {
1298 use super::*;
1299 use crate::XmlVersion::*;
1300 use crate::errors::Error;
1301 use crate::escape::EscapeError::*;
1302 use pretty_assertions::assert_eq;
1303
1304 /// Empty values returned are unchanged
1305 #[test]
1306 fn empty() {
1307 let raw_value = "";
1308 let attr = Attribute {
1309 key: QName("foo"),
1310 value: Cow::Borrowed(raw_value),
1311 };
1312
1313 let value = attr.normalized_value(Implicit1_0).unwrap();
1314 assert_eq!(value, "");
1315 // assert_eq! does not check if value is borrowed, but this is important
1316 assert!(matches!(value, Cow::Borrowed(_)));
1317
1318 let value = attr.normalized_value(Explicit1_0).unwrap();
1319 assert_eq!(value, "");
1320 // assert_eq! does not check if value is borrowed, but this is important
1321 assert!(matches!(value, Cow::Borrowed(_)));
1322
1323 let value = attr.normalized_value(Explicit1_1).unwrap();
1324 assert_eq!(value, "");
1325 // assert_eq! does not check if value is borrowed, but this is important
1326 assert!(matches!(value, Cow::Borrowed(_)));
1327 }
1328
1329 /// Already normalized values are returned unchanged
1330 #[test]
1331 fn already_normalized() {
1332 let raw_value = "foobar123";
1333 let attr = Attribute {
1334 key: QName("foo"),
1335 value: Cow::Borrowed(raw_value),
1336 };
1337
1338 let value = attr.normalized_value(Implicit1_0).unwrap();
1339 assert_eq!(value, "foobar123");
1340 // assert_eq! does not check if value is borrowed, but this is important
1341 assert!(matches!(value, Cow::Borrowed(_)));
1342
1343 let value = attr.normalized_value(Explicit1_0).unwrap();
1344 assert_eq!(value, "foobar123");
1345 // assert_eq! does not check if value is borrowed, but this is important
1346 assert!(matches!(value, Cow::Borrowed(_)));
1347
1348 let value = attr.normalized_value(Explicit1_1).unwrap();
1349 assert_eq!(value, "foobar123");
1350 // assert_eq! does not check if value is borrowed, but this is important
1351 assert!(matches!(value, Cow::Borrowed(_)));
1352 }
1353
1354 /// Return, tab, and newline characters (0xD, 0x9, 0xA) must be substituted with
1355 /// a space character, \r\n and \r\u{85} should be replaced by one space in 1.1
1356 #[test]
1357 fn space_replacement() {
1358 let raw_value = "\r\nfoo\u{85}\u{2028}\rbar\tbaz\n\ndelta\n\r\u{85}";
1359 let attr = Attribute {
1360 key: QName("foo"),
1361 value: Cow::Borrowed(raw_value),
1362 };
1363
1364 assert_eq!(
1365 attr.normalized_value(Implicit1_0).unwrap(),
1366 " foo\u{85}\u{2028} bar baz delta \u{85}"
1367 );
1368 assert_eq!(
1369 attr.normalized_value(Explicit1_0).unwrap(),
1370 " foo\u{85}\u{2028} bar baz delta \u{85}"
1371 );
1372 assert_eq!(
1373 attr.normalized_value(Explicit1_1).unwrap(),
1374 " foo bar baz delta "
1375 );
1376 }
1377
1378 /// Entities must be terminated
1379 #[test]
1380 fn unterminated_entity() {
1381 let raw_value = "abc"def";
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; &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("
"),
1449 "a" => Some("
"),
1450 "da" => Some("
"),
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 = "

A

B
";
1476 let attr = Attribute {
1477 key: QName("foo"),
1478 value: Cow::Borrowed(raw_value),
1479 };
1480
1481 assert_eq!(
1482 attr.normalized_value(Implicit1_0).unwrap(),
1483 "\r\rA\n\nB\r\n"
1484 );
1485 assert_eq!(
1486 attr.normalized_value(Explicit1_0).unwrap(),
1487 "\r\rA\n\nB\r\n"
1488 );
1489 assert_eq!(
1490 attr.normalized_value(Explicit1_1).unwrap(),
1491 "\r\rA\n\nB\r\n"
1492 );
1493 }
1494 }
1495
1496 /// Checked attribute is the single attribute
1497 mod single {
1498 use super::*;
1499 use pretty_assertions::assert_eq;
1500
1501 /// Attribute have a value enclosed in single quotes
1502 #[test]
1503 fn single_quoted() {
1504 let mut iter = Attributes::new(r#"tag key='value'"#, 3);
1505
1506 assert_eq!(
1507 iter.next(),
1508 Some(Ok(Attribute {
1509 key: QName("key"),
1510 value: Cow::Borrowed("value"),
1511 }))
1512 );
1513 assert_eq!(iter.next(), None);
1514 assert_eq!(iter.next(), None);
1515 }
1516
1517 /// Attribute have a value enclosed in double quotes
1518 #[test]
1519 fn double_quoted() {
1520 let mut iter = Attributes::new(r#"tag key="value""#, 3);
1521
1522 assert_eq!(
1523 iter.next(),
1524 Some(Ok(Attribute {
1525 key: QName("key"),
1526 value: Cow::Borrowed("value"),
1527 }))
1528 );
1529 assert_eq!(iter.next(), None);
1530 assert_eq!(iter.next(), None);
1531 }
1532
1533 /// Attribute have a value, not enclosed in quotes
1534 #[test]
1535 fn unquoted() {
1536 let mut iter = Attributes::new(r#"tag key=value"#, 3);
1537 // 0 ^ = 8
1538
1539 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(8))));
1540 assert_eq!(iter.next(), None);
1541 assert_eq!(iter.next(), None);
1542 }
1543
1544 /// Only attribute key is present
1545 #[test]
1546 fn key_only() {
1547 let mut iter = Attributes::new(r#"tag key"#, 3);
1548 // 0 ^ = 7
1549
1550 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(7))));
1551 assert_eq!(iter.next(), None);
1552 assert_eq!(iter.next(), None);
1553 }
1554
1555 /// Key is started with an invalid symbol (a single quote in this test).
1556 /// Because we do not check validity of keys and values during parsing,
1557 /// that invalid attribute will be returned
1558 #[test]
1559 fn key_start_invalid() {
1560 let mut iter = Attributes::new(r#"tag 'key'='value'"#, 3);
1561
1562 assert_eq!(
1563 iter.next(),
1564 Some(Ok(Attribute {
1565 key: QName("'key'"),
1566 value: Cow::Borrowed("value"),
1567 }))
1568 );
1569 assert_eq!(iter.next(), None);
1570 assert_eq!(iter.next(), None);
1571 }
1572
1573 /// Key contains an invalid symbol (an ampersand in this test).
1574 /// Because we do not check validity of keys and values during parsing,
1575 /// that invalid attribute will be returned
1576 #[test]
1577 fn key_contains_invalid() {
1578 let mut iter = Attributes::new(r#"tag key&jey='value'"#, 3);
1579
1580 assert_eq!(
1581 iter.next(),
1582 Some(Ok(Attribute {
1583 key: QName("key&jey"),
1584 value: Cow::Borrowed("value"),
1585 }))
1586 );
1587 assert_eq!(iter.next(), None);
1588 assert_eq!(iter.next(), None);
1589 }
1590
1591 /// Attribute value is missing after `=`
1592 #[test]
1593 fn missed_value() {
1594 let mut iter = Attributes::new(r#"tag key="#, 3);
1595 // 0 ^ = 8
1596
1597 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(8))));
1598 assert_eq!(iter.next(), None);
1599 assert_eq!(iter.next(), None);
1600 }
1601 }
1602
1603 /// Checked attribute is the first attribute in the list of many attributes
1604 mod first {
1605 use super::*;
1606 use pretty_assertions::assert_eq;
1607
1608 /// Attribute have a value enclosed in single quotes
1609 #[test]
1610 fn single_quoted() {
1611 let mut iter = Attributes::new(r#"tag key='value' regular='attribute'"#, 3);
1612
1613 assert_eq!(
1614 iter.next(),
1615 Some(Ok(Attribute {
1616 key: QName("key"),
1617 value: Cow::Borrowed("value"),
1618 }))
1619 );
1620 assert_eq!(
1621 iter.next(),
1622 Some(Ok(Attribute {
1623 key: QName("regular"),
1624 value: Cow::Borrowed("attribute"),
1625 }))
1626 );
1627 assert_eq!(iter.next(), None);
1628 assert_eq!(iter.next(), None);
1629 }
1630
1631 /// Attribute have a value enclosed in double quotes
1632 #[test]
1633 fn double_quoted() {
1634 let mut iter = Attributes::new(r#"tag key="value" regular='attribute'"#, 3);
1635
1636 assert_eq!(
1637 iter.next(),
1638 Some(Ok(Attribute {
1639 key: QName("key"),
1640 value: Cow::Borrowed("value"),
1641 }))
1642 );
1643 assert_eq!(
1644 iter.next(),
1645 Some(Ok(Attribute {
1646 key: QName("regular"),
1647 value: Cow::Borrowed("attribute"),
1648 }))
1649 );
1650 assert_eq!(iter.next(), None);
1651 assert_eq!(iter.next(), None);
1652 }
1653
1654 /// Attribute have a value, not enclosed in quotes
1655 #[test]
1656 fn unquoted() {
1657 let mut iter = Attributes::new(r#"tag key=value regular='attribute'"#, 3);
1658 // 0 ^ = 8
1659
1660 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(8))));
1661 // check error recovery
1662 assert_eq!(
1663 iter.next(),
1664 Some(Ok(Attribute {
1665 key: QName("regular"),
1666 value: Cow::Borrowed("attribute"),
1667 }))
1668 );
1669 assert_eq!(iter.next(), None);
1670 assert_eq!(iter.next(), None);
1671 }
1672
1673 /// Only attribute key is present
1674 #[test]
1675 fn key_only() {
1676 let mut iter = Attributes::new(r#"tag key regular='attribute'"#, 3);
1677 // 0 ^ = 8
1678
1679 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(8))));
1680 // check error recovery
1681 assert_eq!(
1682 iter.next(),
1683 Some(Ok(Attribute {
1684 key: QName("regular"),
1685 value: Cow::Borrowed("attribute"),
1686 }))
1687 );
1688 assert_eq!(iter.next(), None);
1689 assert_eq!(iter.next(), None);
1690 }
1691
1692 /// Key is started with an invalid symbol (a single quote in this test).
1693 /// Because we do not check validity of keys and values during parsing,
1694 /// that invalid attribute will be returned
1695 #[test]
1696 fn key_start_invalid() {
1697 let mut iter = Attributes::new(r#"tag 'key'='value' regular='attribute'"#, 3);
1698
1699 assert_eq!(
1700 iter.next(),
1701 Some(Ok(Attribute {
1702 key: QName("'key'"),
1703 value: Cow::Borrowed("value"),
1704 }))
1705 );
1706 assert_eq!(
1707 iter.next(),
1708 Some(Ok(Attribute {
1709 key: QName("regular"),
1710 value: Cow::Borrowed("attribute"),
1711 }))
1712 );
1713 assert_eq!(iter.next(), None);
1714 assert_eq!(iter.next(), None);
1715 }
1716
1717 /// Key contains an invalid symbol (an ampersand in this test).
1718 /// Because we do not check validity of keys and values during parsing,
1719 /// that invalid attribute will be returned
1720 #[test]
1721 fn key_contains_invalid() {
1722 let mut iter = Attributes::new(r#"tag key&jey='value' regular='attribute'"#, 3);
1723
1724 assert_eq!(
1725 iter.next(),
1726 Some(Ok(Attribute {
1727 key: QName("key&jey"),
1728 value: Cow::Borrowed("value"),
1729 }))
1730 );
1731 assert_eq!(
1732 iter.next(),
1733 Some(Ok(Attribute {
1734 key: QName("regular"),
1735 value: Cow::Borrowed("attribute"),
1736 }))
1737 );
1738 assert_eq!(iter.next(), None);
1739 assert_eq!(iter.next(), None);
1740 }
1741
1742 /// Attribute value is missing after `=`.
1743 #[test]
1744 fn missed_value() {
1745 let mut iter = Attributes::new(r#"tag key= regular='attribute'"#, 3);
1746 // 0 ^ = 9
1747
1748 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1749 // Because we do not check validity of keys and values during parsing,
1750 // "error='recovery'" is considered, as unquoted attribute value and
1751 // skipped during recovery and iteration finished
1752 assert_eq!(iter.next(), None);
1753 assert_eq!(iter.next(), None);
1754
1755 ////////////////////////////////////////////////////////////////////
1756
1757 let mut iter = Attributes::new(r#"tag key= regular= 'attribute'"#, 3);
1758 // 0 ^ = 9 ^ = 29
1759
1760 // In that case "regular=" considered as unquoted value
1761 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1762 // In that case "'attribute'" considered as a key, because we do not check
1763 // validity of key names
1764 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(29))));
1765 assert_eq!(iter.next(), None);
1766 assert_eq!(iter.next(), None);
1767
1768 ////////////////////////////////////////////////////////////////////
1769
1770 let mut iter = Attributes::new(r#"tag key= regular ='attribute'"#, 3);
1771 // 0 ^ = 9 ^ = 29
1772
1773 // In that case "regular" considered as unquoted value
1774 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1775 // In that case "='attribute'" considered as a key, because we do not check
1776 // validity of key names
1777 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(29))));
1778 assert_eq!(iter.next(), None);
1779 assert_eq!(iter.next(), None);
1780
1781 ////////////////////////////////////////////////////////////////////
1782
1783 let mut iter = Attributes::new(r#"tag key= regular = 'attribute'"#, 3);
1784 // 0 ^ = 9 ^ = 19 ^ = 30
1785
1786 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1787 // In that case second "=" considered as a key, because we do not check
1788 // validity of key names
1789 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(19))));
1790 // In that case "'attribute'" considered as a key, because we do not check
1791 // validity of key names
1792 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(30))));
1793 assert_eq!(iter.next(), None);
1794 assert_eq!(iter.next(), None);
1795 }
1796 }
1797
1798 /// Copy of single, but with additional spaces in markup
1799 mod sparsed {
1800 use super::*;
1801 use pretty_assertions::assert_eq;
1802
1803 /// Attribute have a value enclosed in single quotes
1804 #[test]
1805 fn single_quoted() {
1806 let mut iter = Attributes::new(r#"tag key = 'value' "#, 3);
1807
1808 assert_eq!(
1809 iter.next(),
1810 Some(Ok(Attribute {
1811 key: QName("key"),
1812 value: Cow::Borrowed("value"),
1813 }))
1814 );
1815 assert_eq!(iter.next(), None);
1816 assert_eq!(iter.next(), None);
1817 }
1818
1819 /// Attribute have a value enclosed in double quotes
1820 #[test]
1821 fn double_quoted() {
1822 let mut iter = Attributes::new(r#"tag key = "value" "#, 3);
1823
1824 assert_eq!(
1825 iter.next(),
1826 Some(Ok(Attribute {
1827 key: QName("key"),
1828 value: Cow::Borrowed("value"),
1829 }))
1830 );
1831 assert_eq!(iter.next(), None);
1832 assert_eq!(iter.next(), None);
1833 }
1834
1835 /// Attribute have a value, not enclosed in quotes
1836 #[test]
1837 fn unquoted() {
1838 let mut iter = Attributes::new(r#"tag key = value "#, 3);
1839 // 0 ^ = 10
1840
1841 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(10))));
1842 assert_eq!(iter.next(), None);
1843 assert_eq!(iter.next(), None);
1844 }
1845
1846 /// Only attribute key is present
1847 #[test]
1848 fn key_only() {
1849 let mut iter = Attributes::new(r#"tag key "#, 3);
1850 // 0 ^ = 8
1851
1852 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(8))));
1853 assert_eq!(iter.next(), None);
1854 assert_eq!(iter.next(), None);
1855 }
1856
1857 /// Key is started with an invalid symbol (a single quote in this test).
1858 /// Because we do not check validity of keys and values during parsing,
1859 /// that invalid attribute will be returned
1860 #[test]
1861 fn key_start_invalid() {
1862 let mut iter = Attributes::new(r#"tag 'key' = 'value' "#, 3);
1863
1864 assert_eq!(
1865 iter.next(),
1866 Some(Ok(Attribute {
1867 key: QName("'key'"),
1868 value: Cow::Borrowed("value"),
1869 }))
1870 );
1871 assert_eq!(iter.next(), None);
1872 assert_eq!(iter.next(), None);
1873 }
1874
1875 /// Key contains an invalid symbol (an ampersand in this test).
1876 /// Because we do not check validity of keys and values during parsing,
1877 /// that invalid attribute will be returned
1878 #[test]
1879 fn key_contains_invalid() {
1880 let mut iter = Attributes::new(r#"tag key&jey = 'value' "#, 3);
1881
1882 assert_eq!(
1883 iter.next(),
1884 Some(Ok(Attribute {
1885 key: QName("key&jey"),
1886 value: Cow::Borrowed("value"),
1887 }))
1888 );
1889 assert_eq!(iter.next(), None);
1890 assert_eq!(iter.next(), None);
1891 }
1892
1893 /// Attribute value is missing after `=`
1894 #[test]
1895 fn missed_value() {
1896 let mut iter = Attributes::new(r#"tag key = "#, 3);
1897 // 0 ^ = 10
1898
1899 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(10))));
1900 assert_eq!(iter.next(), None);
1901 assert_eq!(iter.next(), None);
1902 }
1903 }
1904
1905 /// Checks that duplicated attributes correctly reported and recovering is
1906 /// possible after that
1907 mod duplicated {
1908 use super::*;
1909
1910 mod with_check {
1911 use super::*;
1912 use pretty_assertions::assert_eq;
1913
1914 /// Attribute have a value enclosed in single quotes
1915 #[test]
1916 fn single_quoted() {
1917 let mut iter = Attributes::new(r#"tag key='value' key='dup' another=''"#, 3);
1918 // 0 ^ = 4 ^ = 16
1919
1920 assert_eq!(
1921 iter.next(),
1922 Some(Ok(Attribute {
1923 key: QName("key"),
1924 value: Cow::Borrowed("value"),
1925 }))
1926 );
1927 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
1928 assert_eq!(
1929 iter.next(),
1930 Some(Ok(Attribute {
1931 key: QName("another"),
1932 value: Cow::Borrowed(""),
1933 }))
1934 );
1935 assert_eq!(iter.next(), None);
1936 assert_eq!(iter.next(), None);
1937 }
1938
1939 /// Attribute have a value enclosed in double quotes
1940 #[test]
1941 fn double_quoted() {
1942 let mut iter = Attributes::new(r#"tag key='value' key="dup" another=''"#, 3);
1943 // 0 ^ = 4 ^ = 16
1944
1945 assert_eq!(
1946 iter.next(),
1947 Some(Ok(Attribute {
1948 key: QName("key"),
1949 value: Cow::Borrowed("value"),
1950 }))
1951 );
1952 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
1953 assert_eq!(
1954 iter.next(),
1955 Some(Ok(Attribute {
1956 key: QName("another"),
1957 value: Cow::Borrowed(""),
1958 }))
1959 );
1960 assert_eq!(iter.next(), None);
1961 assert_eq!(iter.next(), None);
1962 }
1963
1964 /// Attribute have a value, not enclosed in quotes
1965 #[test]
1966 fn unquoted() {
1967 let mut iter = Attributes::new(r#"tag key='value' key=dup another=''"#, 3);
1968 // 0 ^ = 4 ^ = 16
1969
1970 assert_eq!(
1971 iter.next(),
1972 Some(Ok(Attribute {
1973 key: QName("key"),
1974 value: Cow::Borrowed("value"),
1975 }))
1976 );
1977 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
1978 assert_eq!(
1979 iter.next(),
1980 Some(Ok(Attribute {
1981 key: QName("another"),
1982 value: Cow::Borrowed(""),
1983 }))
1984 );
1985 assert_eq!(iter.next(), None);
1986 assert_eq!(iter.next(), None);
1987 }
1988
1989 /// Only attribute key is present
1990 #[test]
1991 fn key_only() {
1992 let mut iter = Attributes::new(r#"tag key='value' key another=''"#, 3);
1993 // 0 ^ = 20
1994
1995 assert_eq!(
1996 iter.next(),
1997 Some(Ok(Attribute {
1998 key: QName("key"),
1999 value: Cow::Borrowed("value"),
2000 }))
2001 );
2002 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(20))));
2003 assert_eq!(
2004 iter.next(),
2005 Some(Ok(Attribute {
2006 key: QName("another"),
2007 value: Cow::Borrowed(""),
2008 }))
2009 );
2010 assert_eq!(iter.next(), None);
2011 assert_eq!(iter.next(), None);
2012 }
2013
2014 /// Once a start tag declares more than `SMALL_ATTRIBUTE_COUNT`
2015 /// attributes the duplicate check switches to its hash-based path. A
2016 /// duplicate of a name first seen during the earlier linear phase must
2017 /// still be detected, with the original position reported. Regression
2018 /// cover for the cold path of [#969].
2019 ///
2020 /// [#969]: https://github.com/tafia/quick-xml/issues/969
2021 #[test]
2022 fn duplicate_past_hash_threshold() {
2023 let dup = SMALL_ATTRIBUTE_COUNT / 2;
2024 let n = SMALL_ATTRIBUTE_COUNT + 8;
2025
2026 let mut source = String::from("tag");
2027 let mut positions = Vec::with_capacity(n);
2028 for i in 0..n {
2029 source.push(' ');
2030 positions.push(source.len());
2031 source.push_str(&format!("k{:04}=''", i));
2032 }
2033 // Repeat the name first seen at `positions[dup]` (linear phase).
2034 source.push(' ');
2035 let dup_pos = source.len();
2036 source.push_str(&format!("k{:04}=''", dup));
2037
2038 let mut iter = Attributes::new(&source, 3);
2039 for _ in 0..n {
2040 assert!(matches!(iter.next(), Some(Ok(_))));
2041 }
2042 assert_eq!(
2043 iter.next(),
2044 Some(Err(AttrError::Duplicated(dup_pos, positions[dup])))
2045 );
2046 }
2047 }
2048
2049 /// Check for duplicated names is disabled
2050 mod without_check {
2051 use super::*;
2052 use pretty_assertions::assert_eq;
2053
2054 /// Attribute have a value enclosed in single quotes
2055 #[test]
2056 fn single_quoted() {
2057 let mut iter = Attributes::new(r#"tag key='value' key='dup' another=''"#, 3);
2058 iter.with_checks(false);
2059
2060 assert_eq!(
2061 iter.next(),
2062 Some(Ok(Attribute {
2063 key: QName("key"),
2064 value: Cow::Borrowed("value"),
2065 }))
2066 );
2067 assert_eq!(
2068 iter.next(),
2069 Some(Ok(Attribute {
2070 key: QName("key"),
2071 value: Cow::Borrowed("dup"),
2072 }))
2073 );
2074 assert_eq!(
2075 iter.next(),
2076 Some(Ok(Attribute {
2077 key: QName("another"),
2078 value: Cow::Borrowed(""),
2079 }))
2080 );
2081 assert_eq!(iter.next(), None);
2082 assert_eq!(iter.next(), None);
2083 }
2084
2085 /// Attribute have a value enclosed in double quotes
2086 #[test]
2087 fn double_quoted() {
2088 let mut iter = Attributes::new(r#"tag key='value' key="dup" another=''"#, 3);
2089 iter.with_checks(false);
2090
2091 assert_eq!(
2092 iter.next(),
2093 Some(Ok(Attribute {
2094 key: QName("key"),
2095 value: Cow::Borrowed("value"),
2096 }))
2097 );
2098 assert_eq!(
2099 iter.next(),
2100 Some(Ok(Attribute {
2101 key: QName("key"),
2102 value: Cow::Borrowed("dup"),
2103 }))
2104 );
2105 assert_eq!(
2106 iter.next(),
2107 Some(Ok(Attribute {
2108 key: QName("another"),
2109 value: Cow::Borrowed(""),
2110 }))
2111 );
2112 assert_eq!(iter.next(), None);
2113 assert_eq!(iter.next(), None);
2114 }
2115
2116 /// Attribute have a value, not enclosed in quotes
2117 #[test]
2118 fn unquoted() {
2119 let mut iter = Attributes::new(r#"tag key='value' key=dup another=''"#, 3);
2120 // 0 ^ = 20
2121 iter.with_checks(false);
2122
2123 assert_eq!(
2124 iter.next(),
2125 Some(Ok(Attribute {
2126 key: QName("key"),
2127 value: Cow::Borrowed("value"),
2128 }))
2129 );
2130 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(20))));
2131 assert_eq!(
2132 iter.next(),
2133 Some(Ok(Attribute {
2134 key: QName("another"),
2135 value: Cow::Borrowed(""),
2136 }))
2137 );
2138 assert_eq!(iter.next(), None);
2139 assert_eq!(iter.next(), None);
2140 }
2141
2142 /// Only attribute key is present
2143 #[test]
2144 fn key_only() {
2145 let mut iter = Attributes::new(r#"tag key='value' key another=''"#, 3);
2146 // 0 ^ = 20
2147 iter.with_checks(false);
2148
2149 assert_eq!(
2150 iter.next(),
2151 Some(Ok(Attribute {
2152 key: QName("key"),
2153 value: Cow::Borrowed("value"),
2154 }))
2155 );
2156 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(20))));
2157 assert_eq!(
2158 iter.next(),
2159 Some(Ok(Attribute {
2160 key: QName("another"),
2161 value: Cow::Borrowed(""),
2162 }))
2163 );
2164 assert_eq!(iter.next(), None);
2165 assert_eq!(iter.next(), None);
2166 }
2167 }
2168 }
2169
2170 #[test]
2171 fn mixed_quote() {
2172 let mut iter = Attributes::new(r#"tag a='a' b = "b" c='cc"cc' d="dd'dd""#, 3);
2173
2174 assert_eq!(
2175 iter.next(),
2176 Some(Ok(Attribute {
2177 key: QName("a"),
2178 value: Cow::Borrowed("a"),
2179 }))
2180 );
2181 assert_eq!(
2182 iter.next(),
2183 Some(Ok(Attribute {
2184 key: QName("b"),
2185 value: Cow::Borrowed("b"),
2186 }))
2187 );
2188 assert_eq!(
2189 iter.next(),
2190 Some(Ok(Attribute {
2191 key: QName("c"),
2192 value: Cow::Borrowed(r#"cc"cc"#),
2193 }))
2194 );
2195 assert_eq!(
2196 iter.next(),
2197 Some(Ok(Attribute {
2198 key: QName("d"),
2199 value: Cow::Borrowed("dd'dd"),
2200 }))
2201 );
2202 assert_eq!(iter.next(), None);
2203 assert_eq!(iter.next(), None);
2204 }
2205}
2206
2207/// Checks, how parsing of HTML-style attributes works. Each attribute can be
2208/// in three forms:
2209/// - XML-like: have a value, enclosed in single or double quotes
2210/// - have a value, do not enclosed in quotes
2211/// - without value, key only
2212#[cfg(test)]
2213mod html {
2214 use super::*;
2215 use pretty_assertions::assert_eq;
2216
2217 /// Checked attribute is the single attribute
2218 mod single {
2219 use super::*;
2220 use pretty_assertions::assert_eq;
2221
2222 /// Attribute have a value enclosed in single quotes
2223 #[test]
2224 fn single_quoted() {
2225 let mut iter = Attributes::html(r#"tag key='value'"#, 3);
2226
2227 assert_eq!(
2228 iter.next(),
2229 Some(Ok(Attribute {
2230 key: QName("key"),
2231 value: Cow::Borrowed("value"),
2232 }))
2233 );
2234 assert_eq!(iter.next(), None);
2235 assert_eq!(iter.next(), None);
2236 }
2237
2238 /// Attribute have a value enclosed in double quotes
2239 #[test]
2240 fn double_quoted() {
2241 let mut iter = Attributes::html(r#"tag key="value""#, 3);
2242
2243 assert_eq!(
2244 iter.next(),
2245 Some(Ok(Attribute {
2246 key: QName("key"),
2247 value: Cow::Borrowed("value"),
2248 }))
2249 );
2250 assert_eq!(iter.next(), None);
2251 assert_eq!(iter.next(), None);
2252 }
2253
2254 /// Attribute have a value, not enclosed in quotes
2255 #[test]
2256 fn unquoted() {
2257 let mut iter = Attributes::html(r#"tag key=value"#, 3);
2258
2259 assert_eq!(
2260 iter.next(),
2261 Some(Ok(Attribute {
2262 key: QName("key"),
2263 value: Cow::Borrowed("value"),
2264 }))
2265 );
2266 assert_eq!(iter.next(), None);
2267 assert_eq!(iter.next(), None);
2268 }
2269
2270 /// Only attribute key is present
2271 #[test]
2272 fn key_only() {
2273 let mut iter = Attributes::html(r#"tag key"#, 3);
2274
2275 assert_eq!(
2276 iter.next(),
2277 Some(Ok(Attribute {
2278 key: QName("key"),
2279 value: Cow::Borrowed(""),
2280 }))
2281 );
2282 assert_eq!(iter.next(), None);
2283 assert_eq!(iter.next(), None);
2284 }
2285
2286 /// Key is started with an invalid symbol (a single quote in this test).
2287 /// Because we do not check validity of keys and values during parsing,
2288 /// that invalid attribute will be returned
2289 #[test]
2290 fn key_start_invalid() {
2291 let mut iter = Attributes::html(r#"tag 'key'='value'"#, 3);
2292
2293 assert_eq!(
2294 iter.next(),
2295 Some(Ok(Attribute {
2296 key: QName("'key'"),
2297 value: Cow::Borrowed("value"),
2298 }))
2299 );
2300 assert_eq!(iter.next(), None);
2301 assert_eq!(iter.next(), None);
2302 }
2303
2304 /// Key contains an invalid symbol (an ampersand in this test).
2305 /// Because we do not check validity of keys and values during parsing,
2306 /// that invalid attribute will be returned
2307 #[test]
2308 fn key_contains_invalid() {
2309 let mut iter = Attributes::html(r#"tag key&jey='value'"#, 3);
2310
2311 assert_eq!(
2312 iter.next(),
2313 Some(Ok(Attribute {
2314 key: QName("key&jey"),
2315 value: Cow::Borrowed("value"),
2316 }))
2317 );
2318 assert_eq!(iter.next(), None);
2319 assert_eq!(iter.next(), None);
2320 }
2321
2322 /// Attribute value is missing after `=`
2323 #[test]
2324 fn missed_value() {
2325 let mut iter = Attributes::html(r#"tag key="#, 3);
2326 // 0 ^ = 8
2327
2328 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(8))));
2329 assert_eq!(iter.next(), None);
2330 assert_eq!(iter.next(), None);
2331 }
2332 }
2333
2334 /// Checked attribute is the first attribute in the list of many attributes
2335 mod first {
2336 use super::*;
2337 use pretty_assertions::assert_eq;
2338
2339 /// Attribute have a value enclosed in single quotes
2340 #[test]
2341 fn single_quoted() {
2342 let mut iter = Attributes::html(r#"tag key='value' regular='attribute'"#, 3);
2343
2344 assert_eq!(
2345 iter.next(),
2346 Some(Ok(Attribute {
2347 key: QName("key"),
2348 value: Cow::Borrowed("value"),
2349 }))
2350 );
2351 assert_eq!(
2352 iter.next(),
2353 Some(Ok(Attribute {
2354 key: QName("regular"),
2355 value: Cow::Borrowed("attribute"),
2356 }))
2357 );
2358 assert_eq!(iter.next(), None);
2359 assert_eq!(iter.next(), None);
2360 }
2361
2362 /// Attribute have a value enclosed in double quotes
2363 #[test]
2364 fn double_quoted() {
2365 let mut iter = Attributes::html(r#"tag key="value" regular='attribute'"#, 3);
2366
2367 assert_eq!(
2368 iter.next(),
2369 Some(Ok(Attribute {
2370 key: QName("key"),
2371 value: Cow::Borrowed("value"),
2372 }))
2373 );
2374 assert_eq!(
2375 iter.next(),
2376 Some(Ok(Attribute {
2377 key: QName("regular"),
2378 value: Cow::Borrowed("attribute"),
2379 }))
2380 );
2381 assert_eq!(iter.next(), None);
2382 assert_eq!(iter.next(), None);
2383 }
2384
2385 /// Attribute have a value, not enclosed in quotes
2386 #[test]
2387 fn unquoted() {
2388 let mut iter = Attributes::html(r#"tag key=value regular='attribute'"#, 3);
2389
2390 assert_eq!(
2391 iter.next(),
2392 Some(Ok(Attribute {
2393 key: QName("key"),
2394 value: Cow::Borrowed("value"),
2395 }))
2396 );
2397 assert_eq!(
2398 iter.next(),
2399 Some(Ok(Attribute {
2400 key: QName("regular"),
2401 value: Cow::Borrowed("attribute"),
2402 }))
2403 );
2404 assert_eq!(iter.next(), None);
2405 assert_eq!(iter.next(), None);
2406 }
2407
2408 /// Only attribute key is present
2409 #[test]
2410 fn key_only() {
2411 let mut iter = Attributes::html(r#"tag key regular='attribute'"#, 3);
2412
2413 assert_eq!(
2414 iter.next(),
2415 Some(Ok(Attribute {
2416 key: QName("key"),
2417 value: Cow::Borrowed(""),
2418 }))
2419 );
2420 assert_eq!(
2421 iter.next(),
2422 Some(Ok(Attribute {
2423 key: QName("regular"),
2424 value: Cow::Borrowed("attribute"),
2425 }))
2426 );
2427 assert_eq!(iter.next(), None);
2428 assert_eq!(iter.next(), None);
2429 }
2430
2431 /// Key is started with an invalid symbol (a single quote in this test).
2432 /// Because we do not check validity of keys and values during parsing,
2433 /// that invalid attribute will be returned
2434 #[test]
2435 fn key_start_invalid() {
2436 let mut iter = Attributes::html(r#"tag 'key'='value' regular='attribute'"#, 3);
2437
2438 assert_eq!(
2439 iter.next(),
2440 Some(Ok(Attribute {
2441 key: QName("'key'"),
2442 value: Cow::Borrowed("value"),
2443 }))
2444 );
2445 assert_eq!(
2446 iter.next(),
2447 Some(Ok(Attribute {
2448 key: QName("regular"),
2449 value: Cow::Borrowed("attribute"),
2450 }))
2451 );
2452 assert_eq!(iter.next(), None);
2453 assert_eq!(iter.next(), None);
2454 }
2455
2456 /// Key contains an invalid symbol (an ampersand in this test).
2457 /// Because we do not check validity of keys and values during parsing,
2458 /// that invalid attribute will be returned
2459 #[test]
2460 fn key_contains_invalid() {
2461 let mut iter = Attributes::html(r#"tag key&jey='value' regular='attribute'"#, 3);
2462
2463 assert_eq!(
2464 iter.next(),
2465 Some(Ok(Attribute {
2466 key: QName("key&jey"),
2467 value: Cow::Borrowed("value"),
2468 }))
2469 );
2470 assert_eq!(
2471 iter.next(),
2472 Some(Ok(Attribute {
2473 key: QName("regular"),
2474 value: Cow::Borrowed("attribute"),
2475 }))
2476 );
2477 assert_eq!(iter.next(), None);
2478 assert_eq!(iter.next(), None);
2479 }
2480
2481 /// Attribute value is missing after `=`
2482 #[test]
2483 fn missed_value() {
2484 let mut iter = Attributes::html(r#"tag key= regular='attribute'"#, 3);
2485
2486 // Because we do not check validity of keys and values during parsing,
2487 // "regular='attribute'" is considered as unquoted attribute value
2488 assert_eq!(
2489 iter.next(),
2490 Some(Ok(Attribute {
2491 key: QName("key"),
2492 value: Cow::Borrowed("regular='attribute'"),
2493 }))
2494 );
2495 assert_eq!(iter.next(), None);
2496 assert_eq!(iter.next(), None);
2497
2498 ////////////////////////////////////////////////////////////////////
2499
2500 let mut iter = Attributes::html(r#"tag key= regular= 'attribute'"#, 3);
2501
2502 // Because we do not check validity of keys and values during parsing,
2503 // "regular=" is considered as unquoted attribute value
2504 assert_eq!(
2505 iter.next(),
2506 Some(Ok(Attribute {
2507 key: QName("key"),
2508 value: Cow::Borrowed("regular="),
2509 }))
2510 );
2511 // Because we do not check validity of keys and values during parsing,
2512 // "'attribute'" is considered as key-only attribute
2513 assert_eq!(
2514 iter.next(),
2515 Some(Ok(Attribute {
2516 key: QName("'attribute'"),
2517 value: Cow::Borrowed(""),
2518 }))
2519 );
2520 assert_eq!(iter.next(), None);
2521 assert_eq!(iter.next(), None);
2522
2523 ////////////////////////////////////////////////////////////////////
2524
2525 let mut iter = Attributes::html(r#"tag key= regular ='attribute'"#, 3);
2526
2527 // Because we do not check validity of keys and values during parsing,
2528 // "regular" is considered as unquoted attribute value
2529 assert_eq!(
2530 iter.next(),
2531 Some(Ok(Attribute {
2532 key: QName("key"),
2533 value: Cow::Borrowed("regular"),
2534 }))
2535 );
2536 // Because we do not check validity of keys and values during parsing,
2537 // "='attribute'" is considered as key-only attribute
2538 assert_eq!(
2539 iter.next(),
2540 Some(Ok(Attribute {
2541 key: QName("='attribute'"),
2542 value: Cow::Borrowed(""),
2543 }))
2544 );
2545 assert_eq!(iter.next(), None);
2546 assert_eq!(iter.next(), None);
2547
2548 ////////////////////////////////////////////////////////////////////
2549
2550 let mut iter = Attributes::html(r#"tag key= regular = 'attribute'"#, 3);
2551 // 0 ^ = 9 ^ = 19 ^ = 30
2552
2553 // Because we do not check validity of keys and values during parsing,
2554 // "regular" is considered as unquoted attribute value
2555 assert_eq!(
2556 iter.next(),
2557 Some(Ok(Attribute {
2558 key: QName("key"),
2559 value: Cow::Borrowed("regular"),
2560 }))
2561 );
2562 // Because we do not check validity of keys and values during parsing,
2563 // "=" is considered as key-only attribute
2564 assert_eq!(
2565 iter.next(),
2566 Some(Ok(Attribute {
2567 key: QName("="),
2568 value: Cow::Borrowed(""),
2569 }))
2570 );
2571 // Because we do not check validity of keys and values during parsing,
2572 // "'attribute'" is considered as key-only attribute
2573 assert_eq!(
2574 iter.next(),
2575 Some(Ok(Attribute {
2576 key: QName("'attribute'"),
2577 value: Cow::Borrowed(""),
2578 }))
2579 );
2580 assert_eq!(iter.next(), None);
2581 assert_eq!(iter.next(), None);
2582 }
2583 }
2584
2585 /// Copy of single, but with additional spaces in markup
2586 mod sparsed {
2587 use super::*;
2588 use pretty_assertions::assert_eq;
2589
2590 /// Attribute have a value enclosed in single quotes
2591 #[test]
2592 fn single_quoted() {
2593 let mut iter = Attributes::html(r#"tag key = 'value' "#, 3);
2594
2595 assert_eq!(
2596 iter.next(),
2597 Some(Ok(Attribute {
2598 key: QName("key"),
2599 value: Cow::Borrowed("value"),
2600 }))
2601 );
2602 assert_eq!(iter.next(), None);
2603 assert_eq!(iter.next(), None);
2604 }
2605
2606 /// Attribute have a value enclosed in double quotes
2607 #[test]
2608 fn double_quoted() {
2609 let mut iter = Attributes::html(r#"tag key = "value" "#, 3);
2610
2611 assert_eq!(
2612 iter.next(),
2613 Some(Ok(Attribute {
2614 key: QName("key"),
2615 value: Cow::Borrowed("value"),
2616 }))
2617 );
2618 assert_eq!(iter.next(), None);
2619 assert_eq!(iter.next(), None);
2620 }
2621
2622 /// Attribute have a value, not enclosed in quotes
2623 #[test]
2624 fn unquoted() {
2625 let mut iter = Attributes::html(r#"tag key = value "#, 3);
2626
2627 assert_eq!(
2628 iter.next(),
2629 Some(Ok(Attribute {
2630 key: QName("key"),
2631 value: Cow::Borrowed("value"),
2632 }))
2633 );
2634 assert_eq!(iter.next(), None);
2635 assert_eq!(iter.next(), None);
2636 }
2637
2638 /// Only attribute key is present
2639 #[test]
2640 fn key_only() {
2641 let mut iter = Attributes::html(r#"tag key "#, 3);
2642
2643 assert_eq!(
2644 iter.next(),
2645 Some(Ok(Attribute {
2646 key: QName("key"),
2647 value: Cow::Borrowed(""),
2648 }))
2649 );
2650 assert_eq!(iter.next(), None);
2651 assert_eq!(iter.next(), None);
2652 }
2653
2654 /// Key is started with an invalid symbol (a single quote in this test).
2655 /// Because we do not check validity of keys and values during parsing,
2656 /// that invalid attribute will be returned
2657 #[test]
2658 fn key_start_invalid() {
2659 let mut iter = Attributes::html(r#"tag 'key' = 'value' "#, 3);
2660
2661 assert_eq!(
2662 iter.next(),
2663 Some(Ok(Attribute {
2664 key: QName("'key'"),
2665 value: Cow::Borrowed("value"),
2666 }))
2667 );
2668 assert_eq!(iter.next(), None);
2669 assert_eq!(iter.next(), None);
2670 }
2671
2672 /// Key contains an invalid symbol (an ampersand in this test).
2673 /// Because we do not check validity of keys and values during parsing,
2674 /// that invalid attribute will be returned
2675 #[test]
2676 fn key_contains_invalid() {
2677 let mut iter = Attributes::html(r#"tag key&jey = 'value' "#, 3);
2678
2679 assert_eq!(
2680 iter.next(),
2681 Some(Ok(Attribute {
2682 key: QName("key&jey"),
2683 value: Cow::Borrowed("value"),
2684 }))
2685 );
2686 assert_eq!(iter.next(), None);
2687 assert_eq!(iter.next(), None);
2688 }
2689
2690 /// Attribute value is missing after `=`
2691 #[test]
2692 fn missed_value() {
2693 let mut iter = Attributes::html(r#"tag key = "#, 3);
2694 // 0 ^ = 10
2695
2696 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(10))));
2697 assert_eq!(iter.next(), None);
2698 assert_eq!(iter.next(), None);
2699 }
2700 }
2701
2702 /// Checks that duplicated attributes correctly reported and recovering is
2703 /// possible after that
2704 mod duplicated {
2705 use super::*;
2706
2707 mod with_check {
2708 use super::*;
2709 use pretty_assertions::assert_eq;
2710
2711 /// Attribute have a value enclosed in single quotes
2712 #[test]
2713 fn single_quoted() {
2714 let mut iter = Attributes::html(r#"tag key='value' key='dup' another=''"#, 3);
2715 // 0 ^ = 4 ^ = 16
2716
2717 assert_eq!(
2718 iter.next(),
2719 Some(Ok(Attribute {
2720 key: QName("key"),
2721 value: Cow::Borrowed("value"),
2722 }))
2723 );
2724 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2725 assert_eq!(
2726 iter.next(),
2727 Some(Ok(Attribute {
2728 key: QName("another"),
2729 value: Cow::Borrowed(""),
2730 }))
2731 );
2732 assert_eq!(iter.next(), None);
2733 assert_eq!(iter.next(), None);
2734 }
2735
2736 /// Attribute have a value enclosed in double quotes
2737 #[test]
2738 fn double_quoted() {
2739 let mut iter = Attributes::html(r#"tag key='value' key="dup" another=''"#, 3);
2740 // 0 ^ = 4 ^ = 16
2741
2742 assert_eq!(
2743 iter.next(),
2744 Some(Ok(Attribute {
2745 key: QName("key"),
2746 value: Cow::Borrowed("value"),
2747 }))
2748 );
2749 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2750 assert_eq!(
2751 iter.next(),
2752 Some(Ok(Attribute {
2753 key: QName("another"),
2754 value: Cow::Borrowed(""),
2755 }))
2756 );
2757 assert_eq!(iter.next(), None);
2758 assert_eq!(iter.next(), None);
2759 }
2760
2761 /// Attribute have a value, not enclosed in quotes
2762 #[test]
2763 fn unquoted() {
2764 let mut iter = Attributes::html(r#"tag key='value' key=dup another=''"#, 3);
2765 // 0 ^ = 4 ^ = 16
2766
2767 assert_eq!(
2768 iter.next(),
2769 Some(Ok(Attribute {
2770 key: QName("key"),
2771 value: Cow::Borrowed("value"),
2772 }))
2773 );
2774 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2775 assert_eq!(
2776 iter.next(),
2777 Some(Ok(Attribute {
2778 key: QName("another"),
2779 value: Cow::Borrowed(""),
2780 }))
2781 );
2782 assert_eq!(iter.next(), None);
2783 assert_eq!(iter.next(), None);
2784 }
2785
2786 /// Only attribute key is present
2787 #[test]
2788 fn key_only() {
2789 let mut iter = Attributes::html(r#"tag key='value' key another=''"#, 3);
2790 // 0 ^ = 4 ^ = 16
2791
2792 assert_eq!(
2793 iter.next(),
2794 Some(Ok(Attribute {
2795 key: QName("key"),
2796 value: Cow::Borrowed("value"),
2797 }))
2798 );
2799 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2800 assert_eq!(
2801 iter.next(),
2802 Some(Ok(Attribute {
2803 key: QName("another"),
2804 value: Cow::Borrowed(""),
2805 }))
2806 );
2807 assert_eq!(iter.next(), None);
2808 assert_eq!(iter.next(), None);
2809 }
2810 }
2811
2812 /// Check for duplicated names is disabled
2813 mod without_check {
2814 use super::*;
2815 use pretty_assertions::assert_eq;
2816
2817 /// Attribute have a value enclosed in single quotes
2818 #[test]
2819 fn single_quoted() {
2820 let mut iter = Attributes::html(r#"tag key='value' key='dup' another=''"#, 3);
2821 iter.with_checks(false);
2822
2823 assert_eq!(
2824 iter.next(),
2825 Some(Ok(Attribute {
2826 key: QName("key"),
2827 value: Cow::Borrowed("value"),
2828 }))
2829 );
2830 assert_eq!(
2831 iter.next(),
2832 Some(Ok(Attribute {
2833 key: QName("key"),
2834 value: Cow::Borrowed("dup"),
2835 }))
2836 );
2837 assert_eq!(
2838 iter.next(),
2839 Some(Ok(Attribute {
2840 key: QName("another"),
2841 value: Cow::Borrowed(""),
2842 }))
2843 );
2844 assert_eq!(iter.next(), None);
2845 assert_eq!(iter.next(), None);
2846 }
2847
2848 /// Attribute have a value enclosed in double quotes
2849 #[test]
2850 fn double_quoted() {
2851 let mut iter = Attributes::html(r#"tag key='value' key="dup" another=''"#, 3);
2852 iter.with_checks(false);
2853
2854 assert_eq!(
2855 iter.next(),
2856 Some(Ok(Attribute {
2857 key: QName("key"),
2858 value: Cow::Borrowed("value"),
2859 }))
2860 );
2861 assert_eq!(
2862 iter.next(),
2863 Some(Ok(Attribute {
2864 key: QName("key"),
2865 value: Cow::Borrowed("dup"),
2866 }))
2867 );
2868 assert_eq!(
2869 iter.next(),
2870 Some(Ok(Attribute {
2871 key: QName("another"),
2872 value: Cow::Borrowed(""),
2873 }))
2874 );
2875 assert_eq!(iter.next(), None);
2876 assert_eq!(iter.next(), None);
2877 }
2878
2879 /// Attribute have a value, not enclosed in quotes
2880 #[test]
2881 fn unquoted() {
2882 let mut iter = Attributes::html(r#"tag key='value' key=dup another=''"#, 3);
2883 iter.with_checks(false);
2884
2885 assert_eq!(
2886 iter.next(),
2887 Some(Ok(Attribute {
2888 key: QName("key"),
2889 value: Cow::Borrowed("value"),
2890 }))
2891 );
2892 assert_eq!(
2893 iter.next(),
2894 Some(Ok(Attribute {
2895 key: QName("key"),
2896 value: Cow::Borrowed("dup"),
2897 }))
2898 );
2899 assert_eq!(
2900 iter.next(),
2901 Some(Ok(Attribute {
2902 key: QName("another"),
2903 value: Cow::Borrowed(""),
2904 }))
2905 );
2906 assert_eq!(iter.next(), None);
2907 assert_eq!(iter.next(), None);
2908 }
2909
2910 /// Only attribute key is present
2911 #[test]
2912 fn key_only() {
2913 let mut iter = Attributes::html(r#"tag key='value' key another=''"#, 3);
2914 iter.with_checks(false);
2915
2916 assert_eq!(
2917 iter.next(),
2918 Some(Ok(Attribute {
2919 key: QName("key"),
2920 value: Cow::Borrowed("value"),
2921 }))
2922 );
2923 assert_eq!(
2924 iter.next(),
2925 Some(Ok(Attribute {
2926 key: QName("key"),
2927 value: Cow::Borrowed(""),
2928 }))
2929 );
2930 assert_eq!(
2931 iter.next(),
2932 Some(Ok(Attribute {
2933 key: QName("another"),
2934 value: Cow::Borrowed(""),
2935 }))
2936 );
2937 assert_eq!(iter.next(), None);
2938 assert_eq!(iter.next(), None);
2939 }
2940 }
2941 }
2942
2943 #[test]
2944 fn mixed_quote() {
2945 let mut iter = Attributes::html(r#"tag a='a' b = "b" c='cc"cc' d="dd'dd""#, 3);
2946
2947 assert_eq!(
2948 iter.next(),
2949 Some(Ok(Attribute {
2950 key: QName("a"),
2951 value: Cow::Borrowed("a"),
2952 }))
2953 );
2954 assert_eq!(
2955 iter.next(),
2956 Some(Ok(Attribute {
2957 key: QName("b"),
2958 value: Cow::Borrowed("b"),
2959 }))
2960 );
2961 assert_eq!(
2962 iter.next(),
2963 Some(Ok(Attribute {
2964 key: QName("c"),
2965 value: Cow::Borrowed(r#"cc"cc"#),
2966 }))
2967 );
2968 assert_eq!(
2969 iter.next(),
2970 Some(Ok(Attribute {
2971 key: QName("d"),
2972 value: Cow::Borrowed("dd'dd"),
2973 }))
2974 );
2975 assert_eq!(iter.next(), None);
2976 assert_eq!(iter.next(), None);
2977 }
2978}