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