Skip to content

Commit

Permalink
Merge pull request tafia#705 from Tpt/prefixes
Browse files Browse the repository at this point in the history
Adds NsReader::prefixes to iterate on all the prefixes currently declared
  • Loading branch information
Mingun committed Jan 21, 2024
2 parents 83fc2d6 + adf873e commit 88aa477
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ to get an offset of the error position. For `SyntaxError`s the range
- [#362]: Added `escape::minimal_escape()` which escapes only `&` and `<`.
- [#362]: Added `BytesCData::minimal_escape()` which escapes only `&` and `<`.
- [#362]: Added `Serializer::set_quote_level()` which allow to set desired level of escaping.
- [#705]: Added `NsReader::prefixes()` to list all the prefixes currently declared.

### Bug Fixes

Expand Down Expand Up @@ -68,6 +69,7 @@ to get an offset of the error position. For `SyntaxError`s the range
[#684]: https://github.com/tafia/quick-xml/pull/684
[#689]: https://github.com/tafia/quick-xml/pull/689
[#704]: https://github.com/tafia/quick-xml/pull/704
[#705]: https://github.com/tafia/quick-xml/pull/705


## 0.31.0 -- 2023-10-22
Expand Down
59 changes: 59 additions & 0 deletions src/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,65 @@ impl NamespaceResolver {
None => ResolveResult::Unbound,
}
}

#[inline]
pub fn iter(&self) -> PrefixIter {
PrefixIter {
resolver: self,
// We initialize the cursor to 2 to skip the two default namespaces xml: and xmlns:
bindings_cursor: 2,
}
}
}

////////////////////////////////////////////////////////////////////////////////////////////////////

/// Iterator on the current declared prefixes.
///
/// See [`NsReader::prefixes`](crate::NsReader::prefixes) for documentation.
#[derive(Debug, Clone)]
pub struct PrefixIter<'a> {
resolver: &'a NamespaceResolver,
bindings_cursor: usize,
}

impl<'a> Iterator for PrefixIter<'a> {
type Item = (PrefixDeclaration<'a>, Namespace<'a>);

fn next(&mut self) -> Option<(PrefixDeclaration<'a>, Namespace<'a>)> {
while let Some(namespace_entry) = self.resolver.bindings.get(self.bindings_cursor) {
self.bindings_cursor += 1; // We increment for next read

// We check if the key has not been overridden by having a look
// at the namespaces declared after in the array
let prefix = namespace_entry.prefix(&self.resolver.buffer);
if self.resolver.bindings[self.bindings_cursor..]
.iter()
.any(|ne| prefix == ne.prefix(&self.resolver.buffer))
{
continue; // Overridden
}
let namespace = if let ResolveResult::Bound(namespace) =
namespace_entry.namespace(&self.resolver.buffer)
{
namespace
} else {
continue; // We don't return unbound namespaces
};
let prefix = if let Some(Prefix(prefix)) = prefix {
PrefixDeclaration::Named(prefix)
} else {
PrefixDeclaration::Default
};
return Some((prefix, namespace));
}
None // We have exhausted the array
}

fn size_hint(&self) -> (usize, Option<usize>) {
// Real count could be less if some namespaces was overridden
(0, Some(self.resolver.bindings.len() - self.bindings_cursor))
}
}

#[cfg(test)]
Expand Down
86 changes: 85 additions & 1 deletion src/reader/ns_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::path::Path;

use crate::errors::Result;
use crate::events::Event;
use crate::name::{LocalName, NamespaceResolver, QName, ResolveResult};
use crate::name::{LocalName, NamespaceResolver, PrefixIter, QName, ResolveResult};
use crate::reader::{Config, Reader, Span, XmlSource};

/// A low level encoding-agnostic XML event reader that performs namespace resolution.
Expand Down Expand Up @@ -48,6 +48,90 @@ impl<R> NsReader<R> {
pub fn config_mut(&mut self) -> &mut Config {
self.reader.config_mut()
}

/// Returns all the prefixes currently declared except the default `xml` and `xmlns` namespaces.
///
/// # Examples
///
/// This example shows what results the returned iterator would return after
/// reading each event of a simple XML.
///
/// ```
/// # use pretty_assertions::assert_eq;
/// use quick_xml::name::{Namespace, PrefixDeclaration};
/// use quick_xml::NsReader;
///
/// let src = "<root>
/// <a xmlns=\"a1\" xmlns:a=\"a2\">
/// <b xmlns=\"b1\" xmlns:b=\"b2\">
/// <c/>
/// </b>
/// <d/>
/// </a>
/// </root>";
/// let mut reader = NsReader::from_str(src);
/// reader.config_mut().trim_text(true);
/// // No prefixes at the beginning
/// assert_eq!(reader.prefixes().collect::<Vec<_>>(), vec![]);
///
/// reader.read_resolved_event()?; // <root>
/// // No prefixes declared on root
/// assert_eq!(reader.prefixes().collect::<Vec<_>>(), vec![]);
///
/// reader.read_resolved_event()?; // <a>
/// // Two prefixes declared on "a"
/// assert_eq!(reader.prefixes().collect::<Vec<_>>(), vec![
/// (PrefixDeclaration::Default, Namespace(b"a1")),
/// (PrefixDeclaration::Named(b"a"), Namespace(b"a2"))
/// ]);
///
/// reader.read_resolved_event()?; // <b>
/// // The default prefix got overridden and new "b" prefix
/// assert_eq!(reader.prefixes().collect::<Vec<_>>(), vec![
/// (PrefixDeclaration::Named(b"a"), Namespace(b"a2")),
/// (PrefixDeclaration::Default, Namespace(b"b1")),
/// (PrefixDeclaration::Named(b"b"), Namespace(b"b2"))
/// ]);
///
/// reader.read_resolved_event()?; // <c/>
/// // Still the same
/// assert_eq!(reader.prefixes().collect::<Vec<_>>(), vec![
/// (PrefixDeclaration::Named(b"a"), Namespace(b"a2")),
/// (PrefixDeclaration::Default, Namespace(b"b1")),
/// (PrefixDeclaration::Named(b"b"), Namespace(b"b2"))
/// ]);
///
/// reader.read_resolved_event()?; // </b>
/// // Still the same
/// assert_eq!(reader.prefixes().collect::<Vec<_>>(), vec![
/// (PrefixDeclaration::Named(b"a"), Namespace(b"a2")),
/// (PrefixDeclaration::Default, Namespace(b"b1")),
/// (PrefixDeclaration::Named(b"b"), Namespace(b"b2"))
/// ]);
///
/// reader.read_resolved_event()?; // <d/>
/// // </b> got closed so back to the prefixes declared on <a>
/// assert_eq!(reader.prefixes().collect::<Vec<_>>(), vec![
/// (PrefixDeclaration::Default, Namespace(b"a1")),
/// (PrefixDeclaration::Named(b"a"), Namespace(b"a2"))
/// ]);
///
/// reader.read_resolved_event()?; // </a>
/// // Still the same
/// assert_eq!(reader.prefixes().collect::<Vec<_>>(), vec![
/// (PrefixDeclaration::Default, Namespace(b"a1")),
/// (PrefixDeclaration::Named(b"a"), Namespace(b"a2"))
/// ]);
///
/// reader.read_resolved_event()?; // </root>
/// // <a> got closed
/// assert_eq!(reader.prefixes().collect::<Vec<_>>(), vec![]);
/// # quick_xml::Result::Ok(())
/// ```
#[inline]
pub fn prefixes(&self) -> PrefixIter {
self.ns_resolver.iter()
}
}

/// Private methods
Expand Down

0 comments on commit 88aa477

Please sign in to comment.