quick_xml/events/attributes.rs
1//! Xml Attributes module
2//!
3//! Provides an iterator over attributes key/value pairs
4
5use crate::encoding::Decoder;
6use crate::errors::Result as XmlResult;
7use crate::escape::{escape, resolve_predefined_entity, unescape_with};
8use crate::name::QName;
9use crate::utils::{is_whitespace, write_byte_string, write_cow_string, Bytes};
10
11use std::fmt::{self, Debug, Display, Formatter};
12use std::iter::FusedIterator;
13use std::{borrow::Cow, ops::Range};
14
15/// A struct representing a key/value XML attribute.
16///
17/// Field `value` stores raw bytes, possibly containing escape-sequences. Most users will likely
18/// want to access the value using one of the [`unescape_value`] and [`decode_and_unescape_value`]
19/// functions.
20///
21/// [`unescape_value`]: Self::unescape_value
22/// [`decode_and_unescape_value`]: Self::decode_and_unescape_value
23#[derive(Clone, Eq, PartialEq)]
24pub struct Attribute<'a> {
25 /// The key to uniquely define the attribute.
26 ///
27 /// If [`Attributes::with_checks`] is turned off, the key might not be unique.
28 pub key: QName<'a>,
29 /// The raw value of the attribute.
30 pub value: Cow<'a, [u8]>,
31}
32
33impl<'a> Attribute<'a> {
34 /// Decodes using UTF-8 then unescapes the value.
35 ///
36 /// This is normally the value you are interested in. Escape sequences such as `>` are
37 /// replaced with their unescaped equivalents such as `>`.
38 ///
39 /// This will allocate if the value contains any escape sequences.
40 ///
41 /// See also [`unescape_value_with()`](Self::unescape_value_with)
42 ///
43 /// This method is available only if [`encoding`] feature is **not** enabled.
44 ///
45 /// [`encoding`]: ../../index.html#encoding
46 #[cfg(any(doc, not(feature = "encoding")))]
47 pub fn unescape_value(&self) -> XmlResult<Cow<'a, str>> {
48 self.unescape_value_with(resolve_predefined_entity)
49 }
50
51 /// Decodes using UTF-8 then unescapes the value, using custom entities.
52 ///
53 /// This is normally the value you are interested in. Escape sequences such as `>` are
54 /// replaced with their unescaped equivalents such as `>`.
55 /// A fallback resolver for additional custom entities can be provided via
56 /// `resolve_entity`.
57 ///
58 /// This will allocate if the value contains any escape sequences.
59 ///
60 /// See also [`unescape_value()`](Self::unescape_value)
61 ///
62 /// This method is available only if [`encoding`] feature is **not** enabled.
63 ///
64 /// [`encoding`]: ../../index.html#encoding
65 #[cfg(any(doc, not(feature = "encoding")))]
66 #[inline]
67 pub fn unescape_value_with<'entity>(
68 &self,
69 resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
70 ) -> XmlResult<Cow<'a, str>> {
71 self.decode_and_unescape_value_with(Decoder::utf8(), resolve_entity)
72 }
73
74 /// Decodes then unescapes the value.
75 ///
76 /// This will allocate if the value contains any escape sequences or in
77 /// non-UTF-8 encoding.
78 pub fn decode_and_unescape_value(&self, decoder: Decoder) -> XmlResult<Cow<'a, str>> {
79 self.decode_and_unescape_value_with(decoder, resolve_predefined_entity)
80 }
81
82 /// Decodes then unescapes the value with custom entities.
83 ///
84 /// This will allocate if the value contains any escape sequences or in
85 /// non-UTF-8 encoding.
86 pub fn decode_and_unescape_value_with<'entity>(
87 &self,
88 decoder: Decoder,
89 resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
90 ) -> XmlResult<Cow<'a, str>> {
91 let decoded = decoder.decode_cow(&self.value)?;
92
93 match unescape_with(&decoded, resolve_entity)? {
94 // Because result is borrowed, no replacements was done and we can use original string
95 Cow::Borrowed(_) => Ok(decoded),
96 Cow::Owned(s) => Ok(s.into()),
97 }
98 }
99}
100
101impl<'a> Debug for Attribute<'a> {
102 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
103 write!(f, "Attribute {{ key: ")?;
104 write_byte_string(f, self.key.as_ref())?;
105 write!(f, ", value: ")?;
106 write_cow_string(f, &self.value)?;
107 write!(f, " }}")
108 }
109}
110
111impl<'a> From<(&'a [u8], &'a [u8])> for Attribute<'a> {
112 /// Creates new attribute from raw bytes.
113 /// Does not apply any transformation to both key and value.
114 ///
115 /// # Examples
116 ///
117 /// ```
118 /// # use pretty_assertions::assert_eq;
119 /// use quick_xml::events::attributes::Attribute;
120 ///
121 /// let features = Attribute::from(("features".as_bytes(), "Bells & whistles".as_bytes()));
122 /// assert_eq!(features.value, "Bells & whistles".as_bytes());
123 /// ```
124 fn from(val: (&'a [u8], &'a [u8])) -> Attribute<'a> {
125 Attribute {
126 key: QName(val.0),
127 value: Cow::from(val.1),
128 }
129 }
130}
131
132impl<'a> From<(&'a str, &'a str)> for Attribute<'a> {
133 /// Creates new attribute from text representation.
134 /// Key is stored as-is, but the value will be escaped.
135 ///
136 /// # Examples
137 ///
138 /// ```
139 /// # use pretty_assertions::assert_eq;
140 /// use quick_xml::events::attributes::Attribute;
141 ///
142 /// let features = Attribute::from(("features", "Bells & whistles"));
143 /// assert_eq!(features.value, "Bells & whistles".as_bytes());
144 /// ```
145 fn from(val: (&'a str, &'a str)) -> Attribute<'a> {
146 Attribute {
147 key: QName(val.0.as_bytes()),
148 value: match escape(val.1) {
149 Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
150 Cow::Owned(s) => Cow::Owned(s.into_bytes()),
151 },
152 }
153 }
154}
155
156impl<'a> From<(&'a str, Cow<'a, str>)> for Attribute<'a> {
157 /// Creates new attribute from text representation.
158 /// Key is stored as-is, but the value will be escaped.
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// # use std::borrow::Cow;
164 /// use pretty_assertions::assert_eq;
165 /// use quick_xml::events::attributes::Attribute;
166 ///
167 /// let features = Attribute::from(("features", Cow::Borrowed("Bells & whistles")));
168 /// assert_eq!(features.value, "Bells & whistles".as_bytes());
169 /// ```
170 fn from(val: (&'a str, Cow<'a, str>)) -> Attribute<'a> {
171 Attribute {
172 key: QName(val.0.as_bytes()),
173 value: match escape(val.1) {
174 Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
175 Cow::Owned(s) => Cow::Owned(s.into_bytes()),
176 },
177 }
178 }
179}
180
181impl<'a> From<Attr<&'a [u8]>> for Attribute<'a> {
182 #[inline]
183 fn from(attr: Attr<&'a [u8]>) -> Self {
184 Self {
185 key: attr.key(),
186 value: Cow::Borrowed(attr.value()),
187 }
188 }
189}
190
191////////////////////////////////////////////////////////////////////////////////////////////////////
192
193/// Iterator over XML attributes.
194///
195/// Yields `Result<Attribute>`. An `Err` will be yielded if an attribute is malformed or duplicated.
196/// The duplicate check can be turned off by calling [`with_checks(false)`].
197///
198/// [`with_checks(false)`]: Self::with_checks
199#[derive(Clone, Debug)]
200pub struct Attributes<'a> {
201 /// Slice of `BytesStart` corresponding to attributes
202 bytes: &'a [u8],
203 /// Iterator state, independent from the actual source of bytes
204 state: IterState,
205}
206
207impl<'a> Attributes<'a> {
208 /// Internal constructor, used by `BytesStart`. Supplies data in reader's encoding
209 #[inline]
210 pub(crate) const fn wrap(buf: &'a [u8], pos: usize, html: bool) -> Self {
211 Self {
212 bytes: buf,
213 state: IterState::new(pos, html),
214 }
215 }
216
217 /// Creates a new attribute iterator from a buffer.
218 pub const fn new(buf: &'a str, pos: usize) -> Self {
219 Self::wrap(buf.as_bytes(), pos, false)
220 }
221
222 /// Creates a new attribute iterator from a buffer, allowing HTML attribute syntax.
223 pub const fn html(buf: &'a str, pos: usize) -> Self {
224 Self::wrap(buf.as_bytes(), pos, true)
225 }
226
227 /// Changes whether attributes should be checked for uniqueness.
228 ///
229 /// The XML specification requires attribute keys in the same element to be unique. This check
230 /// can be disabled to improve performance slightly.
231 ///
232 /// (`true` by default)
233 pub fn with_checks(&mut self, val: bool) -> &mut Attributes<'a> {
234 self.state.check_duplicates = val;
235 self
236 }
237}
238
239impl<'a> Iterator for Attributes<'a> {
240 type Item = Result<Attribute<'a>, AttrError>;
241
242 #[inline]
243 fn next(&mut self) -> Option<Self::Item> {
244 match self.state.next(self.bytes) {
245 None => None,
246 Some(Ok(a)) => Some(Ok(a.map(|range| &self.bytes[range]).into())),
247 Some(Err(e)) => Some(Err(e)),
248 }
249 }
250}
251
252impl<'a> FusedIterator for Attributes<'a> {}
253
254////////////////////////////////////////////////////////////////////////////////////////////////////
255
256/// Errors that can be raised during parsing attributes.
257///
258/// Recovery position in examples shows the position from which parsing of the
259/// next attribute will be attempted.
260#[derive(Clone, Debug, PartialEq, Eq)]
261pub enum AttrError {
262 /// Attribute key was not followed by `=`, position relative to the start of
263 /// the owning tag is provided.
264 ///
265 /// Example of input that raises this error:
266 ///
267 /// ```xml
268 /// <tag key another="attribute"/>
269 /// <!-- ^~~ error position, recovery position (8) -->
270 /// ```
271 ///
272 /// This error can be raised only when the iterator is in XML mode.
273 ExpectedEq(usize),
274 /// Attribute value was not found after `=`, position relative to the start
275 /// of the owning tag is provided.
276 ///
277 /// Example of input that raises this error:
278 ///
279 /// ```xml
280 /// <tag key = />
281 /// <!-- ^~~ error position, recovery position (10) -->
282 /// ```
283 ///
284 /// This error can be returned only for the last attribute in the list,
285 /// because otherwise any content after `=` will be threated as a value.
286 /// The XML
287 ///
288 /// ```xml
289 /// <tag key = another-key = "value"/>
290 /// <!-- ^ ^- recovery position (24) -->
291 /// <!-- '~~ error position (22) -->
292 /// ```
293 ///
294 /// will be treated as `Attribute { key = b"key", value = b"another-key" }`
295 /// and or [`Attribute`] is returned, or [`AttrError::UnquotedValue`] is raised,
296 /// depending on the parsing mode.
297 ExpectedValue(usize),
298 /// Attribute value is not quoted, position relative to the start of the
299 /// owning tag is provided.
300 ///
301 /// Example of input that raises this error:
302 ///
303 /// ```xml
304 /// <tag key = value />
305 /// <!-- ^ ^~~ recovery position (15) -->
306 /// <!-- '~~ error position (10) -->
307 /// ```
308 ///
309 /// This error can be raised only when the iterator is in XML mode.
310 UnquotedValue(usize),
311 /// Attribute value was not finished with a matching quote, position relative
312 /// to the start of owning tag and a quote is provided. That position is always
313 /// a last character in the tag content.
314 ///
315 /// Example of input that raises this error:
316 ///
317 /// ```xml
318 /// <tag key = "value />
319 /// <tag key = 'value />
320 /// <!-- ^~~ error position, recovery position (18) -->
321 /// ```
322 ///
323 /// This error can be returned only for the last attribute in the list,
324 /// because all input was consumed during scanning for a quote.
325 ExpectedQuote(usize, u8),
326 /// An attribute with the same name was already encountered. Two parameters
327 /// define (1) the error position relative to the start of the owning tag
328 /// for a new attribute and (2) the start position of a previously encountered
329 /// attribute with the same name.
330 ///
331 /// Example of input that raises this error:
332 ///
333 /// ```xml
334 /// <tag key = 'value' key="value2" attr3='value3' />
335 /// <!-- ^ ^ ^~~ recovery position (32) -->
336 /// <!-- | '~~ error position (19) -->
337 /// <!-- '~~ previous position (4) -->
338 /// ```
339 ///
340 /// This error is returned only when [`Attributes::with_checks()`] is set
341 /// to `true` (that is default behavior).
342 Duplicated(usize, usize),
343}
344
345impl Display for AttrError {
346 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
347 match self {
348 Self::ExpectedEq(pos) => write!(
349 f,
350 r#"position {}: attribute key must be directly followed by `=` or space"#,
351 pos
352 ),
353 Self::ExpectedValue(pos) => write!(
354 f,
355 r#"position {}: `=` must be followed by an attribute value"#,
356 pos
357 ),
358 Self::UnquotedValue(pos) => write!(
359 f,
360 r#"position {}: attribute value must be enclosed in `"` or `'`"#,
361 pos
362 ),
363 Self::ExpectedQuote(pos, quote) => write!(
364 f,
365 r#"position {}: missing closing quote `{}` in attribute value"#,
366 pos, *quote as char
367 ),
368 Self::Duplicated(pos1, pos2) => write!(
369 f,
370 r#"position {}: duplicated attribute, previous declaration at position {}"#,
371 pos1, pos2
372 ),
373 }
374 }
375}
376
377impl std::error::Error for AttrError {}
378
379////////////////////////////////////////////////////////////////////////////////////////////////////
380
381/// A struct representing a key/value XML or HTML [attribute].
382///
383/// [attribute]: https://www.w3.org/TR/xml11/#NT-Attribute
384#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
385pub enum Attr<T> {
386 /// Attribute with value enclosed in double quotes (`"`). Attribute key and
387 /// value provided. This is a canonical XML-style attribute.
388 DoubleQ(T, T),
389 /// Attribute with value enclosed in single quotes (`'`). Attribute key and
390 /// value provided. This is an XML-style attribute.
391 SingleQ(T, T),
392 /// Attribute with value not enclosed in quotes. Attribute key and value
393 /// provided. This is HTML-style attribute, it can be returned in HTML-mode
394 /// parsing only. In an XML mode [`AttrError::UnquotedValue`] will be raised
395 /// instead.
396 ///
397 /// Attribute value can be invalid according to the [HTML specification],
398 /// in particular, it can contain `"`, `'`, `=`, `<`, and <code>`</code>
399 /// characters. The absence of the `>` character is nevertheless guaranteed,
400 /// since the parser extracts [events] based on them even before the start
401 /// of parsing attributes.
402 ///
403 /// [HTML specification]: https://html.spec.whatwg.org/#unquoted
404 /// [events]: crate::events::Event::Start
405 Unquoted(T, T),
406 /// Attribute without value. Attribute key provided. This is HTML-style attribute,
407 /// it can be returned in HTML-mode parsing only. In XML mode
408 /// [`AttrError::ExpectedEq`] will be raised instead.
409 Empty(T),
410}
411
412impl<T> Attr<T> {
413 /// Maps an `Attr<T>` to `Attr<U>` by applying a function to a contained key and value.
414 #[inline]
415 pub fn map<U, F>(self, mut f: F) -> Attr<U>
416 where
417 F: FnMut(T) -> U,
418 {
419 match self {
420 Attr::DoubleQ(key, value) => Attr::DoubleQ(f(key), f(value)),
421 Attr::SingleQ(key, value) => Attr::SingleQ(f(key), f(value)),
422 Attr::Empty(key) => Attr::Empty(f(key)),
423 Attr::Unquoted(key, value) => Attr::Unquoted(f(key), f(value)),
424 }
425 }
426}
427
428impl<'a> Attr<&'a [u8]> {
429 /// Returns the key value
430 #[inline]
431 pub const fn key(&self) -> QName<'a> {
432 QName(match self {
433 Attr::DoubleQ(key, _) => key,
434 Attr::SingleQ(key, _) => key,
435 Attr::Empty(key) => key,
436 Attr::Unquoted(key, _) => key,
437 })
438 }
439 /// Returns the attribute value. For [`Self::Empty`] variant an empty slice
440 /// is returned according to the [HTML specification].
441 ///
442 /// [HTML specification]: https://www.w3.org/TR/2012/WD-html-markup-20120329/syntax.html#syntax-attr-empty
443 #[inline]
444 pub const fn value(&self) -> &'a [u8] {
445 match self {
446 Attr::DoubleQ(_, value) => value,
447 Attr::SingleQ(_, value) => value,
448 Attr::Empty(_) => &[],
449 Attr::Unquoted(_, value) => value,
450 }
451 }
452}
453
454impl<T: AsRef<[u8]>> Debug for Attr<T> {
455 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
456 match self {
457 Attr::DoubleQ(key, value) => f
458 .debug_tuple("Attr::DoubleQ")
459 .field(&Bytes(key.as_ref()))
460 .field(&Bytes(value.as_ref()))
461 .finish(),
462 Attr::SingleQ(key, value) => f
463 .debug_tuple("Attr::SingleQ")
464 .field(&Bytes(key.as_ref()))
465 .field(&Bytes(value.as_ref()))
466 .finish(),
467 Attr::Empty(key) => f
468 .debug_tuple("Attr::Empty")
469 // Comment to prevent formatting and keep style consistent
470 .field(&Bytes(key.as_ref()))
471 .finish(),
472 Attr::Unquoted(key, value) => f
473 .debug_tuple("Attr::Unquoted")
474 .field(&Bytes(key.as_ref()))
475 .field(&Bytes(value.as_ref()))
476 .finish(),
477 }
478 }
479}
480
481/// Unpacks attribute key and value into tuple of this two elements.
482/// `None` value element is returned only for [`Attr::Empty`] variant.
483impl<T> From<Attr<T>> for (T, Option<T>) {
484 #[inline]
485 fn from(attr: Attr<T>) -> Self {
486 match attr {
487 Attr::DoubleQ(key, value) => (key, Some(value)),
488 Attr::SingleQ(key, value) => (key, Some(value)),
489 Attr::Empty(key) => (key, None),
490 Attr::Unquoted(key, value) => (key, Some(value)),
491 }
492 }
493}
494
495////////////////////////////////////////////////////////////////////////////////////////////////////
496
497type AttrResult = Result<Attr<Range<usize>>, AttrError>;
498
499#[derive(Clone, Copy, Debug)]
500enum State {
501 /// Iteration finished, iterator will return `None` to all [`IterState::next`]
502 /// requests.
503 Done,
504 /// The last attribute returned was deserialized successfully. Contains an
505 /// offset from which next attribute should be searched.
506 Next(usize),
507 /// The last attribute returns [`AttrError::UnquotedValue`], offset pointed
508 /// to the beginning of the value. Recover should skip a value
509 SkipValue(usize),
510 /// The last attribute returns [`AttrError::Duplicated`], offset pointed to
511 /// the equal (`=`) sign. Recover should skip it and a value
512 SkipEqValue(usize),
513}
514
515/// External iterator over spans of attribute key and value
516#[derive(Clone, Debug)]
517pub(crate) struct IterState {
518 /// Iteration state that determines what actions should be done before the
519 /// actual parsing of the next attribute
520 state: State,
521 /// If `true`, enables ability to parse unquoted values and key-only (empty)
522 /// attributes
523 html: bool,
524 /// If `true`, checks for duplicate names
525 check_duplicates: bool,
526 /// If `check_duplicates` is set, contains the ranges of already parsed attribute
527 /// names. We store a ranges instead of slices to able to report a previous
528 /// attribute position
529 keys: Vec<Range<usize>>,
530}
531
532impl IterState {
533 pub const fn new(offset: usize, html: bool) -> Self {
534 Self {
535 state: State::Next(offset),
536 html,
537 check_duplicates: true,
538 keys: Vec::new(),
539 }
540 }
541
542 /// Recover from an error that could have been made on a previous step.
543 /// Returns an offset from which parsing should continue.
544 /// If there no input left, returns `None`.
545 fn recover(&self, slice: &[u8]) -> Option<usize> {
546 match self.state {
547 State::Done => None,
548 State::Next(offset) => Some(offset),
549 State::SkipValue(offset) => self.skip_value(slice, offset),
550 State::SkipEqValue(offset) => self.skip_eq_value(slice, offset),
551 }
552 }
553
554 /// Skip all characters up to first space symbol or end-of-input
555 #[inline]
556 #[allow(clippy::manual_map)]
557 fn skip_value(&self, slice: &[u8], offset: usize) -> Option<usize> {
558 let mut iter = (offset..).zip(slice[offset..].iter());
559
560 match iter.find(|(_, &b)| is_whitespace(b)) {
561 // Input: ` key = value `
562 // | ^
563 // offset e
564 Some((e, _)) => Some(e),
565 // Input: ` key = value`
566 // | ^
567 // offset e = len()
568 None => None,
569 }
570 }
571
572 /// Skip all characters up to first space symbol or end-of-input
573 #[inline]
574 fn skip_eq_value(&self, slice: &[u8], offset: usize) -> Option<usize> {
575 let mut iter = (offset..).zip(slice[offset..].iter());
576
577 // Skip all up to the quote and get the quote type
578 let quote = match iter.find(|(_, &b)| !is_whitespace(b)) {
579 // Input: ` key = "`
580 // | ^
581 // offset
582 Some((_, b'"')) => b'"',
583 // Input: ` key = '`
584 // | ^
585 // offset
586 Some((_, b'\'')) => b'\'',
587
588 // Input: ` key = x`
589 // | ^
590 // offset
591 Some((offset, _)) => return self.skip_value(slice, offset),
592 // Input: ` key = `
593 // | ^
594 // offset
595 None => return None,
596 };
597
598 match iter.find(|(_, &b)| b == quote) {
599 // Input: ` key = " "`
600 // ^
601 Some((e, b'"')) => Some(e),
602 // Input: ` key = ' '`
603 // ^
604 Some((e, _)) => Some(e),
605
606 // Input: ` key = " `
607 // Input: ` key = ' `
608 // ^
609 // Closing quote not found
610 None => None,
611 }
612 }
613
614 #[inline]
615 fn check_for_duplicates(
616 &mut self,
617 slice: &[u8],
618 key: Range<usize>,
619 ) -> Result<Range<usize>, AttrError> {
620 if self.check_duplicates {
621 if let Some(prev) = self
622 .keys
623 .iter()
624 .find(|r| slice[(*r).clone()] == slice[key.clone()])
625 {
626 return Err(AttrError::Duplicated(key.start, prev.start));
627 }
628 self.keys.push(key.clone());
629 }
630 Ok(key)
631 }
632
633 /// # Parameters
634 ///
635 /// - `slice`: content of the tag, used for checking for duplicates
636 /// - `key`: Range of key in slice, if iterator in HTML mode
637 /// - `offset`: Position of error if iterator in XML mode
638 #[inline]
639 fn key_only(&mut self, slice: &[u8], key: Range<usize>, offset: usize) -> Option<AttrResult> {
640 Some(if self.html {
641 self.check_for_duplicates(slice, key).map(Attr::Empty)
642 } else {
643 Err(AttrError::ExpectedEq(offset))
644 })
645 }
646
647 #[inline]
648 fn double_q(&mut self, key: Range<usize>, value: Range<usize>) -> Option<AttrResult> {
649 self.state = State::Next(value.end + 1); // +1 for `"`
650
651 Some(Ok(Attr::DoubleQ(key, value)))
652 }
653
654 #[inline]
655 fn single_q(&mut self, key: Range<usize>, value: Range<usize>) -> Option<AttrResult> {
656 self.state = State::Next(value.end + 1); // +1 for `'`
657
658 Some(Ok(Attr::SingleQ(key, value)))
659 }
660
661 pub fn next(&mut self, slice: &[u8]) -> Option<AttrResult> {
662 let mut iter = match self.recover(slice) {
663 Some(offset) => (offset..).zip(slice[offset..].iter()),
664 None => return None,
665 };
666
667 // Index where next key started
668 let start_key = match iter.find(|(_, &b)| !is_whitespace(b)) {
669 // Input: ` key`
670 // ^
671 Some((s, _)) => s,
672 // Input: ` `
673 // ^
674 None => {
675 // Because we reach end-of-input, stop iteration on next call
676 self.state = State::Done;
677 return None;
678 }
679 };
680 // Span of a key
681 let (key, offset) = match iter.find(|(_, &b)| b == b'=' || is_whitespace(b)) {
682 // Input: ` key=`
683 // | ^
684 // s e
685 Some((e, b'=')) => (start_key..e, e),
686
687 // Input: ` key `
688 // ^
689 Some((e, _)) => match iter.find(|(_, &b)| !is_whitespace(b)) {
690 // Input: ` key =`
691 // | | ^
692 // start_key e
693 Some((offset, b'=')) => (start_key..e, offset),
694 // Input: ` key x`
695 // | | ^
696 // start_key e
697 // If HTML-like attributes is allowed, this is the result, otherwise error
698 Some((offset, _)) => {
699 // In any case, recovering is not required
700 self.state = State::Next(offset);
701 return self.key_only(slice, start_key..e, offset);
702 }
703 // Input: ` key `
704 // | | ^
705 // start_key e
706 // If HTML-like attributes is allowed, this is the result, otherwise error
707 None => {
708 // Because we reach end-of-input, stop iteration on next call
709 self.state = State::Done;
710 return self.key_only(slice, start_key..e, slice.len());
711 }
712 },
713
714 // Input: ` key`
715 // | ^
716 // s e = len()
717 // If HTML-like attributes is allowed, this is the result, otherwise error
718 None => {
719 // Because we reach end-of-input, stop iteration on next call
720 self.state = State::Done;
721 let e = slice.len();
722 return self.key_only(slice, start_key..e, e);
723 }
724 };
725
726 let key = match self.check_for_duplicates(slice, key) {
727 Err(e) => {
728 self.state = State::SkipEqValue(offset);
729 return Some(Err(e));
730 }
731 Ok(key) => key,
732 };
733
734 ////////////////////////////////////////////////////////////////////////
735
736 // Gets the position of quote and quote type
737 let (start_value, quote) = match iter.find(|(_, &b)| !is_whitespace(b)) {
738 // Input: ` key = "`
739 // ^
740 Some((s, b'"')) => (s + 1, b'"'),
741 // Input: ` key = '`
742 // ^
743 Some((s, b'\'')) => (s + 1, b'\''),
744
745 // Input: ` key = x`
746 // ^
747 // If HTML-like attributes is allowed, this is the start of the value
748 Some((s, _)) if self.html => {
749 // We do not check validity of attribute value characters as required
750 // according to https://html.spec.whatwg.org/#unquoted. It can be done
751 // during validation phase
752 let end = match iter.find(|(_, &b)| is_whitespace(b)) {
753 // Input: ` key = value `
754 // | ^
755 // s e
756 Some((e, _)) => e,
757 // Input: ` key = value`
758 // | ^
759 // s e = len()
760 None => slice.len(),
761 };
762 self.state = State::Next(end);
763 return Some(Ok(Attr::Unquoted(key, s..end)));
764 }
765 // Input: ` key = x`
766 // ^
767 Some((s, _)) => {
768 self.state = State::SkipValue(s);
769 return Some(Err(AttrError::UnquotedValue(s)));
770 }
771
772 // Input: ` key = `
773 // ^
774 None => {
775 // Because we reach end-of-input, stop iteration on next call
776 self.state = State::Done;
777 return Some(Err(AttrError::ExpectedValue(slice.len())));
778 }
779 };
780
781 match iter.find(|(_, &b)| b == quote) {
782 // Input: ` key = " "`
783 // ^
784 Some((e, b'"')) => self.double_q(key, start_value..e),
785 // Input: ` key = ' '`
786 // ^
787 Some((e, _)) => self.single_q(key, start_value..e),
788
789 // Input: ` key = " `
790 // Input: ` key = ' `
791 // ^
792 // Closing quote not found
793 None => {
794 // Because we reach end-of-input, stop iteration on next call
795 self.state = State::Done;
796 Some(Err(AttrError::ExpectedQuote(slice.len(), quote)))
797 }
798 }
799 }
800}
801
802////////////////////////////////////////////////////////////////////////////////////////////////////
803
804/// Checks, how parsing of XML-style attributes works. Each attribute should
805/// have a value, enclosed in single or double quotes.
806#[cfg(test)]
807mod xml {
808 use super::*;
809 use pretty_assertions::assert_eq;
810
811 /// Checked attribute is the single attribute
812 mod single {
813 use super::*;
814 use pretty_assertions::assert_eq;
815
816 /// Attribute have a value enclosed in single quotes
817 #[test]
818 fn single_quoted() {
819 let mut iter = Attributes::new(r#"tag key='value'"#, 3);
820
821 assert_eq!(
822 iter.next(),
823 Some(Ok(Attribute {
824 key: QName(b"key"),
825 value: Cow::Borrowed(b"value"),
826 }))
827 );
828 assert_eq!(iter.next(), None);
829 assert_eq!(iter.next(), None);
830 }
831
832 /// Attribute have a value enclosed in double quotes
833 #[test]
834 fn double_quoted() {
835 let mut iter = Attributes::new(r#"tag key="value""#, 3);
836
837 assert_eq!(
838 iter.next(),
839 Some(Ok(Attribute {
840 key: QName(b"key"),
841 value: Cow::Borrowed(b"value"),
842 }))
843 );
844 assert_eq!(iter.next(), None);
845 assert_eq!(iter.next(), None);
846 }
847
848 /// Attribute have a value, not enclosed in quotes
849 #[test]
850 fn unquoted() {
851 let mut iter = Attributes::new(r#"tag key=value"#, 3);
852 // 0 ^ = 8
853
854 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(8))));
855 assert_eq!(iter.next(), None);
856 assert_eq!(iter.next(), None);
857 }
858
859 /// Only attribute key is present
860 #[test]
861 fn key_only() {
862 let mut iter = Attributes::new(r#"tag key"#, 3);
863 // 0 ^ = 7
864
865 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(7))));
866 assert_eq!(iter.next(), None);
867 assert_eq!(iter.next(), None);
868 }
869
870 /// Key is started with an invalid symbol (a single quote in this test).
871 /// Because we do not check validity of keys and values during parsing,
872 /// that invalid attribute will be returned
873 #[test]
874 fn key_start_invalid() {
875 let mut iter = Attributes::new(r#"tag 'key'='value'"#, 3);
876
877 assert_eq!(
878 iter.next(),
879 Some(Ok(Attribute {
880 key: QName(b"'key'"),
881 value: Cow::Borrowed(b"value"),
882 }))
883 );
884 assert_eq!(iter.next(), None);
885 assert_eq!(iter.next(), None);
886 }
887
888 /// Key contains an invalid symbol (an ampersand in this test).
889 /// Because we do not check validity of keys and values during parsing,
890 /// that invalid attribute will be returned
891 #[test]
892 fn key_contains_invalid() {
893 let mut iter = Attributes::new(r#"tag key&jey='value'"#, 3);
894
895 assert_eq!(
896 iter.next(),
897 Some(Ok(Attribute {
898 key: QName(b"key&jey"),
899 value: Cow::Borrowed(b"value"),
900 }))
901 );
902 assert_eq!(iter.next(), None);
903 assert_eq!(iter.next(), None);
904 }
905
906 /// Attribute value is missing after `=`
907 #[test]
908 fn missed_value() {
909 let mut iter = Attributes::new(r#"tag key="#, 3);
910 // 0 ^ = 8
911
912 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(8))));
913 assert_eq!(iter.next(), None);
914 assert_eq!(iter.next(), None);
915 }
916 }
917
918 /// Checked attribute is the first attribute in the list of many attributes
919 mod first {
920 use super::*;
921 use pretty_assertions::assert_eq;
922
923 /// Attribute have a value enclosed in single quotes
924 #[test]
925 fn single_quoted() {
926 let mut iter = Attributes::new(r#"tag key='value' regular='attribute'"#, 3);
927
928 assert_eq!(
929 iter.next(),
930 Some(Ok(Attribute {
931 key: QName(b"key"),
932 value: Cow::Borrowed(b"value"),
933 }))
934 );
935 assert_eq!(
936 iter.next(),
937 Some(Ok(Attribute {
938 key: QName(b"regular"),
939 value: Cow::Borrowed(b"attribute"),
940 }))
941 );
942 assert_eq!(iter.next(), None);
943 assert_eq!(iter.next(), None);
944 }
945
946 /// Attribute have a value enclosed in double quotes
947 #[test]
948 fn double_quoted() {
949 let mut iter = Attributes::new(r#"tag key="value" regular='attribute'"#, 3);
950
951 assert_eq!(
952 iter.next(),
953 Some(Ok(Attribute {
954 key: QName(b"key"),
955 value: Cow::Borrowed(b"value"),
956 }))
957 );
958 assert_eq!(
959 iter.next(),
960 Some(Ok(Attribute {
961 key: QName(b"regular"),
962 value: Cow::Borrowed(b"attribute"),
963 }))
964 );
965 assert_eq!(iter.next(), None);
966 assert_eq!(iter.next(), None);
967 }
968
969 /// Attribute have a value, not enclosed in quotes
970 #[test]
971 fn unquoted() {
972 let mut iter = Attributes::new(r#"tag key=value regular='attribute'"#, 3);
973 // 0 ^ = 8
974
975 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(8))));
976 // check error recovery
977 assert_eq!(
978 iter.next(),
979 Some(Ok(Attribute {
980 key: QName(b"regular"),
981 value: Cow::Borrowed(b"attribute"),
982 }))
983 );
984 assert_eq!(iter.next(), None);
985 assert_eq!(iter.next(), None);
986 }
987
988 /// Only attribute key is present
989 #[test]
990 fn key_only() {
991 let mut iter = Attributes::new(r#"tag key regular='attribute'"#, 3);
992 // 0 ^ = 8
993
994 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(8))));
995 // check error recovery
996 assert_eq!(
997 iter.next(),
998 Some(Ok(Attribute {
999 key: QName(b"regular"),
1000 value: Cow::Borrowed(b"attribute"),
1001 }))
1002 );
1003 assert_eq!(iter.next(), None);
1004 assert_eq!(iter.next(), None);
1005 }
1006
1007 /// Key is started with an invalid symbol (a single quote in this test).
1008 /// Because we do not check validity of keys and values during parsing,
1009 /// that invalid attribute will be returned
1010 #[test]
1011 fn key_start_invalid() {
1012 let mut iter = Attributes::new(r#"tag 'key'='value' regular='attribute'"#, 3);
1013
1014 assert_eq!(
1015 iter.next(),
1016 Some(Ok(Attribute {
1017 key: QName(b"'key'"),
1018 value: Cow::Borrowed(b"value"),
1019 }))
1020 );
1021 assert_eq!(
1022 iter.next(),
1023 Some(Ok(Attribute {
1024 key: QName(b"regular"),
1025 value: Cow::Borrowed(b"attribute"),
1026 }))
1027 );
1028 assert_eq!(iter.next(), None);
1029 assert_eq!(iter.next(), None);
1030 }
1031
1032 /// Key contains an invalid symbol (an ampersand in this test).
1033 /// Because we do not check validity of keys and values during parsing,
1034 /// that invalid attribute will be returned
1035 #[test]
1036 fn key_contains_invalid() {
1037 let mut iter = Attributes::new(r#"tag key&jey='value' regular='attribute'"#, 3);
1038
1039 assert_eq!(
1040 iter.next(),
1041 Some(Ok(Attribute {
1042 key: QName(b"key&jey"),
1043 value: Cow::Borrowed(b"value"),
1044 }))
1045 );
1046 assert_eq!(
1047 iter.next(),
1048 Some(Ok(Attribute {
1049 key: QName(b"regular"),
1050 value: Cow::Borrowed(b"attribute"),
1051 }))
1052 );
1053 assert_eq!(iter.next(), None);
1054 assert_eq!(iter.next(), None);
1055 }
1056
1057 /// Attribute value is missing after `=`.
1058 #[test]
1059 fn missed_value() {
1060 let mut iter = Attributes::new(r#"tag key= regular='attribute'"#, 3);
1061 // 0 ^ = 9
1062
1063 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1064 // Because we do not check validity of keys and values during parsing,
1065 // "error='recovery'" is considered, as unquoted attribute value and
1066 // skipped during recovery and iteration finished
1067 assert_eq!(iter.next(), None);
1068 assert_eq!(iter.next(), None);
1069
1070 ////////////////////////////////////////////////////////////////////
1071
1072 let mut iter = Attributes::new(r#"tag key= regular= 'attribute'"#, 3);
1073 // 0 ^ = 9 ^ = 29
1074
1075 // In that case "regular=" considered as unquoted value
1076 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1077 // In that case "'attribute'" considered as a key, because we do not check
1078 // validity of key names
1079 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(29))));
1080 assert_eq!(iter.next(), None);
1081 assert_eq!(iter.next(), None);
1082
1083 ////////////////////////////////////////////////////////////////////
1084
1085 let mut iter = Attributes::new(r#"tag key= regular ='attribute'"#, 3);
1086 // 0 ^ = 9 ^ = 29
1087
1088 // In that case "regular" considered as unquoted value
1089 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1090 // In that case "='attribute'" considered as a key, because we do not check
1091 // validity of key names
1092 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(29))));
1093 assert_eq!(iter.next(), None);
1094 assert_eq!(iter.next(), None);
1095
1096 ////////////////////////////////////////////////////////////////////
1097
1098 let mut iter = Attributes::new(r#"tag key= regular = 'attribute'"#, 3);
1099 // 0 ^ = 9 ^ = 19 ^ = 30
1100
1101 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(9))));
1102 // In that case second "=" considered as a key, because we do not check
1103 // validity of key names
1104 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(19))));
1105 // In that case "'attribute'" considered as a key, because we do not check
1106 // validity of key names
1107 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(30))));
1108 assert_eq!(iter.next(), None);
1109 assert_eq!(iter.next(), None);
1110 }
1111 }
1112
1113 /// Copy of single, but with additional spaces in markup
1114 mod sparsed {
1115 use super::*;
1116 use pretty_assertions::assert_eq;
1117
1118 /// Attribute have a value enclosed in single quotes
1119 #[test]
1120 fn single_quoted() {
1121 let mut iter = Attributes::new(r#"tag key = 'value' "#, 3);
1122
1123 assert_eq!(
1124 iter.next(),
1125 Some(Ok(Attribute {
1126 key: QName(b"key"),
1127 value: Cow::Borrowed(b"value"),
1128 }))
1129 );
1130 assert_eq!(iter.next(), None);
1131 assert_eq!(iter.next(), None);
1132 }
1133
1134 /// Attribute have a value enclosed in double quotes
1135 #[test]
1136 fn double_quoted() {
1137 let mut iter = Attributes::new(r#"tag key = "value" "#, 3);
1138
1139 assert_eq!(
1140 iter.next(),
1141 Some(Ok(Attribute {
1142 key: QName(b"key"),
1143 value: Cow::Borrowed(b"value"),
1144 }))
1145 );
1146 assert_eq!(iter.next(), None);
1147 assert_eq!(iter.next(), None);
1148 }
1149
1150 /// Attribute have a value, not enclosed in quotes
1151 #[test]
1152 fn unquoted() {
1153 let mut iter = Attributes::new(r#"tag key = value "#, 3);
1154 // 0 ^ = 10
1155
1156 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(10))));
1157 assert_eq!(iter.next(), None);
1158 assert_eq!(iter.next(), None);
1159 }
1160
1161 /// Only attribute key is present
1162 #[test]
1163 fn key_only() {
1164 let mut iter = Attributes::new(r#"tag key "#, 3);
1165 // 0 ^ = 8
1166
1167 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(8))));
1168 assert_eq!(iter.next(), None);
1169 assert_eq!(iter.next(), None);
1170 }
1171
1172 /// Key is started with an invalid symbol (a single quote in this test).
1173 /// Because we do not check validity of keys and values during parsing,
1174 /// that invalid attribute will be returned
1175 #[test]
1176 fn key_start_invalid() {
1177 let mut iter = Attributes::new(r#"tag 'key' = 'value' "#, 3);
1178
1179 assert_eq!(
1180 iter.next(),
1181 Some(Ok(Attribute {
1182 key: QName(b"'key'"),
1183 value: Cow::Borrowed(b"value"),
1184 }))
1185 );
1186 assert_eq!(iter.next(), None);
1187 assert_eq!(iter.next(), None);
1188 }
1189
1190 /// Key contains an invalid symbol (an ampersand in this test).
1191 /// Because we do not check validity of keys and values during parsing,
1192 /// that invalid attribute will be returned
1193 #[test]
1194 fn key_contains_invalid() {
1195 let mut iter = Attributes::new(r#"tag key&jey = 'value' "#, 3);
1196
1197 assert_eq!(
1198 iter.next(),
1199 Some(Ok(Attribute {
1200 key: QName(b"key&jey"),
1201 value: Cow::Borrowed(b"value"),
1202 }))
1203 );
1204 assert_eq!(iter.next(), None);
1205 assert_eq!(iter.next(), None);
1206 }
1207
1208 /// Attribute value is missing after `=`
1209 #[test]
1210 fn missed_value() {
1211 let mut iter = Attributes::new(r#"tag key = "#, 3);
1212 // 0 ^ = 10
1213
1214 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(10))));
1215 assert_eq!(iter.next(), None);
1216 assert_eq!(iter.next(), None);
1217 }
1218 }
1219
1220 /// Checks that duplicated attributes correctly reported and recovering is
1221 /// possible after that
1222 mod duplicated {
1223 use super::*;
1224
1225 mod with_check {
1226 use super::*;
1227 use pretty_assertions::assert_eq;
1228
1229 /// Attribute have a value enclosed in single quotes
1230 #[test]
1231 fn single_quoted() {
1232 let mut iter = Attributes::new(r#"tag key='value' key='dup' another=''"#, 3);
1233 // 0 ^ = 4 ^ = 16
1234
1235 assert_eq!(
1236 iter.next(),
1237 Some(Ok(Attribute {
1238 key: QName(b"key"),
1239 value: Cow::Borrowed(b"value"),
1240 }))
1241 );
1242 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
1243 assert_eq!(
1244 iter.next(),
1245 Some(Ok(Attribute {
1246 key: QName(b"another"),
1247 value: Cow::Borrowed(b""),
1248 }))
1249 );
1250 assert_eq!(iter.next(), None);
1251 assert_eq!(iter.next(), None);
1252 }
1253
1254 /// Attribute have a value enclosed in double quotes
1255 #[test]
1256 fn double_quoted() {
1257 let mut iter = Attributes::new(r#"tag key='value' key="dup" another=''"#, 3);
1258 // 0 ^ = 4 ^ = 16
1259
1260 assert_eq!(
1261 iter.next(),
1262 Some(Ok(Attribute {
1263 key: QName(b"key"),
1264 value: Cow::Borrowed(b"value"),
1265 }))
1266 );
1267 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
1268 assert_eq!(
1269 iter.next(),
1270 Some(Ok(Attribute {
1271 key: QName(b"another"),
1272 value: Cow::Borrowed(b""),
1273 }))
1274 );
1275 assert_eq!(iter.next(), None);
1276 assert_eq!(iter.next(), None);
1277 }
1278
1279 /// Attribute have a value, not enclosed in quotes
1280 #[test]
1281 fn unquoted() {
1282 let mut iter = Attributes::new(r#"tag key='value' key=dup another=''"#, 3);
1283 // 0 ^ = 4 ^ = 16
1284
1285 assert_eq!(
1286 iter.next(),
1287 Some(Ok(Attribute {
1288 key: QName(b"key"),
1289 value: Cow::Borrowed(b"value"),
1290 }))
1291 );
1292 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
1293 assert_eq!(
1294 iter.next(),
1295 Some(Ok(Attribute {
1296 key: QName(b"another"),
1297 value: Cow::Borrowed(b""),
1298 }))
1299 );
1300 assert_eq!(iter.next(), None);
1301 assert_eq!(iter.next(), None);
1302 }
1303
1304 /// Only attribute key is present
1305 #[test]
1306 fn key_only() {
1307 let mut iter = Attributes::new(r#"tag key='value' key another=''"#, 3);
1308 // 0 ^ = 20
1309
1310 assert_eq!(
1311 iter.next(),
1312 Some(Ok(Attribute {
1313 key: QName(b"key"),
1314 value: Cow::Borrowed(b"value"),
1315 }))
1316 );
1317 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(20))));
1318 assert_eq!(
1319 iter.next(),
1320 Some(Ok(Attribute {
1321 key: QName(b"another"),
1322 value: Cow::Borrowed(b""),
1323 }))
1324 );
1325 assert_eq!(iter.next(), None);
1326 assert_eq!(iter.next(), None);
1327 }
1328 }
1329
1330 /// Check for duplicated names is disabled
1331 mod without_check {
1332 use super::*;
1333 use pretty_assertions::assert_eq;
1334
1335 /// Attribute have a value enclosed in single quotes
1336 #[test]
1337 fn single_quoted() {
1338 let mut iter = Attributes::new(r#"tag key='value' key='dup' another=''"#, 3);
1339 iter.with_checks(false);
1340
1341 assert_eq!(
1342 iter.next(),
1343 Some(Ok(Attribute {
1344 key: QName(b"key"),
1345 value: Cow::Borrowed(b"value"),
1346 }))
1347 );
1348 assert_eq!(
1349 iter.next(),
1350 Some(Ok(Attribute {
1351 key: QName(b"key"),
1352 value: Cow::Borrowed(b"dup"),
1353 }))
1354 );
1355 assert_eq!(
1356 iter.next(),
1357 Some(Ok(Attribute {
1358 key: QName(b"another"),
1359 value: Cow::Borrowed(b""),
1360 }))
1361 );
1362 assert_eq!(iter.next(), None);
1363 assert_eq!(iter.next(), None);
1364 }
1365
1366 /// Attribute have a value enclosed in double quotes
1367 #[test]
1368 fn double_quoted() {
1369 let mut iter = Attributes::new(r#"tag key='value' key="dup" another=''"#, 3);
1370 iter.with_checks(false);
1371
1372 assert_eq!(
1373 iter.next(),
1374 Some(Ok(Attribute {
1375 key: QName(b"key"),
1376 value: Cow::Borrowed(b"value"),
1377 }))
1378 );
1379 assert_eq!(
1380 iter.next(),
1381 Some(Ok(Attribute {
1382 key: QName(b"key"),
1383 value: Cow::Borrowed(b"dup"),
1384 }))
1385 );
1386 assert_eq!(
1387 iter.next(),
1388 Some(Ok(Attribute {
1389 key: QName(b"another"),
1390 value: Cow::Borrowed(b""),
1391 }))
1392 );
1393 assert_eq!(iter.next(), None);
1394 assert_eq!(iter.next(), None);
1395 }
1396
1397 /// Attribute have a value, not enclosed in quotes
1398 #[test]
1399 fn unquoted() {
1400 let mut iter = Attributes::new(r#"tag key='value' key=dup another=''"#, 3);
1401 // 0 ^ = 20
1402 iter.with_checks(false);
1403
1404 assert_eq!(
1405 iter.next(),
1406 Some(Ok(Attribute {
1407 key: QName(b"key"),
1408 value: Cow::Borrowed(b"value"),
1409 }))
1410 );
1411 assert_eq!(iter.next(), Some(Err(AttrError::UnquotedValue(20))));
1412 assert_eq!(
1413 iter.next(),
1414 Some(Ok(Attribute {
1415 key: QName(b"another"),
1416 value: Cow::Borrowed(b""),
1417 }))
1418 );
1419 assert_eq!(iter.next(), None);
1420 assert_eq!(iter.next(), None);
1421 }
1422
1423 /// Only attribute key is present
1424 #[test]
1425 fn key_only() {
1426 let mut iter = Attributes::new(r#"tag key='value' key another=''"#, 3);
1427 // 0 ^ = 20
1428 iter.with_checks(false);
1429
1430 assert_eq!(
1431 iter.next(),
1432 Some(Ok(Attribute {
1433 key: QName(b"key"),
1434 value: Cow::Borrowed(b"value"),
1435 }))
1436 );
1437 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedEq(20))));
1438 assert_eq!(
1439 iter.next(),
1440 Some(Ok(Attribute {
1441 key: QName(b"another"),
1442 value: Cow::Borrowed(b""),
1443 }))
1444 );
1445 assert_eq!(iter.next(), None);
1446 assert_eq!(iter.next(), None);
1447 }
1448 }
1449 }
1450
1451 #[test]
1452 fn mixed_quote() {
1453 let mut iter = Attributes::new(r#"tag a='a' b = "b" c='cc"cc' d="dd'dd""#, 3);
1454
1455 assert_eq!(
1456 iter.next(),
1457 Some(Ok(Attribute {
1458 key: QName(b"a"),
1459 value: Cow::Borrowed(b"a"),
1460 }))
1461 );
1462 assert_eq!(
1463 iter.next(),
1464 Some(Ok(Attribute {
1465 key: QName(b"b"),
1466 value: Cow::Borrowed(b"b"),
1467 }))
1468 );
1469 assert_eq!(
1470 iter.next(),
1471 Some(Ok(Attribute {
1472 key: QName(b"c"),
1473 value: Cow::Borrowed(br#"cc"cc"#),
1474 }))
1475 );
1476 assert_eq!(
1477 iter.next(),
1478 Some(Ok(Attribute {
1479 key: QName(b"d"),
1480 value: Cow::Borrowed(b"dd'dd"),
1481 }))
1482 );
1483 assert_eq!(iter.next(), None);
1484 assert_eq!(iter.next(), None);
1485 }
1486}
1487
1488/// Checks, how parsing of HTML-style attributes works. Each attribute can be
1489/// in three forms:
1490/// - XML-like: have a value, enclosed in single or double quotes
1491/// - have a value, do not enclosed in quotes
1492/// - without value, key only
1493#[cfg(test)]
1494mod html {
1495 use super::*;
1496 use pretty_assertions::assert_eq;
1497
1498 /// Checked attribute is the single attribute
1499 mod single {
1500 use super::*;
1501 use pretty_assertions::assert_eq;
1502
1503 /// Attribute have a value enclosed in single quotes
1504 #[test]
1505 fn single_quoted() {
1506 let mut iter = Attributes::html(r#"tag key='value'"#, 3);
1507
1508 assert_eq!(
1509 iter.next(),
1510 Some(Ok(Attribute {
1511 key: QName(b"key"),
1512 value: Cow::Borrowed(b"value"),
1513 }))
1514 );
1515 assert_eq!(iter.next(), None);
1516 assert_eq!(iter.next(), None);
1517 }
1518
1519 /// Attribute have a value enclosed in double quotes
1520 #[test]
1521 fn double_quoted() {
1522 let mut iter = Attributes::html(r#"tag key="value""#, 3);
1523
1524 assert_eq!(
1525 iter.next(),
1526 Some(Ok(Attribute {
1527 key: QName(b"key"),
1528 value: Cow::Borrowed(b"value"),
1529 }))
1530 );
1531 assert_eq!(iter.next(), None);
1532 assert_eq!(iter.next(), None);
1533 }
1534
1535 /// Attribute have a value, not enclosed in quotes
1536 #[test]
1537 fn unquoted() {
1538 let mut iter = Attributes::html(r#"tag key=value"#, 3);
1539
1540 assert_eq!(
1541 iter.next(),
1542 Some(Ok(Attribute {
1543 key: QName(b"key"),
1544 value: Cow::Borrowed(b"value"),
1545 }))
1546 );
1547 assert_eq!(iter.next(), None);
1548 assert_eq!(iter.next(), None);
1549 }
1550
1551 /// Only attribute key is present
1552 #[test]
1553 fn key_only() {
1554 let mut iter = Attributes::html(r#"tag key"#, 3);
1555
1556 assert_eq!(
1557 iter.next(),
1558 Some(Ok(Attribute {
1559 key: QName(b"key"),
1560 value: Cow::Borrowed(&[]),
1561 }))
1562 );
1563 assert_eq!(iter.next(), None);
1564 assert_eq!(iter.next(), None);
1565 }
1566
1567 /// Key is started with an invalid symbol (a single quote in this test).
1568 /// Because we do not check validity of keys and values during parsing,
1569 /// that invalid attribute will be returned
1570 #[test]
1571 fn key_start_invalid() {
1572 let mut iter = Attributes::html(r#"tag 'key'='value'"#, 3);
1573
1574 assert_eq!(
1575 iter.next(),
1576 Some(Ok(Attribute {
1577 key: QName(b"'key'"),
1578 value: Cow::Borrowed(b"value"),
1579 }))
1580 );
1581 assert_eq!(iter.next(), None);
1582 assert_eq!(iter.next(), None);
1583 }
1584
1585 /// Key contains an invalid symbol (an ampersand in this test).
1586 /// Because we do not check validity of keys and values during parsing,
1587 /// that invalid attribute will be returned
1588 #[test]
1589 fn key_contains_invalid() {
1590 let mut iter = Attributes::html(r#"tag key&jey='value'"#, 3);
1591
1592 assert_eq!(
1593 iter.next(),
1594 Some(Ok(Attribute {
1595 key: QName(b"key&jey"),
1596 value: Cow::Borrowed(b"value"),
1597 }))
1598 );
1599 assert_eq!(iter.next(), None);
1600 assert_eq!(iter.next(), None);
1601 }
1602
1603 /// Attribute value is missing after `=`
1604 #[test]
1605 fn missed_value() {
1606 let mut iter = Attributes::html(r#"tag key="#, 3);
1607 // 0 ^ = 8
1608
1609 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(8))));
1610 assert_eq!(iter.next(), None);
1611 assert_eq!(iter.next(), None);
1612 }
1613 }
1614
1615 /// Checked attribute is the first attribute in the list of many attributes
1616 mod first {
1617 use super::*;
1618 use pretty_assertions::assert_eq;
1619
1620 /// Attribute have a value enclosed in single quotes
1621 #[test]
1622 fn single_quoted() {
1623 let mut iter = Attributes::html(r#"tag key='value' regular='attribute'"#, 3);
1624
1625 assert_eq!(
1626 iter.next(),
1627 Some(Ok(Attribute {
1628 key: QName(b"key"),
1629 value: Cow::Borrowed(b"value"),
1630 }))
1631 );
1632 assert_eq!(
1633 iter.next(),
1634 Some(Ok(Attribute {
1635 key: QName(b"regular"),
1636 value: Cow::Borrowed(b"attribute"),
1637 }))
1638 );
1639 assert_eq!(iter.next(), None);
1640 assert_eq!(iter.next(), None);
1641 }
1642
1643 /// Attribute have a value enclosed in double quotes
1644 #[test]
1645 fn double_quoted() {
1646 let mut iter = Attributes::html(r#"tag key="value" regular='attribute'"#, 3);
1647
1648 assert_eq!(
1649 iter.next(),
1650 Some(Ok(Attribute {
1651 key: QName(b"key"),
1652 value: Cow::Borrowed(b"value"),
1653 }))
1654 );
1655 assert_eq!(
1656 iter.next(),
1657 Some(Ok(Attribute {
1658 key: QName(b"regular"),
1659 value: Cow::Borrowed(b"attribute"),
1660 }))
1661 );
1662 assert_eq!(iter.next(), None);
1663 assert_eq!(iter.next(), None);
1664 }
1665
1666 /// Attribute have a value, not enclosed in quotes
1667 #[test]
1668 fn unquoted() {
1669 let mut iter = Attributes::html(r#"tag key=value regular='attribute'"#, 3);
1670
1671 assert_eq!(
1672 iter.next(),
1673 Some(Ok(Attribute {
1674 key: QName(b"key"),
1675 value: Cow::Borrowed(b"value"),
1676 }))
1677 );
1678 assert_eq!(
1679 iter.next(),
1680 Some(Ok(Attribute {
1681 key: QName(b"regular"),
1682 value: Cow::Borrowed(b"attribute"),
1683 }))
1684 );
1685 assert_eq!(iter.next(), None);
1686 assert_eq!(iter.next(), None);
1687 }
1688
1689 /// Only attribute key is present
1690 #[test]
1691 fn key_only() {
1692 let mut iter = Attributes::html(r#"tag key regular='attribute'"#, 3);
1693
1694 assert_eq!(
1695 iter.next(),
1696 Some(Ok(Attribute {
1697 key: QName(b"key"),
1698 value: Cow::Borrowed(&[]),
1699 }))
1700 );
1701 assert_eq!(
1702 iter.next(),
1703 Some(Ok(Attribute {
1704 key: QName(b"regular"),
1705 value: Cow::Borrowed(b"attribute"),
1706 }))
1707 );
1708 assert_eq!(iter.next(), None);
1709 assert_eq!(iter.next(), None);
1710 }
1711
1712 /// Key is started with an invalid symbol (a single quote in this test).
1713 /// Because we do not check validity of keys and values during parsing,
1714 /// that invalid attribute will be returned
1715 #[test]
1716 fn key_start_invalid() {
1717 let mut iter = Attributes::html(r#"tag 'key'='value' regular='attribute'"#, 3);
1718
1719 assert_eq!(
1720 iter.next(),
1721 Some(Ok(Attribute {
1722 key: QName(b"'key'"),
1723 value: Cow::Borrowed(b"value"),
1724 }))
1725 );
1726 assert_eq!(
1727 iter.next(),
1728 Some(Ok(Attribute {
1729 key: QName(b"regular"),
1730 value: Cow::Borrowed(b"attribute"),
1731 }))
1732 );
1733 assert_eq!(iter.next(), None);
1734 assert_eq!(iter.next(), None);
1735 }
1736
1737 /// Key contains an invalid symbol (an ampersand in this test).
1738 /// Because we do not check validity of keys and values during parsing,
1739 /// that invalid attribute will be returned
1740 #[test]
1741 fn key_contains_invalid() {
1742 let mut iter = Attributes::html(r#"tag key&jey='value' regular='attribute'"#, 3);
1743
1744 assert_eq!(
1745 iter.next(),
1746 Some(Ok(Attribute {
1747 key: QName(b"key&jey"),
1748 value: Cow::Borrowed(b"value"),
1749 }))
1750 );
1751 assert_eq!(
1752 iter.next(),
1753 Some(Ok(Attribute {
1754 key: QName(b"regular"),
1755 value: Cow::Borrowed(b"attribute"),
1756 }))
1757 );
1758 assert_eq!(iter.next(), None);
1759 assert_eq!(iter.next(), None);
1760 }
1761
1762 /// Attribute value is missing after `=`
1763 #[test]
1764 fn missed_value() {
1765 let mut iter = Attributes::html(r#"tag key= regular='attribute'"#, 3);
1766
1767 // Because we do not check validity of keys and values during parsing,
1768 // "regular='attribute'" is considered as unquoted attribute value
1769 assert_eq!(
1770 iter.next(),
1771 Some(Ok(Attribute {
1772 key: QName(b"key"),
1773 value: Cow::Borrowed(b"regular='attribute'"),
1774 }))
1775 );
1776 assert_eq!(iter.next(), None);
1777 assert_eq!(iter.next(), None);
1778
1779 ////////////////////////////////////////////////////////////////////
1780
1781 let mut iter = Attributes::html(r#"tag key= regular= 'attribute'"#, 3);
1782
1783 // Because we do not check validity of keys and values during parsing,
1784 // "regular=" is considered as unquoted attribute value
1785 assert_eq!(
1786 iter.next(),
1787 Some(Ok(Attribute {
1788 key: QName(b"key"),
1789 value: Cow::Borrowed(b"regular="),
1790 }))
1791 );
1792 // Because we do not check validity of keys and values during parsing,
1793 // "'attribute'" is considered as key-only attribute
1794 assert_eq!(
1795 iter.next(),
1796 Some(Ok(Attribute {
1797 key: QName(b"'attribute'"),
1798 value: Cow::Borrowed(&[]),
1799 }))
1800 );
1801 assert_eq!(iter.next(), None);
1802 assert_eq!(iter.next(), None);
1803
1804 ////////////////////////////////////////////////////////////////////
1805
1806 let mut iter = Attributes::html(r#"tag key= regular ='attribute'"#, 3);
1807
1808 // Because we do not check validity of keys and values during parsing,
1809 // "regular" is considered as unquoted attribute value
1810 assert_eq!(
1811 iter.next(),
1812 Some(Ok(Attribute {
1813 key: QName(b"key"),
1814 value: Cow::Borrowed(b"regular"),
1815 }))
1816 );
1817 // Because we do not check validity of keys and values during parsing,
1818 // "='attribute'" is considered as key-only attribute
1819 assert_eq!(
1820 iter.next(),
1821 Some(Ok(Attribute {
1822 key: QName(b"='attribute'"),
1823 value: Cow::Borrowed(&[]),
1824 }))
1825 );
1826 assert_eq!(iter.next(), None);
1827 assert_eq!(iter.next(), None);
1828
1829 ////////////////////////////////////////////////////////////////////
1830
1831 let mut iter = Attributes::html(r#"tag key= regular = 'attribute'"#, 3);
1832 // 0 ^ = 9 ^ = 19 ^ = 30
1833
1834 // Because we do not check validity of keys and values during parsing,
1835 // "regular" is considered as unquoted attribute value
1836 assert_eq!(
1837 iter.next(),
1838 Some(Ok(Attribute {
1839 key: QName(b"key"),
1840 value: Cow::Borrowed(b"regular"),
1841 }))
1842 );
1843 // Because we do not check validity of keys and values during parsing,
1844 // "=" is considered as key-only attribute
1845 assert_eq!(
1846 iter.next(),
1847 Some(Ok(Attribute {
1848 key: QName(b"="),
1849 value: Cow::Borrowed(&[]),
1850 }))
1851 );
1852 // Because we do not check validity of keys and values during parsing,
1853 // "'attribute'" is considered as key-only attribute
1854 assert_eq!(
1855 iter.next(),
1856 Some(Ok(Attribute {
1857 key: QName(b"'attribute'"),
1858 value: Cow::Borrowed(&[]),
1859 }))
1860 );
1861 assert_eq!(iter.next(), None);
1862 assert_eq!(iter.next(), None);
1863 }
1864 }
1865
1866 /// Copy of single, but with additional spaces in markup
1867 mod sparsed {
1868 use super::*;
1869 use pretty_assertions::assert_eq;
1870
1871 /// Attribute have a value enclosed in single quotes
1872 #[test]
1873 fn single_quoted() {
1874 let mut iter = Attributes::html(r#"tag key = 'value' "#, 3);
1875
1876 assert_eq!(
1877 iter.next(),
1878 Some(Ok(Attribute {
1879 key: QName(b"key"),
1880 value: Cow::Borrowed(b"value"),
1881 }))
1882 );
1883 assert_eq!(iter.next(), None);
1884 assert_eq!(iter.next(), None);
1885 }
1886
1887 /// Attribute have a value enclosed in double quotes
1888 #[test]
1889 fn double_quoted() {
1890 let mut iter = Attributes::html(r#"tag key = "value" "#, 3);
1891
1892 assert_eq!(
1893 iter.next(),
1894 Some(Ok(Attribute {
1895 key: QName(b"key"),
1896 value: Cow::Borrowed(b"value"),
1897 }))
1898 );
1899 assert_eq!(iter.next(), None);
1900 assert_eq!(iter.next(), None);
1901 }
1902
1903 /// Attribute have a value, not enclosed in quotes
1904 #[test]
1905 fn unquoted() {
1906 let mut iter = Attributes::html(r#"tag key = value "#, 3);
1907
1908 assert_eq!(
1909 iter.next(),
1910 Some(Ok(Attribute {
1911 key: QName(b"key"),
1912 value: Cow::Borrowed(b"value"),
1913 }))
1914 );
1915 assert_eq!(iter.next(), None);
1916 assert_eq!(iter.next(), None);
1917 }
1918
1919 /// Only attribute key is present
1920 #[test]
1921 fn key_only() {
1922 let mut iter = Attributes::html(r#"tag key "#, 3);
1923
1924 assert_eq!(
1925 iter.next(),
1926 Some(Ok(Attribute {
1927 key: QName(b"key"),
1928 value: Cow::Borrowed(&[]),
1929 }))
1930 );
1931 assert_eq!(iter.next(), None);
1932 assert_eq!(iter.next(), None);
1933 }
1934
1935 /// Key is started with an invalid symbol (a single quote in this test).
1936 /// Because we do not check validity of keys and values during parsing,
1937 /// that invalid attribute will be returned
1938 #[test]
1939 fn key_start_invalid() {
1940 let mut iter = Attributes::html(r#"tag 'key' = 'value' "#, 3);
1941
1942 assert_eq!(
1943 iter.next(),
1944 Some(Ok(Attribute {
1945 key: QName(b"'key'"),
1946 value: Cow::Borrowed(b"value"),
1947 }))
1948 );
1949 assert_eq!(iter.next(), None);
1950 assert_eq!(iter.next(), None);
1951 }
1952
1953 /// Key contains an invalid symbol (an ampersand in this test).
1954 /// Because we do not check validity of keys and values during parsing,
1955 /// that invalid attribute will be returned
1956 #[test]
1957 fn key_contains_invalid() {
1958 let mut iter = Attributes::html(r#"tag key&jey = 'value' "#, 3);
1959
1960 assert_eq!(
1961 iter.next(),
1962 Some(Ok(Attribute {
1963 key: QName(b"key&jey"),
1964 value: Cow::Borrowed(b"value"),
1965 }))
1966 );
1967 assert_eq!(iter.next(), None);
1968 assert_eq!(iter.next(), None);
1969 }
1970
1971 /// Attribute value is missing after `=`
1972 #[test]
1973 fn missed_value() {
1974 let mut iter = Attributes::html(r#"tag key = "#, 3);
1975 // 0 ^ = 10
1976
1977 assert_eq!(iter.next(), Some(Err(AttrError::ExpectedValue(10))));
1978 assert_eq!(iter.next(), None);
1979 assert_eq!(iter.next(), None);
1980 }
1981 }
1982
1983 /// Checks that duplicated attributes correctly reported and recovering is
1984 /// possible after that
1985 mod duplicated {
1986 use super::*;
1987
1988 mod with_check {
1989 use super::*;
1990 use pretty_assertions::assert_eq;
1991
1992 /// Attribute have a value enclosed in single quotes
1993 #[test]
1994 fn single_quoted() {
1995 let mut iter = Attributes::html(r#"tag key='value' key='dup' another=''"#, 3);
1996 // 0 ^ = 4 ^ = 16
1997
1998 assert_eq!(
1999 iter.next(),
2000 Some(Ok(Attribute {
2001 key: QName(b"key"),
2002 value: Cow::Borrowed(b"value"),
2003 }))
2004 );
2005 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2006 assert_eq!(
2007 iter.next(),
2008 Some(Ok(Attribute {
2009 key: QName(b"another"),
2010 value: Cow::Borrowed(b""),
2011 }))
2012 );
2013 assert_eq!(iter.next(), None);
2014 assert_eq!(iter.next(), None);
2015 }
2016
2017 /// Attribute have a value enclosed in double quotes
2018 #[test]
2019 fn double_quoted() {
2020 let mut iter = Attributes::html(r#"tag key='value' key="dup" another=''"#, 3);
2021 // 0 ^ = 4 ^ = 16
2022
2023 assert_eq!(
2024 iter.next(),
2025 Some(Ok(Attribute {
2026 key: QName(b"key"),
2027 value: Cow::Borrowed(b"value"),
2028 }))
2029 );
2030 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2031 assert_eq!(
2032 iter.next(),
2033 Some(Ok(Attribute {
2034 key: QName(b"another"),
2035 value: Cow::Borrowed(b""),
2036 }))
2037 );
2038 assert_eq!(iter.next(), None);
2039 assert_eq!(iter.next(), None);
2040 }
2041
2042 /// Attribute have a value, not enclosed in quotes
2043 #[test]
2044 fn unquoted() {
2045 let mut iter = Attributes::html(r#"tag key='value' key=dup another=''"#, 3);
2046 // 0 ^ = 4 ^ = 16
2047
2048 assert_eq!(
2049 iter.next(),
2050 Some(Ok(Attribute {
2051 key: QName(b"key"),
2052 value: Cow::Borrowed(b"value"),
2053 }))
2054 );
2055 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2056 assert_eq!(
2057 iter.next(),
2058 Some(Ok(Attribute {
2059 key: QName(b"another"),
2060 value: Cow::Borrowed(b""),
2061 }))
2062 );
2063 assert_eq!(iter.next(), None);
2064 assert_eq!(iter.next(), None);
2065 }
2066
2067 /// Only attribute key is present
2068 #[test]
2069 fn key_only() {
2070 let mut iter = Attributes::html(r#"tag key='value' key another=''"#, 3);
2071 // 0 ^ = 4 ^ = 16
2072
2073 assert_eq!(
2074 iter.next(),
2075 Some(Ok(Attribute {
2076 key: QName(b"key"),
2077 value: Cow::Borrowed(b"value"),
2078 }))
2079 );
2080 assert_eq!(iter.next(), Some(Err(AttrError::Duplicated(16, 4))));
2081 assert_eq!(
2082 iter.next(),
2083 Some(Ok(Attribute {
2084 key: QName(b"another"),
2085 value: Cow::Borrowed(b""),
2086 }))
2087 );
2088 assert_eq!(iter.next(), None);
2089 assert_eq!(iter.next(), None);
2090 }
2091 }
2092
2093 /// Check for duplicated names is disabled
2094 mod without_check {
2095 use super::*;
2096 use pretty_assertions::assert_eq;
2097
2098 /// Attribute have a value enclosed in single quotes
2099 #[test]
2100 fn single_quoted() {
2101 let mut iter = Attributes::html(r#"tag key='value' key='dup' another=''"#, 3);
2102 iter.with_checks(false);
2103
2104 assert_eq!(
2105 iter.next(),
2106 Some(Ok(Attribute {
2107 key: QName(b"key"),
2108 value: Cow::Borrowed(b"value"),
2109 }))
2110 );
2111 assert_eq!(
2112 iter.next(),
2113 Some(Ok(Attribute {
2114 key: QName(b"key"),
2115 value: Cow::Borrowed(b"dup"),
2116 }))
2117 );
2118 assert_eq!(
2119 iter.next(),
2120 Some(Ok(Attribute {
2121 key: QName(b"another"),
2122 value: Cow::Borrowed(b""),
2123 }))
2124 );
2125 assert_eq!(iter.next(), None);
2126 assert_eq!(iter.next(), None);
2127 }
2128
2129 /// Attribute have a value enclosed in double quotes
2130 #[test]
2131 fn double_quoted() {
2132 let mut iter = Attributes::html(r#"tag key='value' key="dup" another=''"#, 3);
2133 iter.with_checks(false);
2134
2135 assert_eq!(
2136 iter.next(),
2137 Some(Ok(Attribute {
2138 key: QName(b"key"),
2139 value: Cow::Borrowed(b"value"),
2140 }))
2141 );
2142 assert_eq!(
2143 iter.next(),
2144 Some(Ok(Attribute {
2145 key: QName(b"key"),
2146 value: Cow::Borrowed(b"dup"),
2147 }))
2148 );
2149 assert_eq!(
2150 iter.next(),
2151 Some(Ok(Attribute {
2152 key: QName(b"another"),
2153 value: Cow::Borrowed(b""),
2154 }))
2155 );
2156 assert_eq!(iter.next(), None);
2157 assert_eq!(iter.next(), None);
2158 }
2159
2160 /// Attribute have a value, not enclosed in quotes
2161 #[test]
2162 fn unquoted() {
2163 let mut iter = Attributes::html(r#"tag key='value' key=dup another=''"#, 3);
2164 iter.with_checks(false);
2165
2166 assert_eq!(
2167 iter.next(),
2168 Some(Ok(Attribute {
2169 key: QName(b"key"),
2170 value: Cow::Borrowed(b"value"),
2171 }))
2172 );
2173 assert_eq!(
2174 iter.next(),
2175 Some(Ok(Attribute {
2176 key: QName(b"key"),
2177 value: Cow::Borrowed(b"dup"),
2178 }))
2179 );
2180 assert_eq!(
2181 iter.next(),
2182 Some(Ok(Attribute {
2183 key: QName(b"another"),
2184 value: Cow::Borrowed(b""),
2185 }))
2186 );
2187 assert_eq!(iter.next(), None);
2188 assert_eq!(iter.next(), None);
2189 }
2190
2191 /// Only attribute key is present
2192 #[test]
2193 fn key_only() {
2194 let mut iter = Attributes::html(r#"tag key='value' key another=''"#, 3);
2195 iter.with_checks(false);
2196
2197 assert_eq!(
2198 iter.next(),
2199 Some(Ok(Attribute {
2200 key: QName(b"key"),
2201 value: Cow::Borrowed(b"value"),
2202 }))
2203 );
2204 assert_eq!(
2205 iter.next(),
2206 Some(Ok(Attribute {
2207 key: QName(b"key"),
2208 value: Cow::Borrowed(&[]),
2209 }))
2210 );
2211 assert_eq!(
2212 iter.next(),
2213 Some(Ok(Attribute {
2214 key: QName(b"another"),
2215 value: Cow::Borrowed(b""),
2216 }))
2217 );
2218 assert_eq!(iter.next(), None);
2219 assert_eq!(iter.next(), None);
2220 }
2221 }
2222 }
2223
2224 #[test]
2225 fn mixed_quote() {
2226 let mut iter = Attributes::html(r#"tag a='a' b = "b" c='cc"cc' d="dd'dd""#, 3);
2227
2228 assert_eq!(
2229 iter.next(),
2230 Some(Ok(Attribute {
2231 key: QName(b"a"),
2232 value: Cow::Borrowed(b"a"),
2233 }))
2234 );
2235 assert_eq!(
2236 iter.next(),
2237 Some(Ok(Attribute {
2238 key: QName(b"b"),
2239 value: Cow::Borrowed(b"b"),
2240 }))
2241 );
2242 assert_eq!(
2243 iter.next(),
2244 Some(Ok(Attribute {
2245 key: QName(b"c"),
2246 value: Cow::Borrowed(br#"cc"cc"#),
2247 }))
2248 );
2249 assert_eq!(
2250 iter.next(),
2251 Some(Ok(Attribute {
2252 key: QName(b"d"),
2253 value: Cow::Borrowed(b"dd'dd"),
2254 }))
2255 );
2256 assert_eq!(iter.next(), None);
2257 assert_eq!(iter.next(), None);
2258 }
2259}