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