From 6e7293b93159e75a5bcb02fe7405d8df47b1f4ba Mon Sep 17 00:00:00 2001 From: Jayonas <37936740+Jayonas@users.noreply.github.com> Date: Wed, 22 May 2024 04:44:35 -0400 Subject: [PATCH] Add `Attribute.range()` method. Closes #113 --- Cargo.toml | 2 +- src/lib.rs | 17 +++++++++++++++-- src/parse.rs | 28 ++++++++++++++-------------- src/tokenizer.rs | 5 +++-- tests/api.rs | 6 ++++-- 5 files changed, 37 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1cfd9f..6ca4850 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,5 +20,5 @@ exclude = ["testing-tools"] default = ["std", "positions"] std = [] # Enables Nodes and Attributes position in the original document preserving. -# Increases memory usage by `usize` for each Node and Attribute. +# Increases memory usage by `Range` for each Node and Attribute. positions = [] diff --git a/src/lib.rs b/src/lib.rs index 8446d12..a851aa3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -489,7 +489,7 @@ struct AttributeData<'input> { name: ExpandedNameIndexed<'input>, value: StringStorage<'input>, #[cfg(feature = "positions")] - pos: usize, + range: Range, } /// An attribute. @@ -569,10 +569,23 @@ impl<'a, 'input> Attribute<'a, 'input> { /// ``` /// /// [Document::text_pos_at]: struct.Document.html#method.text_pos_at + #[deprecated(note="replaced by `range`")] #[cfg(feature = "positions")] #[inline] pub fn position(&self) -> usize { - self.data.pos + self.data.range.start + } + + /// Returns attribute's range in bytes in the original document. + /// + /// ```text + /// + /// ^^^^^^^^^^^^^^ + /// ``` + #[cfg(feature = "positions")] + #[inline] + pub fn range(&self) -> Range { + self.data.range.clone() } } diff --git a/src/parse.rs b/src/parse.rs index 42d51a7..5470ef4 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -352,7 +352,7 @@ struct TempAttributeData<'input> { prefix: &'input str, local: &'input str, value: StringStorage<'input>, - pos: usize, + range: Range, } impl<'input> Document<'input> { @@ -644,8 +644,8 @@ impl<'input> tokenizer::XmlEvents<'input> for Context<'input> { self.after_text = false; } - tokenizer::Token::Attribute(attr_start, prefix, local, value) => { - process_attribute(attr_start, prefix, local, value, self)?; + tokenizer::Token::Attribute(range, prefix, local, value) => { + process_attribute(range, prefix, local, value, self)?; } tokenizer::Token::ElementEnd(end, range) => { process_element(end, range, self)?; @@ -665,7 +665,7 @@ impl<'input> tokenizer::XmlEvents<'input> for Context<'input> { #[allow(clippy::too_many_arguments)] fn process_attribute<'input>( - attr_pos: usize, + range: Range, prefix: &'input str, local: &'input str, value: StrSpan<'input>, @@ -676,7 +676,7 @@ fn process_attribute<'input>( if prefix == XMLNS { // The xmlns namespace MUST NOT be declared as the default namespace. if value.as_str() == NS_XMLNS_URI { - let pos = ctx.err_pos_at(attr_pos); + let pos = ctx.err_pos_at(range.start); return Err(Error::UnexpectedXmlnsUri(pos)); } @@ -687,13 +687,13 @@ fn process_attribute<'input>( // It MUST NOT be bound to any other namespace name. if local == NS_XML_PREFIX { if !is_xml_ns_uri { - let pos = ctx.err_pos_at(attr_pos); + let pos = ctx.err_pos_at(range.start); return Err(Error::InvalidXmlPrefixUri(pos)); } } else { // The xml namespace MUST NOT be bound to a non-xml prefix. if is_xml_ns_uri { - let pos = ctx.err_pos_at(attr_pos); + let pos = ctx.err_pos_at(range.start); return Err(Error::UnexpectedXmlUri(pos)); } } @@ -704,7 +704,7 @@ fn process_attribute<'input>( .namespaces .exists(ctx.namespace_start_idx, Some(local)) { - let pos = ctx.err_pos_at(attr_pos); + let pos = ctx.err_pos_at(range.start); return Err(Error::DuplicatedNamespace(local.to_string(), pos)); } @@ -715,13 +715,13 @@ fn process_attribute<'input>( } else if local == XMLNS { // The xml namespace MUST NOT be declared as the default namespace. if value.as_str() == NS_XML_URI { - let pos = ctx.err_pos_at(attr_pos); + let pos = ctx.err_pos_at(range.start); return Err(Error::UnexpectedXmlUri(pos)); } // The xmlns namespace MUST NOT be declared as the default namespace. if value.as_str() == NS_XMLNS_URI { - let pos = ctx.err_pos_at(attr_pos); + let pos = ctx.err_pos_at(range.start); return Err(Error::UnexpectedXmlnsUri(pos)); } @@ -731,7 +731,7 @@ fn process_attribute<'input>( prefix, local, value, - pos: attr_pos, + range, }); } @@ -888,7 +888,7 @@ fn resolve_attributes(namespaces: ShortRange, ctx: &mut Context) -> Result Result Result { ElementStart(&'input str, &'input str, usize), // ns:attr="value" - Attribute(usize, &'input str, &'input str, StrSpan<'input>), + Attribute(Range, &'input str, &'input str, StrSpan<'input>), ElementEnd(ElementEnd<'input>, Range), @@ -561,7 +561,8 @@ fn parse_element<'input>(s: &mut Stream<'input>, events: &mut dyn XmlEvents<'inp s.skip_chars(|_, c| c != quote_c && c != '<')?; let value = s.slice_back_span(value_start); s.consume_byte(quote)?; - events.token(Token::Attribute(start, prefix, local, value))?; + let end = s.pos(); + events.token(Token::Attribute(start..end, prefix, local, value))?; } } } diff --git a/tests/api.rs b/tests/api.rs index 100a0e2..211f7d3 100644 --- a/tests/api.rs +++ b/tests/api.rs @@ -156,7 +156,8 @@ fn text_pos_01() { assert_eq!(doc.text_pos_at(node.range().end), TextPos::new(4, 5)); if let Some(attr) = node.attribute_node("a") { - assert_eq!(doc.text_pos_at(attr.position()), TextPos::new(1, 4)); + assert_eq!(doc.text_pos_at(attr.range().start), TextPos::new(1, 4)); + assert_eq!(doc.text_pos_at(attr.range().end), TextPos::new(1, 9)); } // first child is a text/whitespace, not a comment @@ -181,7 +182,8 @@ fn text_pos_02() { assert_eq!(doc.text_pos_at(node.range().start), TextPos::new(1, 1)); if let Some(attr) = node.attribute_node(("http://www.w3.org", "a")) { - assert_eq!(doc.text_pos_at(attr.position()), TextPos::new(1, 36)); + assert_eq!(doc.text_pos_at(attr.range().start), TextPos::new(1, 36)); + assert_eq!(doc.text_pos_at(attr.range().end), TextPos::new(1, 44)); } }