Visitor API
vldm opened this issue · 0 comments
Currently rstml already has more than ten items. Some of them is reused more than once like NodeName
, or Block
.
And currently, in order to check if NodeName
has correct spelling, or in order to check if NodeElement
name is blacklisted
one should write a recursion over Node
s::children
.
To avoid repetition, and make code cleaner, i propose to add visitor/inspector
module.
Which is a just a custom trait with bunch of methods like:
visit_node_name
, visit_element
, visit_node_text
, visit_attribute
- and routine that make a recursion over it, in order to provide
The main usage for this, is to validate some of the user inputs, so i thing signature could be the following:
fn(&mut self, &mut RecoverableContext, ty: &Ty)
Example:
trait Visitor {
fn visit_node_name(&mut self, ctx: &mut RecoverableContext, ty: &NodeName) {
rstml::visitor::visit_node_name_propagate(self, ctx, ty)
}
fn visit_element(&mut self, ctx: &mut RecoverableContext, ty: &NodeElement) {
rstml::visitor::visit_element_propagate(self, ctx, ty)
}
}
struct WhiteListVisitor {
whitelist: HashSet<&'static str>,
failed: bool,
}
impl WhiteListVisitor {
fn head() -> Self {
Self {
whitelist: vec![
"title", "base", "link", "style", "meta", "script", "noscript", "template",
]
.into_iter()
.collect(),
}
}
}
impl Visitor for WhiteListVisitor {
fn visit_element(&mut self, ctx: &mut RecoverableContext, node: &NodeElement) {
if self.contains(self.node.name())
{
self.failed = true;
return;
}
}
}
// Check that head inside html, and no alien tags are present
struct HeadVerifier;
impl Visitor for HeadVerifier {
fn visit_element(&mut self, ctx: &mut RecoverableContext, node: &NodeElement) {
match &self.node.name().to_string() {
"head" => {
ctx.push_error(Error::new(self.node.span(), "Element outside of html"))
return;
}
"html" => {}
// this will handle elements attribute, childs and other fields, and propagate it to other field visitors
_ => return rstml::visitor::visit_element_propagate(&mut self, ctx, node)
}
// we are in html element
let mut head_exist = false;
for c in self.node.children {
if let Node::Element(e) = c {
if e.name() == "head" {
let visitor = WhiteListVisitor::head();
rstml::visitor::visit_element_propagate(&mut self, ctx, e);
continue;
}
}
rstml::visitor::visit_node_propagate(&mut self, ctx, c)
}
// attribute handling can be skipped in this kind of visitor, so no reason to propagate it
}
}