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 | #[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 `>` 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 |
|
110 | impl<'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 |
|
120 | impl<'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 & whistles" .as_bytes()));
|
131 | /// assert_eq!(features.value, "Bells & 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 |
|
141 | impl<'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 & 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 |
|
165 | impl<'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)]
|
184 | pub 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 |
|
191 | impl<'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 |
|
223 | impl<'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 |
|
236 | impl<'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)]
|
245 | pub 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 |
|
329 | impl 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 |
|
361 | impl 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)]
|
369 | pub 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>`</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 |
|
396 | impl<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 |
|
412 | impl<'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 |
|
438 | impl<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.
|
467 | impl<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 |
|
481 | type AttrResult = Result<Attr<Range<usize>>, AttrError>;
|
482 |
|
483 | #[derive (Clone, Copy, Debug)]
|
484 | enum 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)]
|
501 | pub(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 |
|
516 | impl 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)]
|
791 | mod 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)]
|
1478 | mod 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 | |