newsletter-to-web/kuchiki/src/iter.rs

453 lines
14 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Node iterators
use std::borrow::Borrow;
use std::cell::RefCell;
use std::iter::Rev;
use crate::node_data_ref::NodeDataRef;
use crate::select::Selectors;
use crate::tree::{ElementData, NodeRef};
impl NodeRef {
/// Return an iterator of references to this node and its ancestors.
#[inline]
pub fn inclusive_ancestors(&self) -> Ancestors {
Ancestors(Some(self.clone()))
}
/// Return an iterator of references to this nodes ancestors.
#[inline]
pub fn ancestors(&self) -> Ancestors {
Ancestors(self.parent())
}
/// Return an iterator of references to this node and the siblings before it.
#[inline]
pub fn inclusive_preceding_siblings(&self) -> Rev<Siblings> {
match self.parent() {
Some(parent) => {
let first_sibling = parent.first_child().unwrap();
debug_assert!(self.previous_sibling().is_some() || *self == first_sibling);
Siblings(Some(State {
next: first_sibling,
next_back: self.clone(),
}))
}
None => {
debug_assert!(self.previous_sibling().is_none());
Siblings(Some(State {
next: self.clone(),
next_back: self.clone(),
}))
}
}
.rev()
}
/// Return an iterator of references to this nodes siblings before it.
#[inline]
pub fn preceding_siblings(&self) -> Rev<Siblings> {
match (self.parent(), self.previous_sibling()) {
(Some(parent), Some(previous_sibling)) => {
let first_sibling = parent.first_child().unwrap();
Siblings(Some(State {
next: first_sibling,
next_back: previous_sibling,
}))
}
_ => Siblings(None),
}
.rev()
}
/// Return an iterator of references to this node and the siblings after it.
#[inline]
pub fn inclusive_following_siblings(&self) -> Siblings {
match self.parent() {
Some(parent) => {
let last_sibling = parent.last_child().unwrap();
debug_assert!(self.next_sibling().is_some() || *self == last_sibling);
Siblings(Some(State {
next: self.clone(),
next_back: last_sibling,
}))
}
None => {
debug_assert!(self.next_sibling().is_none());
Siblings(Some(State {
next: self.clone(),
next_back: self.clone(),
}))
}
}
}
/// Return an iterator of references to this nodes siblings after it.
#[inline]
pub fn following_siblings(&self) -> Siblings {
match (self.parent(), self.next_sibling()) {
(Some(parent), Some(next_sibling)) => {
let last_sibling = parent.last_child().unwrap();
Siblings(Some(State {
next: next_sibling,
next_back: last_sibling,
}))
}
_ => Siblings(None),
}
}
/// Return an iterator of references to this nodes children.
#[inline]
pub fn children(&self) -> Siblings {
match (self.first_child(), self.last_child()) {
(Some(first_child), Some(last_child)) => Siblings(Some(State {
next: first_child,
next_back: last_child,
})),
(None, None) => Siblings(None),
_ => unreachable!(),
}
}
/// Return an iterator of references to this node and its descendants, in tree order.
///
/// Parent nodes appear before the descendants.
///
/// Note: this is the `NodeEdge::Start` items from `traverse()`.
#[inline]
pub fn inclusive_descendants(&self) -> Descendants {
Descendants(self.traverse_inclusive())
}
/// Return an iterator of references to this nodes descendants, in tree order.
///
/// Parent nodes appear before the descendants.
///
/// Note: this is the `NodeEdge::Start` items from `traverse()`.
#[inline]
pub fn descendants(&self) -> Descendants {
Descendants(self.traverse())
}
/// Return an iterator of the start and end edges of this node and its descendants,
/// in tree order.
#[inline]
pub fn traverse_inclusive(&self) -> Traverse {
Traverse(Some(State {
next: NodeEdge::Start(self.clone()),
next_back: NodeEdge::End(self.clone()),
}))
}
/// Return an iterator of the start and end edges of this nodes descendants,
/// in tree order.
#[inline]
pub fn traverse(&self) -> Traverse {
match (self.first_child(), self.last_child()) {
(Some(first_child), Some(last_child)) => Traverse(Some(State {
next: NodeEdge::Start(first_child),
next_back: NodeEdge::End(last_child),
})),
(None, None) => Traverse(None),
_ => unreachable!(),
}
}
/// Return an iterator of the inclusive descendants element that match the given selector list.
#[inline]
pub fn select(&self, selectors: &str) -> Result<Select<Elements<Descendants>>, ()> {
self.inclusive_descendants().select(selectors)
}
/// Return the first inclusive descendants element that match the given selector list.
#[inline]
pub fn select_first(&self, selectors: &str) -> Result<NodeDataRef<ElementData>, ()> {
let mut elements = self.select(selectors)?;
elements.next().ok_or(())
}
}
#[derive(Debug, Clone)]
struct State<T> {
next: T,
next_back: T,
}
/// A double-ended iterator of sibling nodes.
#[derive(Debug, Clone)]
pub struct Siblings(Option<State<NodeRef>>);
macro_rules! siblings_next {
($next: ident, $next_back: ident, $next_sibling: ident) => {
fn $next(&mut self) -> Option<NodeRef> {
#![allow(non_shorthand_field_patterns)]
self.0.take().map(|State { $next: next, $next_back: next_back }| {
if let Some(sibling) = next.$next_sibling() {
if next != next_back {
self.0 = Some(State { $next: sibling, $next_back: next_back })
}
}
next
})
}
}
}
impl Iterator for Siblings {
type Item = NodeRef;
siblings_next!(next, next_back, next_sibling);
}
impl DoubleEndedIterator for Siblings {
siblings_next!(next_back, next, previous_sibling);
}
/// An iterator on ancestor nodes.
#[derive(Debug, Clone)]
pub struct Ancestors(Option<NodeRef>);
impl Iterator for Ancestors {
type Item = NodeRef;
#[inline]
fn next(&mut self) -> Option<NodeRef> {
self.0.take().map(|node| {
self.0 = node.parent();
node
})
}
}
/// An iterator of references to a given node and its descendants, in tree order.
#[derive(Debug, Clone)]
pub struct Descendants(Traverse);
macro_rules! descendants_next {
($next: ident) => {
#[inline]
fn $next(&mut self) -> Option<NodeRef> {
loop {
match (self.0).$next() {
Some(NodeEdge::Start(node)) => return Some(node),
Some(NodeEdge::End(_)) => {}
None => return None
}
}
}
}
}
impl Iterator for Descendants {
type Item = NodeRef;
descendants_next!(next);
}
impl DoubleEndedIterator for Descendants {
descendants_next!(next_back);
}
/// Marks either the start or the end of a node.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NodeEdge<T> {
/// Indicates that start of a node that has children.
/// Yielded by `Traverse::next` before the nodes descendants.
/// In HTML or XML, this corresponds to an opening tag like `<div>`
Start(T),
/// Indicates that end of a node that has children.
/// Yielded by `Traverse::next` after the nodes descendants.
/// In HTML or XML, this corresponds to a closing tag like `</div>`
End(T),
}
/// An iterator of the start and end edges of the nodes in a given subtree.
#[derive(Debug, Clone)]
pub struct Traverse(Option<State<NodeEdge<NodeRef>>>);
macro_rules! traverse_next {
($next: ident, $next_back: ident, $first_child: ident, $next_sibling: ident, $Start: ident, $End: ident) => {
fn $next(&mut self) -> Option<NodeEdge<NodeRef>> {
#![allow(non_shorthand_field_patterns)]
self.0.take().map(|State { $next: next, $next_back: next_back }| {
if next != next_back {
self.0 = match next {
NodeEdge::$Start(ref node) => {
match node.$first_child() {
Some(child) => {
Some(State { $next: NodeEdge::$Start(child), $next_back: next_back })
}
None => Some(State { $next: NodeEdge::$End(node.clone()), $next_back: next_back })
}
}
NodeEdge::$End(ref node) => {
match node.$next_sibling() {
Some(sibling) => {
Some(State { $next: NodeEdge::$Start(sibling), $next_back: next_back })
}
None => node.parent().map(|parent| {
State { $next: NodeEdge::$End(parent), $next_back: next_back }
})
}
}
};
}
next
})
}
}
}
impl Iterator for Traverse {
type Item = NodeEdge<NodeRef>;
traverse_next!(next, next_back, first_child, next_sibling, Start, End);
}
impl DoubleEndedIterator for Traverse {
traverse_next!(next_back, next, last_child, previous_sibling, End, Start);
}
macro_rules! filter_map_like_iterator {
(#[$doc: meta] $name: ident: $f: expr, $from: ty => $to: ty) => {
#[$doc]
#[derive(Debug, Clone)]
pub struct $name<I>(pub I);
impl<I> Iterator for $name<I>
where
I: Iterator<Item = $from>,
{
type Item = $to;
#[inline]
fn next(&mut self) -> Option<$to> {
for x in self.0.by_ref() {
if let Some(y) = ($f)(x) {
return Some(y);
}
}
None
}
}
impl<I> DoubleEndedIterator for $name<I>
where
I: DoubleEndedIterator<Item = $from>,
{
#[inline]
fn next_back(&mut self) -> Option<$to> {
for x in self.0.by_ref().rev() {
if let Some(y) = ($f)(x) {
return Some(y);
}
}
None
}
}
};
}
filter_map_like_iterator! {
/// A node iterator adaptor that yields element nodes.
Elements: NodeRef::into_element_ref, NodeRef => NodeDataRef<ElementData>
}
filter_map_like_iterator! {
/// A node iterator adaptor that yields comment nodes.
Comments: NodeRef::into_comment_ref, NodeRef => NodeDataRef<RefCell<String>>
}
filter_map_like_iterator! {
/// A node iterator adaptor that yields text nodes.
TextNodes: NodeRef::into_text_ref, NodeRef => NodeDataRef<RefCell<String>>
}
/// An element iterator adaptor that yields elements maching given selectors.
pub struct Select<I, S = Selectors>
where
I: Iterator<Item = NodeDataRef<ElementData>>,
S: Borrow<Selectors>,
{
/// The underlying iterator.
pub iter: I,
/// The selectors to be matched.
pub selectors: S,
}
impl<I, S> Iterator for Select<I, S>
where
I: Iterator<Item = NodeDataRef<ElementData>>,
S: Borrow<Selectors>,
{
type Item = NodeDataRef<ElementData>;
#[inline]
fn next(&mut self) -> Option<NodeDataRef<ElementData>> {
for element in self.iter.by_ref() {
if self.selectors.borrow().matches(&element) {
return Some(element);
}
}
None
}
}
impl<I, S> DoubleEndedIterator for Select<I, S>
where
I: DoubleEndedIterator<Item = NodeDataRef<ElementData>>,
S: Borrow<Selectors>,
{
#[inline]
fn next_back(&mut self) -> Option<NodeDataRef<ElementData>> {
for element in self.iter.by_ref().rev() {
if self.selectors.borrow().matches(&element) {
return Some(element);
}
}
None
}
}
/// Convenience methods for node iterators.
pub trait NodeIterator: Sized + Iterator<Item = NodeRef> {
/// Filter this element iterator to elements.
#[inline]
fn elements(self) -> Elements<Self> {
Elements(self)
}
/// Filter this node iterator to text nodes.
#[inline]
fn text_nodes(self) -> TextNodes<Self> {
TextNodes(self)
}
/// Filter this node iterator to comment nodes.
#[inline]
fn comments(self) -> Comments<Self> {
Comments(self)
}
/// Filter this node iterator to elements maching the given selectors.
#[inline]
fn select(self, selectors: &str) -> Result<Select<Elements<Self>>, ()> {
self.elements().select(selectors)
}
}
/// Convenience methods for element iterators.
pub trait ElementIterator: Sized + Iterator<Item = NodeDataRef<ElementData>> {
/// Filter this element iterator to elements maching the given selectors.
#[inline]
fn select(self, selectors: &str) -> Result<Select<Self>, ()> {
Selectors::compile(selectors).map(|s| Select {
iter: self,
selectors: s,
})
}
}
impl<I> NodeIterator for I where I: Iterator<Item = NodeRef> {}
impl<I> ElementIterator for I where I: Iterator<Item = NodeDataRef<ElementData>> {}