summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2023-04-09 11:07:21 +0300
committerLars Wirzenius <liw@liw.fi>2023-04-09 11:19:05 +0300
commitacf434dddb300e24350e2ee40c628a5d8283126f (patch)
treee79993089a29d39ce5e743e4a737755831be6c32
parentc55a41936938dce5adc22dece6b7ea02e14980ae (diff)
downloadhtml-page-acf434dddb300e24350e2ee40c628a5d8283126f.tar.gz
feat: add a visitor to an element and its children
Sponsored-by: author
-rw-r--r--src/lib.rs87
1 files changed, 86 insertions, 1 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 88d1e90..d6a7027 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -327,9 +327,64 @@ impl Display for Content {
}
}
+/// A read-only visitor for an HTML element.
+///
+/// Implementing this trait allows "visiting" element and all of its
+/// children. The provided [`Visitor::visit`] method visits the
+/// element first, and then each of its children in order, and
+/// recursively visits the children of each child.
+///
+/// ~~~
+/// # use html_rs::{Element, Tag, Visitor};
+/// #[derive(Default)]
+/// struct Collector {
+/// tags: Vec<Tag>,
+/// text: String,
+/// }
+///
+/// impl Visitor for Collector {
+/// fn visit_element(&mut self, e: &Element) {
+/// self.tags.push(e.tag());
+/// }
+///
+/// fn visit_text(&mut self, s: &str) {
+/// self.text.push_str(s);
+/// }
+/// }
+/// #
+/// # let mut e = Element::new(Tag::P);
+/// # e.push_text("hello ");
+/// # let mut world = Element::new(Tag::B);
+/// # world.push_text("world");
+/// # e.push_child(&world);
+/// #
+/// # let mut collector = Collector::default();
+/// # collector.visit(&e);
+/// # assert_eq!(collector.tags, vec![Tag::P, Tag::B]);
+/// # assert_eq!(collector.text, "hello world");
+/// ~~~
+pub trait Visitor {
+ /// Visit an element.
+ fn visit_element(&mut self, e: &Element);
+ /// Visit non-HTML text content.
+ fn visit_text(&mut self, s: &str);
+
+ /// Visit recursively an element and each of its children.
+ fn visit(&mut self, root: &Element) {
+ eprintln!("root: {:?}", root);
+ self.visit_element(root);
+ for child in &root.children {
+ match child {
+ Content::Text(s) => self.visit_text(s),
+ Content::Element(e) => self.visit(e),
+ }
+ }
+ }
+}
+
#[cfg(test)]
mod test {
- use super::{AttributeValue, Content, Element, Tag};
+ use super::{AttributeValue, Content, Element, Tag, Visitor};
#[test]
fn element_has_correct_tag() {
@@ -443,4 +498,34 @@ mod test {
e.push_text("hello <world>");
assert_eq!(e.serialize(), "<P>hello &lt;world&gt;</P>");
}
+
+ #[derive(Default)]
+ struct Collector {
+ tags: Vec<Tag>,
+ text: String,
+ }
+
+ impl Visitor for Collector {
+ fn visit_element(&mut self, e: &Element) {
+ self.tags.push(e.tag());
+ }
+
+ fn visit_text(&mut self, s: &str) {
+ self.text.push_str(s);
+ }
+ }
+
+ #[test]
+ fn visits_all_children() {
+ let mut e = Element::new(Tag::P);
+ e.push_text("hello ");
+ let mut world = Element::new(Tag::B);
+ world.push_text("world");
+ e.push_child(&world);
+
+ let mut collector = Collector::default();
+ collector.visit(&e);
+ assert_eq!(collector.tags, vec![Tag::P, Tag::B]);
+ assert_eq!(collector.text, "hello world");
+ }
}