weft_derive 0.1.2

HTML templating with composability in mind
Documentation
use anyhow::Error;
use log::*;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Segment {
    Literal(String),
    Expr(syn::Expr),
}

#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub struct Substitutable {
    children: Vec<Segment>,
}

impl Substitutable {
    pub fn children(&self) -> impl Iterator<Item = Segment> + '_ {
        self.children.iter().cloned()
    }
}

pub fn parse_inline(input: &str) -> Result<Substitutable, Error> {
    let re = regex::Regex::new(r"\{\{([^}]|}[^}])*\}\}")?;
    trace!("Scanning: {:?}", input);
    let mut last_match = 0;
    let mut children = Vec::new();
    for it in re.find_iter(input) {
        trace!("Got: {:?}", it);

        let previous = &input[last_match..it.start()];
        if !previous.is_empty() {
            children.push(Segment::Literal(previous.into()));
        }

        let m = it.as_str();
        let range = 2..(m.len() - 2);
        let expr: syn::Expr = syn::parse_str(&m[range])?;

        children.push(Segment::Expr(expr));

        last_match = it.end();
    }
    let remainder = &input[last_match..];
    if !remainder.is_empty() {
        children.push(Segment::Literal(remainder.into()));
    }

    Ok(Substitutable { children })
}

#[cfg(test)]
mod tests {
    use super::*;
    use syn::parse_quote;

    #[test]
    fn test_trivial_expr() {
        let segments = parse_inline("{{ foo }}").expect("parse_inline");
        assert_eq!(&segments.children, &[Segment::Expr(parse_quote!(foo))])
    }

    #[test]
    fn test_trivial_literal() {
        let segments = parse_inline("Hi!").expect("parse_inline");
        let expected = vec![Segment::Literal("Hi!".into())];
        println!("Expected: {:?}", expected);
        println!("Got: {:?}", segments);
        assert_eq!(&segments.children, &expected)
    }

    #[test]
    fn test_mixed_1() {
        let segments = parse_inline("A {{ foo }}").expect("parse_inline");
        assert_eq!(
            &segments.children,
            &[
                Segment::Literal("A ".into()),
                Segment::Expr(parse_quote!(foo))
            ]
        )
    }

    #[test]
    fn test_mixed_2() {
        let segments = parse_inline("I {{ verb }} with {{ noun }}.").expect("parse_inline");
        assert_eq!(
            &segments.children,
            &[
                Segment::Literal("I ".into()),
                Segment::Expr(parse_quote!(verb)),
                Segment::Literal(" with ".into()),
                Segment::Expr(parse_quote!(noun)),
                Segment::Literal(".".into())
            ]
        )
    }
}