|
|
|
@ -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()
|
|
|
|
|
}))
|
|
|
|
|
]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|