Support sections with bodies

This commit is contained in:
Tom Alexander 2020-04-04 23:42:27 -04:00
parent 95a3cff278
commit 5a0b3abf0e
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE

View File

@ -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<Block<'a>>,
}
#[derive(Clone, Debug, PartialEq)]
struct Block<'a> {
elements: Vec<TemplateElement<'a>>,
}
#[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()
}))
]
})
}
))
);
}
}