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