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