#![deny(missing_docs)]
extern crate html5ever;
use html5ever::driver::ParseOpts;
use html5ever::parse_document;
use html5ever::rcdom::{Handle, NodeData, RcDom};
use html5ever::tendril::TendrilSink;
use html5ever::tree_builder::TreeBuilderOpts;
use std::io;
pub struct TagParser {
dom: RcDom,
}
impl TagParser {
pub fn new<A>(input: &mut A) -> Self
where
A: io::Read + Sized,
{
let opts = ParseOpts {
tree_builder: TreeBuilderOpts {
drop_doctype: true,
scripting_enabled: false,
..Default::default()
},
..Default::default()
};
let dom = parse_document(RcDom::default(), opts)
.from_utf8()
.read_from(input)
.unwrap();
println!("Errors: {:?}", dom.errors);
TagParser { dom }
}
fn internal_walk<F>(handle: &Handle, callback: &F) -> String
where
F: Fn(&mut Tag),
{
let mut output = String::new();
if let NodeData::Element { name, attrs, .. } = &handle.data {
let name = &name.local;
let attrs = attrs.borrow();
let mut attributes = Vec::<(&str, &str)>::new();
for attr in attrs.iter() {
attributes.push((
&attr.name.local,
&attr.value
));
}
let mut tag = Tag::from_name_and_attrs(name, &attributes);
callback(&mut tag);
if tag.ignore_self && tag.ignore_contents {
return output;
}
if let Some(rewrite) = tag.rewrite {
return rewrite;
}
if !tag.ignore_self {
output += "<";
output += name;
for attr in tag.attrs.iter() {
if tag.allowed_attributes.iter().any(|a| a == attr.0) {
output += " ";
output += attr.0;
output += "=\"";
output += attr.1;
output += "\"";
}
}
output += ">";
}
if !tag.ignore_contents {
for child in handle.children.borrow().iter() {
output += &TagParser::internal_walk(child, callback);
}
}
if !tag.ignore_self {
output += "</";
output += name;
output += ">";
}
} else {
match &handle.data {
NodeData::Document => {}
NodeData::Doctype { .. } => {}
NodeData::Text { contents } => output += (&contents.borrow()).trim(),
NodeData::Comment { .. } => {},
NodeData::Element { .. } => unreachable!(),
NodeData::ProcessingInstruction { target, contents } => println!(
"Unknown enum tag: NodeData::ProcessingInstruction {{ {:?} {:?} }}",
target, contents
),
}
for child in handle.children.borrow().iter() {
output += &TagParser::internal_walk(child, callback);
}
}
output
}
pub fn walk<F>(&mut self, callback: F) -> String
where
F: Fn(&mut Tag),
{
TagParser::internal_walk(&self.dom.document, &callback)
}
}
pub struct Tag<'a> {
pub name: &'a str,
pub attrs: &'a [(&'a str, &'a str)],
rewrite: Option<String>,
allowed_attributes: Vec<String>,
ignore_self: bool,
ignore_contents: bool,
}
impl<'a> Tag<'a> {
fn from_name_and_attrs(name: &'a str, attrs: &'a [(&'a str, &'a str)]) -> Tag<'a> {
Tag {
name,
attrs,
rewrite: None,
allowed_attributes: Vec::new(),
ignore_self: false,
ignore_contents: false,
}
}
pub fn allow_attribute(&mut self, attr: String) {
self.allowed_attributes.push(attr);
}
pub fn allow_attributes(&mut self, attr: &[String]) {
self.allowed_attributes.reserve(attr.len());
for attr in attr {
self.allowed_attributes.push(attr.clone());
}
}
pub fn ignore_self_and_contents(&mut self){
self.ignore_self = true;
self.ignore_contents = true;
}
pub fn ignore_self(&mut self){
self.ignore_self = true;
}
pub fn rewrite_as(&mut self, new_contents: String) {
self.rewrite = Some(new_contents);
}
}