Support sections with bodies
This commit is contained in:
parent
95a3cff278
commit
5a0b3abf0e
@ -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()
|
||||||
|
}))
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user