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::value;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many0; use nom::multi::many0;
use nom::multi::many1;
use nom::multi::separated_list; use nom::multi::separated_list;
use nom::sequence::delimited; use nom::sequence::delimited;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use nom::IResult; use nom::IResult;
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
enum DustTag<'a> { enum DustTag<'a> {
DTSpecial(Special), DTSpecial(Special),
DTComment(Comment<'a>), DTComment(Comment<'a>),
@ -64,15 +65,27 @@ struct Span<'a> {
contents: &'a str, contents: &'a str,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct Template<'a> { struct Section<'a> {
path: Path<'a>,
contents: Option<Block<'a>>,
}
#[derive(Clone, Debug, PartialEq)]
struct Block<'a> {
elements: Vec<TemplateElement<'a>>, elements: Vec<TemplateElement<'a>>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Template<'a> {
contents: Block<'a>,
}
#[derive(Clone, Debug, PartialEq)]
enum TemplateElement<'a> { enum TemplateElement<'a> {
TESpan(Span<'a>), TESpan(Span<'a>),
TETag(DustTag<'a>), TETag(DustTag<'a>),
TESection(Section<'a>),
} }
fn dust_tag(i: &str) -> IResult<&str, DustTag> { 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> { fn filter(i: &str) -> IResult<&str, Filter> {
preceded( preceded(
tag("|"), tag("|"),
@ -150,19 +179,25 @@ fn span(i: &str) -> IResult<&str, Span> {
Ok((remaining, Span { contents: body })) Ok((remaining, Span { contents: body }))
} }
pub fn template(i: &str) -> IResult<&str, Template> { fn block(i: &str) -> IResult<&str, Block> {
let (remaining, template_elements) = many0(alt(( let (remaining, template_elements) = many1(alt((
map(span, TemplateElement::TESpan), map(span, TemplateElement::TESpan),
map(dust_tag, TemplateElement::TETag), map(dust_tag, TemplateElement::TETag),
map(section, TemplateElement::TESection),
)))(i)?; )))(i)?;
Ok(( Ok((
remaining, remaining,
Template { Block {
elements: template_elements, elements: template_elements,
}, },
)) ))
} }
pub fn template(i: &str) -> IResult<&str, Template> {
let (remaining, contents) = block(i)?;
Ok((remaining, Template { contents: contents }))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -264,4 +299,57 @@ mod tests {
Err(Error(("{~lb}", ErrorKind::Verify))) 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()
}))
]
})
}
))
);
}
} }