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("{