Skip to content

Commit 6e7293b

Browse files
authored
Add Attribute.range() method.
Closes #113
1 parent 0a6e7af commit 6e7293b

File tree

5 files changed

+37
-21
lines changed

5 files changed

+37
-21
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ exclude = ["testing-tools"]
2020
default = ["std", "positions"]
2121
std = []
2222
# Enables Nodes and Attributes position in the original document preserving.
23-
# Increases memory usage by `usize` for each Node and Attribute.
23+
# Increases memory usage by `Range<usize>` for each Node and Attribute.
2424
positions = []

src/lib.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ struct AttributeData<'input> {
489489
name: ExpandedNameIndexed<'input>,
490490
value: StringStorage<'input>,
491491
#[cfg(feature = "positions")]
492-
pos: usize,
492+
range: Range<usize>,
493493
}
494494

495495
/// An attribute.
@@ -569,10 +569,23 @@ impl<'a, 'input> Attribute<'a, 'input> {
569569
/// ```
570570
///
571571
/// [Document::text_pos_at]: struct.Document.html#method.text_pos_at
572+
#[deprecated(note="replaced by `range`")]
572573
#[cfg(feature = "positions")]
573574
#[inline]
574575
pub fn position(&self) -> usize {
575-
self.data.pos
576+
self.data.range.start
577+
}
578+
579+
/// Returns attribute's range in bytes in the original document.
580+
///
581+
/// ```text
582+
/// <e n:attr='value'/>
583+
/// ^^^^^^^^^^^^^^
584+
/// ```
585+
#[cfg(feature = "positions")]
586+
#[inline]
587+
pub fn range(&self) -> Range<usize> {
588+
self.data.range.clone()
576589
}
577590
}
578591

src/parse.rs

+14-14
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ struct TempAttributeData<'input> {
352352
prefix: &'input str,
353353
local: &'input str,
354354
value: StringStorage<'input>,
355-
pos: usize,
355+
range: Range<usize>,
356356
}
357357

358358
impl<'input> Document<'input> {
@@ -644,8 +644,8 @@ impl<'input> tokenizer::XmlEvents<'input> for Context<'input> {
644644

645645
self.after_text = false;
646646
}
647-
tokenizer::Token::Attribute(attr_start, prefix, local, value) => {
648-
process_attribute(attr_start, prefix, local, value, self)?;
647+
tokenizer::Token::Attribute(range, prefix, local, value) => {
648+
process_attribute(range, prefix, local, value, self)?;
649649
}
650650
tokenizer::Token::ElementEnd(end, range) => {
651651
process_element(end, range, self)?;
@@ -665,7 +665,7 @@ impl<'input> tokenizer::XmlEvents<'input> for Context<'input> {
665665

666666
#[allow(clippy::too_many_arguments)]
667667
fn process_attribute<'input>(
668-
attr_pos: usize,
668+
range: Range<usize>,
669669
prefix: &'input str,
670670
local: &'input str,
671671
value: StrSpan<'input>,
@@ -676,7 +676,7 @@ fn process_attribute<'input>(
676676
if prefix == XMLNS {
677677
// The xmlns namespace MUST NOT be declared as the default namespace.
678678
if value.as_str() == NS_XMLNS_URI {
679-
let pos = ctx.err_pos_at(attr_pos);
679+
let pos = ctx.err_pos_at(range.start);
680680
return Err(Error::UnexpectedXmlnsUri(pos));
681681
}
682682

@@ -687,13 +687,13 @@ fn process_attribute<'input>(
687687
// It MUST NOT be bound to any other namespace name.
688688
if local == NS_XML_PREFIX {
689689
if !is_xml_ns_uri {
690-
let pos = ctx.err_pos_at(attr_pos);
690+
let pos = ctx.err_pos_at(range.start);
691691
return Err(Error::InvalidXmlPrefixUri(pos));
692692
}
693693
} else {
694694
// The xml namespace MUST NOT be bound to a non-xml prefix.
695695
if is_xml_ns_uri {
696-
let pos = ctx.err_pos_at(attr_pos);
696+
let pos = ctx.err_pos_at(range.start);
697697
return Err(Error::UnexpectedXmlUri(pos));
698698
}
699699
}
@@ -704,7 +704,7 @@ fn process_attribute<'input>(
704704
.namespaces
705705
.exists(ctx.namespace_start_idx, Some(local))
706706
{
707-
let pos = ctx.err_pos_at(attr_pos);
707+
let pos = ctx.err_pos_at(range.start);
708708
return Err(Error::DuplicatedNamespace(local.to_string(), pos));
709709
}
710710

@@ -715,13 +715,13 @@ fn process_attribute<'input>(
715715
} else if local == XMLNS {
716716
// The xml namespace MUST NOT be declared as the default namespace.
717717
if value.as_str() == NS_XML_URI {
718-
let pos = ctx.err_pos_at(attr_pos);
718+
let pos = ctx.err_pos_at(range.start);
719719
return Err(Error::UnexpectedXmlUri(pos));
720720
}
721721

722722
// The xmlns namespace MUST NOT be declared as the default namespace.
723723
if value.as_str() == NS_XMLNS_URI {
724-
let pos = ctx.err_pos_at(attr_pos);
724+
let pos = ctx.err_pos_at(range.start);
725725
return Err(Error::UnexpectedXmlnsUri(pos));
726726
}
727727

@@ -731,7 +731,7 @@ fn process_attribute<'input>(
731731
prefix,
732732
local,
733733
value,
734-
pos: attr_pos,
734+
range,
735735
});
736736
}
737737

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

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

907907
ctx.doc.attributes.push(AttributeData {
908908
name: attr_name,
909909
value: attr.value,
910910
#[cfg(feature = "positions")]
911-
pos: attr.pos,
911+
range: attr.range,
912912
});
913913
}
914914

src/tokenizer.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ pub enum Token<'input> {
160160
ElementStart(&'input str, &'input str, usize),
161161

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

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

@@ -561,7 +561,8 @@ fn parse_element<'input>(s: &mut Stream<'input>, events: &mut dyn XmlEvents<'inp
561561
s.skip_chars(|_, c| c != quote_c && c != '<')?;
562562
let value = s.slice_back_span(value_start);
563563
s.consume_byte(quote)?;
564-
events.token(Token::Attribute(start, prefix, local, value))?;
564+
let end = s.pos();
565+
events.token(Token::Attribute(start..end, prefix, local, value))?;
565566
}
566567
}
567568
}

tests/api.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ fn text_pos_01() {
156156
assert_eq!(doc.text_pos_at(node.range().end), TextPos::new(4, 5));
157157

158158
if let Some(attr) = node.attribute_node("a") {
159-
assert_eq!(doc.text_pos_at(attr.position()), TextPos::new(1, 4));
159+
assert_eq!(doc.text_pos_at(attr.range().start), TextPos::new(1, 4));
160+
assert_eq!(doc.text_pos_at(attr.range().end), TextPos::new(1, 9));
160161
}
161162

162163
// first child is a text/whitespace, not a comment
@@ -181,7 +182,8 @@ fn text_pos_02() {
181182
assert_eq!(doc.text_pos_at(node.range().start), TextPos::new(1, 1));
182183

183184
if let Some(attr) = node.attribute_node(("http://www.w3.org", "a")) {
184-
assert_eq!(doc.text_pos_at(attr.position()), TextPos::new(1, 36));
185+
assert_eq!(doc.text_pos_at(attr.range().start), TextPos::new(1, 36));
186+
assert_eq!(doc.text_pos_at(attr.range().end), TextPos::new(1, 44));
185187
}
186188
}
187189

0 commit comments

Comments
 (0)