#[cfg(test)]
mod tests;
use crate::options::ConversionOptions;
use crate::renderer::MarkdownRenderer;
use crate::utils;
use scraper::Html;
enum Event<'a> {
Enter(ego_tree::NodeRef<'a, scraper::Node>),
Leave(ego_tree::NodeRef<'a, scraper::Node>),
}
pub fn traverse(document: &Html, opts: &ConversionOptions) -> String {
let capacity = document.html().len() / 2;
let mut renderer = MarkdownRenderer::new(capacity.max(256));
let mut stack: Vec<Event> = Vec::with_capacity(64);
for child in document.tree.root().children().rev() {
stack.push(Event::Enter(child));
}
while let Some(event) = stack.pop() {
match event {
Event::Enter(node) => match node.value() {
scraper::Node::Element(elem) => {
let tag = elem.name();
if utils::is_skip_tag(tag) {
continue;
}
if opts.drop_interactive_shell && utils::is_shell_tag(tag) {
continue;
}
if opts.unwrap_unknown_wrappers
&& utils::is_wrapper_tag(tag)
&& !utils::is_structural_tag(tag)
{
for child in node.children().rev() {
stack.push(Event::Enter(child));
}
continue;
}
renderer.enter_element(elem);
stack.push(Event::Leave(node));
for child in node.children().rev() {
stack.push(Event::Enter(child));
}
}
scraper::Node::Text(text) => {
renderer.process_text(&text.text);
}
_ => {
for child in node.children().rev() {
stack.push(Event::Enter(child));
}
}
},
Event::Leave(node) => {
if let scraper::Node::Element(elem) = node.value() {
renderer.leave_element(elem);
}
}
}
}
renderer.finish()
}