Skip to content

Commit

Permalink
Add Attribute.range() method.
Browse files Browse the repository at this point in the history
Closes #113
  • Loading branch information
Jayonas authored May 22, 2024
1 parent 0a6e7af commit 6e7293b
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 21 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize>` for each Node and Attribute.
positions = []
17 changes: 15 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ struct AttributeData<'input> {
name: ExpandedNameIndexed<'input>,
value: StringStorage<'input>,
#[cfg(feature = "positions")]
pos: usize,
range: Range<usize>,
}

/// An attribute.
Expand Down Expand Up @@ -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
/// <e n:attr='value'/>
/// ^^^^^^^^^^^^^^
/// ```
#[cfg(feature = "positions")]
#[inline]
pub fn range(&self) -> Range<usize> {
self.data.range.clone()
}
}

Expand Down
28 changes: 14 additions & 14 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ struct TempAttributeData<'input> {
prefix: &'input str,
local: &'input str,
value: StringStorage<'input>,
pos: usize,
range: Range<usize>,
}

impl<'input> Document<'input> {
Expand Down Expand Up @@ -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)?;
Expand All @@ -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<usize>,
prefix: &'input str,
local: &'input str,
value: StrSpan<'input>,
Expand All @@ -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));
}

Expand All @@ -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));
}
}
Expand All @@ -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));
}

Expand All @@ -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));
}

Expand All @@ -731,7 +731,7 @@ fn process_attribute<'input>(
prefix,
local,
value,
pos: attr_pos,
range,
});
}

Expand Down Expand Up @@ -888,7 +888,7 @@ fn resolve_attributes(namespaces: ShortRange, ctx: &mut Context) -> Result<Short
// always has no value.'
None
} else {
get_ns_idx_by_prefix(namespaces, attr.pos, attr.prefix, ctx)?
get_ns_idx_by_prefix(namespaces, attr.range.start, attr.prefix, ctx)?
};

let attr_name = ExpandedNameIndexed {
Expand All @@ -900,15 +900,15 @@ fn resolve_attributes(namespaces: ShortRange, ctx: &mut Context) -> Result<Short
if ctx.doc.attributes[start_idx..].iter().any(|attr| {
attr.name.as_expanded_name(&ctx.doc) == attr_name.as_expanded_name(&ctx.doc)
}) {
let pos = ctx.err_pos_at(attr.pos);
let pos = ctx.err_pos_at(attr.range.start);
return Err(Error::DuplicatedAttribute(attr.local.to_string(), pos));
}

ctx.doc.attributes.push(AttributeData {
name: attr_name,
value: attr.value,
#[cfg(feature = "positions")]
pos: attr.pos,
range: attr.range,
});
}

Expand Down
5 changes: 3 additions & 2 deletions src/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ pub enum Token<'input> {
ElementStart(&'input str, &'input str, usize),

// ns:attr="value"
Attribute(usize, &'input str, &'input str, StrSpan<'input>),
Attribute(Range<usize>, &'input str, &'input str, StrSpan<'input>),

ElementEnd(ElementEnd<'input>, Range<usize>),

Expand Down Expand Up @@ -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))?;
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions tests/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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));
}
}

Expand Down

0 comments on commit 6e7293b

Please sign in to comment.