From 41abcb9b4a41df8a342ff9a581b69a3de5224ca1 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 5 Apr 2020 23:47:55 -0400 Subject: [PATCH] Add support for named blocks --- src/parser/parser.rs | 142 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 48bab92..69109f2 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -25,6 +25,8 @@ enum DustTag<'a> { DTSection(Container<'a>), DTExists(Container<'a>), DTNotExists(Container<'a>), + DTBlock(NamedBlock<'a>), + DTInlinePartial(NamedBlock<'a>), } #[derive(Clone, Debug, PartialEq)] @@ -75,6 +77,12 @@ struct Container<'a> { else_contents: Option>, } +#[derive(Clone, Debug, PartialEq)] +struct NamedBlock<'a> { + name: &'a str, + contents: Option>, +} + #[derive(Clone, Debug, PartialEq)] struct Body<'a> { elements: Vec>, @@ -102,6 +110,8 @@ fn dust_tag(i: &str) -> IResult<&str, DustTag> { conditional("{#", DustTag::DTSection), conditional("{?", DustTag::DTExists), conditional("{^", DustTag::DTNotExists), + named_block("{+", DustTag::DTBlock), + named_block("{<", DustTag::DTInlinePartial), ))(i) } @@ -217,6 +227,66 @@ where } } +fn named_block<'a, F>( + open_matcher: &'static str, + constructor: F, +) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>> +where + F: Copy + Fn(NamedBlock<'a>) -> DustTag<'a>, +{ + alt(( + named_block_with_body(open_matcher, constructor), + self_closing_named_block(open_matcher, constructor), + )) +} + +fn named_block_with_body<'a, F>( + open_matcher: &'static str, + constructor: F, +) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>> +where + F: Fn(NamedBlock<'a>) -> DustTag<'a>, +{ + move |i: &'a str| { + let (i, (opening_name, inner, _closing_name)) = verify( + tuple(( + delimited(tag(open_matcher), key, tag("}")), + opt(body), + delimited(tag("{/"), key, tag("}")), + )), + |(open, _inn, close)| open == close, + )(i)?; + + Ok(( + i, + constructor(NamedBlock { + name: opening_name, + contents: inner, + }), + )) + } +} + +fn self_closing_named_block<'a, F>( + open_matcher: &'static str, + constructor: F, +) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>> +where + F: Fn(NamedBlock<'a>) -> DustTag<'a>, +{ + move |i: &'a str| { + let (i, name) = delimited(tag(open_matcher), key, tag("/}"))(i)?; + + Ok(( + i, + constructor(NamedBlock { + name: name, + contents: None, + }), + )) + } +} + fn filter(i: &str) -> IResult<&str, Filter> { preceded( tag("|"), @@ -459,4 +529,76 @@ mod tests { )) ); } + + #[test] + fn test_self_closing_block() { + assert_eq!( + super::dust_tag("{+foo/}"), + Ok(( + "", + DustTag::DTBlock(NamedBlock { + name: "foo", + contents: None + }) + )) + ); + } + + #[test] + fn test_block() { + assert_eq!( + super::dust_tag("{+foo}hello {name}{/foo}"), + Ok(( + "", + DustTag::DTBlock(NamedBlock { + name: "foo", + contents: Some(Body { + elements: vec![ + TemplateElement::TESpan(Span { contents: "hello " }), + TemplateElement::TETag(DustTag::DTReference(Reference { + path: Path { keys: vec!["name"] }, + filters: Vec::new() + })) + ] + }) + }) + )) + ); + } + + #[test] + fn test_self_closing_inline_partial() { + assert_eq!( + super::dust_tag("{