rs-tml/rstml

Visitor API

vldm opened this issue · 0 comments

vldm commented

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 Nodes::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 
    }
}