diff --git a/src/duster/parser.rs b/src/duster/parser.rs index ae8d214..123c33e 100644 --- a/src/duster/parser.rs +++ b/src/duster/parser.rs @@ -10,13 +10,14 @@ use nom::combinator::rest; use nom::combinator::value; use nom::combinator::verify; use nom::multi::many0; +use nom::multi::many1; use nom::multi::separated_list; use nom::sequence::delimited; use nom::sequence::preceded; use nom::sequence::tuple; use nom::IResult; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] enum DustTag<'a> { DTSpecial(Special), DTComment(Comment<'a>), @@ -64,15 +65,27 @@ struct Span<'a> { contents: &'a str, } -#[derive(Clone, Debug)] -pub struct Template<'a> { +#[derive(Clone, Debug, PartialEq)] +struct Section<'a> { + path: Path<'a>, + contents: Option>, +} + +#[derive(Clone, Debug, PartialEq)] +struct Block<'a> { elements: Vec>, } #[derive(Clone, Debug)] +pub struct Template<'a> { + contents: Block<'a>, +} + +#[derive(Clone, Debug, PartialEq)] enum TemplateElement<'a> { TESpan(Span<'a>), TETag(DustTag<'a>), + TESection(Section<'a>), } fn dust_tag(i: &str) -> IResult<&str, DustTag> { @@ -129,6 +142,22 @@ fn reference(i: &str) -> IResult<&str, Reference> { )) } +fn section(i: &str) -> IResult<&str, Section> { + let (i, opening_name) = delimited(tag("{#"), path, tag("}"))(i)?; + let (i, inner) = opt(block)(i)?; + let (i, _closing_name) = verify(delimited(tag("{/"), path, tag("}")), |name: &Path| { + name == &opening_name + })(i)?; + + Ok(( + i, + Section { + path: opening_name, + contents: inner, + }, + )) +} + fn filter(i: &str) -> IResult<&str, Filter> { preceded( tag("|"), @@ -150,19 +179,25 @@ fn span(i: &str) -> IResult<&str, Span> { Ok((remaining, Span { contents: body })) } -pub fn template(i: &str) -> IResult<&str, Template> { - let (remaining, template_elements) = many0(alt(( +fn block(i: &str) -> IResult<&str, Block> { + let (remaining, template_elements) = many1(alt(( map(span, TemplateElement::TESpan), map(dust_tag, TemplateElement::TETag), + map(section, TemplateElement::TESection), )))(i)?; Ok(( remaining, - Template { + Block { elements: template_elements, }, )) } +pub fn template(i: &str) -> IResult<&str, Template> { + let (remaining, contents) = block(i)?; + Ok((remaining, Template { contents: contents })) +} + #[cfg(test)] mod tests { use super::*; @@ -264,4 +299,57 @@ mod tests { Err(Error(("{~lb}", ErrorKind::Verify))) ); } + + #[test] + fn test_section_mismatched_paths() { + assert_eq!( + super::section("{#foo.bar}{/baz}"), + Err(Error(("{/baz}", ErrorKind::Verify))) + ); + } + + #[test] + fn test_empty_section() { + assert_eq!( + super::section("{#foo.bar}{/foo.bar}"), + Ok(( + "", + Section { + path: Path { + keys: vec!["foo", "bar"] + }, + contents: None + } + )) + ); + + assert_eq!( + super::section("{#foo.bar}{/baz}"), + Err(Error(("{/baz}", ErrorKind::Verify))) + ); + } + + #[test] + fn test_section_with_body() { + assert_eq!( + super::section("{#foo.bar}hello {name}{/foo.bar}"), + Ok(( + "", + Section { + path: Path { + keys: vec!["foo", "bar"] + }, + contents: Some(Block { + elements: vec![ + TemplateElement::TESpan(Span { contents: "hello " }), + TemplateElement::TETag(DustTag::DTReference(Reference { + path: Path { keys: vec!["name"] }, + filters: Vec::new() + })) + ] + }) + } + )) + ); + } }