2020-04-05 00:16:44 +00:00
|
|
|
use nom::branch::alt;
|
2020-04-07 01:09:06 +00:00
|
|
|
use nom::bytes::complete::escaped_transform;
|
2020-04-05 00:37:35 +00:00
|
|
|
use nom::bytes::complete::is_a;
|
2020-04-07 00:20:53 +00:00
|
|
|
use nom::bytes::complete::is_not;
|
2020-06-07 17:25:27 +00:00
|
|
|
use nom::bytes::complete::{tag, take_until, take_until_parser_matches};
|
2020-04-12 20:02:26 +00:00
|
|
|
use nom::character::complete::line_ending;
|
|
|
|
use nom::character::complete::multispace0;
|
2020-04-05 02:45:56 +00:00
|
|
|
use nom::character::complete::one_of;
|
2020-05-11 03:01:14 +00:00
|
|
|
use nom::character::complete::{digit1, space0, space1};
|
2020-04-12 20:02:26 +00:00
|
|
|
use nom::combinator::all_consuming;
|
2020-04-05 00:16:44 +00:00
|
|
|
use nom::combinator::map;
|
2020-04-05 02:45:56 +00:00
|
|
|
use nom::combinator::opt;
|
2020-04-05 00:37:35 +00:00
|
|
|
use nom::combinator::recognize;
|
2020-04-05 00:16:44 +00:00
|
|
|
use nom::combinator::value;
|
2020-04-05 02:45:56 +00:00
|
|
|
use nom::combinator::verify;
|
2020-04-05 00:51:10 +00:00
|
|
|
use nom::multi::many0;
|
2020-04-05 03:42:27 +00:00
|
|
|
use nom::multi::many1;
|
2020-04-12 21:39:24 +00:00
|
|
|
use nom::multi::separated_list1;
|
2020-04-05 00:16:44 +00:00
|
|
|
use nom::sequence::delimited;
|
2020-04-05 00:51:10 +00:00
|
|
|
use nom::sequence::preceded;
|
2020-04-07 03:21:03 +00:00
|
|
|
use nom::sequence::separated_pair;
|
2020-04-08 01:49:28 +00:00
|
|
|
use nom::sequence::terminated;
|
2020-04-05 00:37:35 +00:00
|
|
|
use nom::sequence::tuple;
|
2020-04-05 00:16:44 +00:00
|
|
|
use nom::IResult;
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-04-11 22:25:48 +00:00
|
|
|
pub enum DustTag<'a> {
|
2020-04-05 00:16:44 +00:00
|
|
|
DTSpecial(Special),
|
|
|
|
DTComment(Comment<'a>),
|
2020-05-24 03:41:05 +00:00
|
|
|
DTLiteralStringBlock(&'a str),
|
2020-04-05 00:37:35 +00:00
|
|
|
DTReference(Reference<'a>),
|
2020-05-26 01:26:16 +00:00
|
|
|
DTSection(ParameterizedBlock<'a>),
|
|
|
|
DTExists(ParameterizedBlock<'a>),
|
|
|
|
DTNotExists(ParameterizedBlock<'a>),
|
2020-05-30 18:39:31 +00:00
|
|
|
DTBlock(ParameterizedBlock<'a>),
|
|
|
|
DTInlinePartial(ParameterizedBlock<'a>),
|
2020-05-18 01:17:34 +00:00
|
|
|
DTPartial(Partial<'a>),
|
2020-04-08 01:49:28 +00:00
|
|
|
DTHelperEquals(ParameterizedBlock<'a>),
|
|
|
|
DTHelperNotEquals(ParameterizedBlock<'a>),
|
|
|
|
DTHelperGreaterThan(ParameterizedBlock<'a>),
|
|
|
|
DTHelperLessThan(ParameterizedBlock<'a>),
|
2020-05-16 23:05:03 +00:00
|
|
|
DTHelperGreaterThanOrEquals(ParameterizedBlock<'a>),
|
|
|
|
DTHelperLessThanOrEquals(ParameterizedBlock<'a>),
|
2020-06-07 18:45:32 +00:00
|
|
|
DTHelperSep(ParameterizedBlock<'a>),
|
|
|
|
DTHelperFirst(ParameterizedBlock<'a>),
|
|
|
|
DTHelperLast(ParameterizedBlock<'a>),
|
2020-06-07 21:06:14 +00:00
|
|
|
DTHelperSelect(ParameterizedBlock<'a>),
|
|
|
|
DTHelperAny(ParameterizedBlock<'a>),
|
|
|
|
DTHelperNone(ParameterizedBlock<'a>),
|
2020-06-13 16:03:55 +00:00
|
|
|
DTHelperMath(ParameterizedBlock<'a>),
|
2020-06-14 16:32:10 +00:00
|
|
|
DTHelperSize(ParameterizedBlock<'a>),
|
|
|
|
DTHelperContextDump(ParameterizedBlock<'a>),
|
2020-04-05 00:16:44 +00:00
|
|
|
}
|
|
|
|
|
2020-04-05 02:45:56 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
2020-04-13 01:54:15 +00:00
|
|
|
pub enum Special {
|
2020-04-05 00:16:44 +00:00
|
|
|
Space,
|
|
|
|
NewLine,
|
|
|
|
CarriageReturn,
|
|
|
|
LeftCurlyBrace,
|
|
|
|
RightCurlyBrace,
|
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-05-03 17:43:49 +00:00
|
|
|
pub enum IgnoredWhitespace<'a> {
|
|
|
|
StartOfLine(&'a str),
|
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-04-11 22:40:36 +00:00
|
|
|
pub struct Comment<'a> {
|
2020-04-05 00:16:44 +00:00
|
|
|
value: &'a str,
|
|
|
|
}
|
|
|
|
|
2020-04-28 23:02:43 +00:00
|
|
|
/// A series of keys separated by '.' to reference a variable in the context
|
|
|
|
///
|
|
|
|
/// Special case: If the path is just "." then keys will be an empty vec
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-04-12 02:52:20 +00:00
|
|
|
pub struct Path<'a> {
|
|
|
|
pub keys: Vec<&'a str>,
|
2020-04-05 00:37:35 +00:00
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-04-11 22:40:36 +00:00
|
|
|
pub struct Reference<'a> {
|
2020-04-12 02:52:20 +00:00
|
|
|
pub path: Path<'a>,
|
2020-04-13 01:54:15 +00:00
|
|
|
pub filters: Vec<Filter>,
|
2020-04-05 00:51:10 +00:00
|
|
|
}
|
|
|
|
|
2020-04-05 02:45:56 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
2020-04-13 01:54:15 +00:00
|
|
|
pub enum Filter {
|
2020-04-05 00:51:10 +00:00
|
|
|
HtmlEncode,
|
|
|
|
DisableHtmlEncode,
|
|
|
|
JavascriptStringEncode,
|
|
|
|
EncodeUri,
|
|
|
|
EncodeUriComponent,
|
|
|
|
JsonStringify,
|
|
|
|
JsonParse,
|
2020-04-05 00:37:35 +00:00
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-04-11 22:25:48 +00:00
|
|
|
pub struct Span<'a> {
|
2020-05-03 18:44:09 +00:00
|
|
|
pub contents: &'a str,
|
2020-05-03 16:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-04-13 01:54:15 +00:00
|
|
|
pub struct ParameterizedBlock<'a> {
|
2020-05-26 01:17:58 +00:00
|
|
|
pub path: Path<'a>,
|
2020-05-25 18:14:17 +00:00
|
|
|
pub explicit_context: Option<Path<'a>>,
|
2020-05-10 18:22:59 +00:00
|
|
|
pub params: Vec<KVPair<'a>>,
|
|
|
|
pub contents: Option<Body<'a>>,
|
|
|
|
pub else_contents: Option<Body<'a>>,
|
2020-04-08 00:37:15 +00:00
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-05-18 01:17:34 +00:00
|
|
|
pub struct Partial<'a> {
|
2020-05-18 00:44:17 +00:00
|
|
|
pub name: Vec<PartialNameElement>,
|
2020-05-25 18:05:46 +00:00
|
|
|
pub explicit_context: Option<Path<'a>>,
|
2020-05-18 00:44:17 +00:00
|
|
|
pub params: Vec<KVPair<'a>>,
|
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-05-17 02:30:04 +00:00
|
|
|
pub enum OwnedLiteral {
|
2020-05-30 20:34:32 +00:00
|
|
|
LString(String),
|
2020-05-17 02:30:04 +00:00
|
|
|
LPositiveInteger(u64),
|
2020-06-13 20:06:31 +00:00
|
|
|
LNegativeInteger(i64),
|
2020-06-13 17:37:26 +00:00
|
|
|
LFloat(f64),
|
2020-05-17 02:30:04 +00:00
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-05-09 00:58:32 +00:00
|
|
|
pub enum RValue<'a> {
|
2020-04-07 03:47:50 +00:00
|
|
|
RVPath(Path<'a>),
|
2020-05-30 19:49:13 +00:00
|
|
|
RVTemplate(Vec<PartialNameElement>),
|
2020-05-17 02:30:04 +00:00
|
|
|
RVLiteral(OwnedLiteral),
|
2020-04-07 03:47:50 +00:00
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-05-09 02:12:35 +00:00
|
|
|
pub struct KVPair<'a> {
|
|
|
|
pub key: &'a str,
|
|
|
|
pub value: RValue<'a>,
|
2020-04-07 03:47:50 +00:00
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-05-18 00:44:17 +00:00
|
|
|
pub enum PartialNameElement {
|
|
|
|
PNSpan {
|
|
|
|
contents: String,
|
|
|
|
},
|
|
|
|
PNReference {
|
|
|
|
path: Vec<String>,
|
|
|
|
filters: Vec<Filter>,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-04-11 22:25:48 +00:00
|
|
|
pub struct Body<'a> {
|
|
|
|
pub elements: Vec<TemplateElement<'a>>,
|
2020-04-05 01:30:56 +00:00
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-04-05 03:42:27 +00:00
|
|
|
pub struct Template<'a> {
|
2020-04-11 22:25:48 +00:00
|
|
|
pub contents: Body<'a>,
|
2020-04-05 03:42:27 +00:00
|
|
|
}
|
|
|
|
|
2020-05-31 23:01:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2020-04-11 22:25:48 +00:00
|
|
|
pub enum TemplateElement<'a> {
|
2020-04-05 01:30:56 +00:00
|
|
|
TESpan(Span<'a>),
|
|
|
|
TETag(DustTag<'a>),
|
2020-05-03 17:43:49 +00:00
|
|
|
TEIgnoredWhitespace(IgnoredWhitespace<'a>),
|
2020-04-05 01:30:56 +00:00
|
|
|
}
|
|
|
|
|
2020-05-18 00:44:17 +00:00
|
|
|
impl From<TemplateElement<'_>> for PartialNameElement {
|
|
|
|
fn from(original: TemplateElement) -> Self {
|
|
|
|
match original {
|
|
|
|
TemplateElement::TESpan(span) => PartialNameElement::PNSpan {
|
|
|
|
contents: span.contents.to_owned(),
|
|
|
|
},
|
|
|
|
TemplateElement::TETag(DustTag::DTReference(reference)) => {
|
|
|
|
PartialNameElement::PNReference {
|
|
|
|
path: reference
|
|
|
|
.path
|
|
|
|
.keys
|
|
|
|
.into_iter()
|
|
|
|
.map(|s| s.to_owned())
|
|
|
|
.collect(),
|
|
|
|
filters: reference.filters,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => panic!("Only spans and references can be used in partial names."),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-18 01:11:55 +00:00
|
|
|
impl<'a> From<&'a PartialNameElement> for TemplateElement<'a> {
|
|
|
|
fn from(original: &'a PartialNameElement) -> Self {
|
|
|
|
match original {
|
|
|
|
PartialNameElement::PNSpan { contents } => {
|
|
|
|
TemplateElement::TESpan(Span { contents: contents })
|
|
|
|
}
|
|
|
|
PartialNameElement::PNReference { path, filters } => {
|
|
|
|
TemplateElement::TETag(DustTag::DTReference(Reference {
|
|
|
|
path: Path {
|
|
|
|
keys: path.into_iter().map(|s| s.as_str()).collect(),
|
|
|
|
},
|
|
|
|
filters: filters.into_iter().map(|f| f.clone()).collect(),
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-05 23:34:46 +00:00
|
|
|
/// Any element significant to dust that isn't plain text
|
|
|
|
///
|
|
|
|
/// These elements are always wrapped in curly braces
|
2020-04-05 00:16:44 +00:00
|
|
|
fn dust_tag(i: &str) -> IResult<&str, DustTag> {
|
2020-05-03 18:44:09 +00:00
|
|
|
alt((
|
|
|
|
map(special, DustTag::DTSpecial),
|
|
|
|
map(comment, DustTag::DTComment),
|
2020-05-24 03:41:05 +00:00
|
|
|
map(literal_string_block, DustTag::DTLiteralStringBlock),
|
2020-05-03 18:44:09 +00:00
|
|
|
map(reference, DustTag::DTReference),
|
2020-05-26 01:26:16 +00:00
|
|
|
map(parameterized_block("{#", path), DustTag::DTSection),
|
|
|
|
map(parameterized_block("{?", path), DustTag::DTExists),
|
|
|
|
map(parameterized_block("{^", path), DustTag::DTNotExists),
|
2020-05-30 18:39:31 +00:00
|
|
|
map(
|
|
|
|
parameterized_block_without_else("{+", key_to_path),
|
|
|
|
DustTag::DTBlock,
|
|
|
|
),
|
|
|
|
map(
|
|
|
|
parameterized_block_without_else("{<", key_to_path),
|
|
|
|
DustTag::DTInlinePartial,
|
|
|
|
),
|
2020-05-18 01:17:34 +00:00
|
|
|
partial("{>", DustTag::DTPartial),
|
2020-06-07 21:06:14 +00:00
|
|
|
dust_tag_helper,
|
|
|
|
))(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Nom's alt() is limited to 21 possibilities, so I pushed this out
|
|
|
|
/// into its own parser. Otherwise there is no reason for this not to
|
|
|
|
/// be part of the dust_tag parser.
|
|
|
|
fn dust_tag_helper(i: &str) -> IResult<&str, DustTag> {
|
|
|
|
alt((
|
2020-05-26 00:40:58 +00:00
|
|
|
map(
|
2020-05-26 01:17:58 +00:00
|
|
|
parameterized_block("{@", &tag_to_path("gte")),
|
2020-05-26 00:40:58 +00:00
|
|
|
DustTag::DTHelperGreaterThanOrEquals,
|
|
|
|
),
|
|
|
|
map(
|
2020-05-26 01:17:58 +00:00
|
|
|
parameterized_block("{@", &tag_to_path("lte")),
|
2020-05-26 00:40:58 +00:00
|
|
|
DustTag::DTHelperLessThanOrEquals,
|
|
|
|
),
|
|
|
|
map(
|
2020-05-26 01:17:58 +00:00
|
|
|
parameterized_block("{@", &tag_to_path("eq")),
|
|
|
|
DustTag::DTHelperEquals,
|
|
|
|
),
|
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("ne")),
|
|
|
|
DustTag::DTHelperNotEquals,
|
|
|
|
),
|
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("gt")),
|
2020-05-26 00:40:58 +00:00
|
|
|
DustTag::DTHelperGreaterThan,
|
|
|
|
),
|
2020-05-26 01:17:58 +00:00
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("lt")),
|
|
|
|
DustTag::DTHelperLessThan,
|
|
|
|
),
|
2020-06-07 18:45:32 +00:00
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("sep")),
|
|
|
|
DustTag::DTHelperSep,
|
|
|
|
),
|
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("first")),
|
|
|
|
DustTag::DTHelperFirst,
|
|
|
|
),
|
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("last")),
|
|
|
|
DustTag::DTHelperLast,
|
|
|
|
),
|
2020-06-07 21:06:14 +00:00
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("select")),
|
|
|
|
DustTag::DTHelperSelect,
|
|
|
|
),
|
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("any")),
|
|
|
|
DustTag::DTHelperAny,
|
|
|
|
),
|
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("none")),
|
|
|
|
DustTag::DTHelperNone,
|
|
|
|
),
|
2020-06-13 16:03:55 +00:00
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("math")),
|
|
|
|
DustTag::DTHelperMath,
|
|
|
|
),
|
2020-06-14 16:32:10 +00:00
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("size")),
|
|
|
|
DustTag::DTHelperSize,
|
|
|
|
),
|
|
|
|
map(
|
|
|
|
parameterized_block("{@", &tag_to_path("contextDump")),
|
|
|
|
DustTag::DTHelperContextDump,
|
|
|
|
),
|
2020-05-03 18:44:09 +00:00
|
|
|
))(i)
|
2020-04-05 00:16:44 +00:00
|
|
|
}
|
|
|
|
|
2020-04-05 23:34:46 +00:00
|
|
|
/// Special characters
|
2020-04-05 00:16:44 +00:00
|
|
|
fn special(i: &str) -> IResult<&str, Special> {
|
|
|
|
delimited(
|
|
|
|
tag("{~"),
|
|
|
|
alt((
|
2020-04-05 02:45:56 +00:00
|
|
|
value(Special::LeftCurlyBrace, tag("lb")),
|
|
|
|
value(Special::RightCurlyBrace, tag("rb")),
|
2020-04-05 00:16:44 +00:00
|
|
|
value(Special::Space, tag("s")),
|
2020-04-05 02:45:56 +00:00
|
|
|
value(Special::NewLine, tag("n")),
|
|
|
|
value(Special::CarriageReturn, tag("r")),
|
2020-04-05 00:16:44 +00:00
|
|
|
)),
|
|
|
|
tag("}"),
|
|
|
|
)(i)
|
|
|
|
}
|
|
|
|
|
2020-04-05 23:34:46 +00:00
|
|
|
/// Part of a dust template that does not get rendered
|
2020-04-05 00:16:44 +00:00
|
|
|
fn comment(i: &str) -> IResult<&str, Comment> {
|
|
|
|
map(delimited(tag("{!"), take_until("!}"), tag("!}")), |body| {
|
|
|
|
Comment { value: body }
|
|
|
|
})(i)
|
|
|
|
}
|
2020-04-05 00:37:35 +00:00
|
|
|
|
2020-04-05 23:34:46 +00:00
|
|
|
/// A single element of a path
|
|
|
|
fn key(i: &str) -> IResult<&str, &str> {
|
|
|
|
recognize(tuple((
|
|
|
|
one_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$"),
|
|
|
|
opt(is_a(
|
|
|
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$0123456789-",
|
|
|
|
)),
|
|
|
|
)))(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A series of keys separated by '.' to reference a variable in the context
|
2020-04-05 00:37:35 +00:00
|
|
|
fn path(i: &str) -> IResult<&str, Path> {
|
2020-04-28 23:02:43 +00:00
|
|
|
alt((
|
|
|
|
map(separated_list1(tag("."), key), |body| Path { keys: body }),
|
2020-05-24 03:12:51 +00:00
|
|
|
map(
|
|
|
|
tuple((tag("."), separated_list1(tag("."), key))),
|
|
|
|
|(dot, mut body)| {
|
|
|
|
body.insert(0, dot);
|
|
|
|
Path { keys: body }
|
|
|
|
},
|
|
|
|
),
|
|
|
|
map(tag("."), |dot| Path { keys: vec![dot] }),
|
2020-04-28 23:02:43 +00:00
|
|
|
))(i)
|
2020-04-05 00:37:35 +00:00
|
|
|
}
|
|
|
|
|
2020-05-26 01:17:58 +00:00
|
|
|
fn tag_to_path<'a>(text: &'static str) -> impl Fn(&'a str) -> IResult<&str, Path<'a>> {
|
|
|
|
move |i: &'a str| map(tag(text), |t| Path { keys: vec![t] })(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn key_to_path<'a>(i: &'a str) -> IResult<&str, Path<'a>> {
|
|
|
|
map(key, |k| Path { keys: vec![k] })(i)
|
|
|
|
}
|
|
|
|
|
2020-05-11 03:13:25 +00:00
|
|
|
/// Just digits, no signs or decimals
|
|
|
|
fn postitive_integer_literal(i: &str) -> IResult<&str, u64> {
|
2020-05-11 03:01:14 +00:00
|
|
|
map(
|
|
|
|
verify(
|
2020-06-14 17:29:01 +00:00
|
|
|
map(
|
|
|
|
recognize(tuple((opt(tag("+")), digit1))),
|
|
|
|
|number_string: &str| number_string.parse::<u64>(),
|
|
|
|
),
|
2020-05-11 03:01:14 +00:00
|
|
|
|parse_result| parse_result.is_ok(),
|
|
|
|
),
|
2020-05-11 03:13:25 +00:00
|
|
|
|parsed_number| parsed_number.unwrap(),
|
2020-05-11 03:01:14 +00:00
|
|
|
)(i)
|
|
|
|
}
|
|
|
|
|
2020-06-13 20:06:31 +00:00
|
|
|
/// No decimals, just the sign and digits
|
|
|
|
fn negative_integer_literal(i: &str) -> IResult<&str, i64> {
|
|
|
|
map(
|
|
|
|
verify(
|
|
|
|
map(
|
|
|
|
recognize(tuple((tag("-"), digit1))),
|
|
|
|
|number_string: &str| number_string.parse::<i64>(),
|
|
|
|
),
|
|
|
|
|parse_result| parse_result.is_ok(),
|
|
|
|
),
|
|
|
|
|parsed_number| parsed_number.unwrap(),
|
|
|
|
)(i)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A non-scientific notation float (sign, digits, decimal, and more digits)
|
|
|
|
fn float_literal(i: &str) -> IResult<&str, f64> {
|
|
|
|
map(
|
|
|
|
verify(
|
|
|
|
map(
|
|
|
|
recognize(tuple((opt(one_of("+-")), digit1, tag("."), digit1))),
|
|
|
|
|number_string: &str| number_string.parse::<f64>(),
|
|
|
|
),
|
|
|
|
|parse_result| parse_result.is_ok(),
|
|
|
|
),
|
|
|
|
|parsed_number| parsed_number.unwrap(),
|
|
|
|
)(i)
|
|
|
|
}
|
|
|
|
|
2020-05-30 19:45:44 +00:00
|
|
|
fn template_string_rvalue(i: &str) -> IResult<&str, Vec<PartialNameElement>> {
|
|
|
|
let (i, template_string) = verify(quoted_string, |s: &String| {
|
|
|
|
partial_quoted_tag(s.as_str()).is_ok()
|
|
|
|
})(i)?;
|
|
|
|
|
|
|
|
let (_remaining, parsed_template_elements) = partial_quoted_tag(template_string.as_str())
|
|
|
|
.expect("A successful parse was verified earlier with a call to verify()");
|
|
|
|
let converted_template_elements = parsed_template_elements
|
|
|
|
.into_iter()
|
|
|
|
.map(|e| e.into())
|
|
|
|
.collect();
|
|
|
|
Ok((i, converted_template_elements))
|
|
|
|
}
|
|
|
|
|
2020-04-07 03:21:03 +00:00
|
|
|
/// Either a literal or a path to a value
|
2020-04-07 03:30:42 +00:00
|
|
|
fn rvalue(i: &str) -> IResult<&str, RValue> {
|
2020-04-07 03:47:50 +00:00
|
|
|
alt((
|
|
|
|
map(path, RValue::RVPath),
|
2020-05-30 19:49:13 +00:00
|
|
|
map(template_string_rvalue, RValue::RVTemplate),
|
2020-06-13 20:23:46 +00:00
|
|
|
map(float_literal, |num| {
|
|
|
|
RValue::RVLiteral(OwnedLiteral::LFloat(num))
|
2020-05-17 02:30:04 +00:00
|
|
|
}),
|
2020-06-13 20:06:31 +00:00
|
|
|
map(negative_integer_literal, |num| {
|
|
|
|
RValue::RVLiteral(OwnedLiteral::LNegativeInteger(num))
|
|
|
|
}),
|
2020-06-13 20:23:46 +00:00
|
|
|
map(postitive_integer_literal, |num| {
|
|
|
|
RValue::RVLiteral(OwnedLiteral::LPositiveInteger(num))
|
2020-06-13 20:06:31 +00:00
|
|
|
}),
|
2020-04-07 03:47:50 +00:00
|
|
|
))(i)
|
2020-04-07 03:21:03 +00:00
|
|
|
}
|
|
|
|
|
2020-04-07 03:30:42 +00:00
|
|
|
/// Parameters for a partial
|
2020-04-07 03:35:09 +00:00
|
|
|
fn key_value_pair(i: &str) -> IResult<&str, KVPair> {
|
|
|
|
map(separated_pair(key, tag("="), rvalue), |(k, v)| KVPair {
|
|
|
|
key: k,
|
|
|
|
value: v,
|
|
|
|
})(i)
|
2020-04-07 03:21:03 +00:00
|
|
|
}
|
|
|
|
|
2020-04-05 23:34:46 +00:00
|
|
|
/// Display a value from the context
|
2020-04-05 00:37:35 +00:00
|
|
|
fn reference(i: &str) -> IResult<&str, Reference> {
|
2020-06-14 17:29:01 +00:00
|
|
|
let (remaining, (p, filters)) = delimited(
|
|
|
|
tag("{"),
|
|
|
|
tuple((path, many0(filter))),
|
|
|
|
preceded(space0, tag("}")),
|
|
|
|
)(i)?;
|
2020-04-05 00:51:10 +00:00
|
|
|
Ok((
|
|
|
|
remaining,
|
|
|
|
Reference {
|
|
|
|
path: p,
|
|
|
|
filters: filters,
|
|
|
|
},
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2020-05-30 18:39:31 +00:00
|
|
|
fn parameterized_block<'a, F>(
|
2020-04-06 03:47:55 +00:00
|
|
|
open_matcher: &'static str,
|
2020-05-30 18:39:31 +00:00
|
|
|
name_matcher: F,
|
|
|
|
) -> impl FnMut(&'a str) -> IResult<&'a str, ParameterizedBlock<'a>>
|
2020-04-06 03:47:55 +00:00
|
|
|
where
|
2020-05-30 18:39:31 +00:00
|
|
|
F: Copy + Fn(&'a str) -> IResult<&'a str, Path<'a>>,
|
2020-04-06 03:47:55 +00:00
|
|
|
{
|
|
|
|
alt((
|
2020-05-30 18:39:31 +00:00
|
|
|
parameterized_block_with_body(open_matcher, name_matcher),
|
|
|
|
parameterized_self_closing_block(open_matcher, name_matcher),
|
2020-04-06 03:47:55 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2020-05-30 18:39:31 +00:00
|
|
|
fn parameterized_block_without_else<'a, F>(
|
2020-04-06 03:47:55 +00:00
|
|
|
open_matcher: &'static str,
|
2020-05-30 18:39:31 +00:00
|
|
|
name_matcher: F,
|
|
|
|
) -> impl FnMut(&'a str) -> IResult<&'a str, ParameterizedBlock<'a>>
|
2020-04-06 03:47:55 +00:00
|
|
|
where
|
2020-05-30 18:39:31 +00:00
|
|
|
F: Copy + Fn(&'a str) -> IResult<&'a str, Path<'a>>,
|
2020-04-06 03:47:55 +00:00
|
|
|
{
|
2020-05-30 18:39:31 +00:00
|
|
|
alt((
|
|
|
|
parameterized_block_with_body_without_else(open_matcher, name_matcher),
|
|
|
|
parameterized_self_closing_block(open_matcher, name_matcher),
|
|
|
|
))
|
2020-04-06 03:47:55 +00:00
|
|
|
}
|
|
|
|
|
2020-05-30 18:39:31 +00:00
|
|
|
fn parameterized_block_with_body<'a, F>(
|
2020-04-06 03:47:55 +00:00
|
|
|
open_matcher: &'static str,
|
2020-05-30 18:39:31 +00:00
|
|
|
name_matcher: F,
|
|
|
|
) -> impl Fn(&'a str) -> IResult<&'a str, ParameterizedBlock<'a>>
|
2020-04-06 03:47:55 +00:00
|
|
|
where
|
2020-05-30 18:39:31 +00:00
|
|
|
F: Copy + Fn(&'a str) -> IResult<&'a str, Path<'a>>,
|
2020-04-06 03:47:55 +00:00
|
|
|
{
|
|
|
|
move |i: &'a str| {
|
2020-05-30 18:39:31 +00:00
|
|
|
let (i, (opening_name, maybe_explicit_context, params, inner, maybe_else, _closing_name)) =
|
|
|
|
verify(
|
|
|
|
tuple((
|
|
|
|
preceded(tag(open_matcher), name_matcher),
|
|
|
|
opt(preceded(tag(":"), path)),
|
|
|
|
terminated(
|
2020-06-14 17:29:01 +00:00
|
|
|
opt(preceded(space1, separated_list1(space1, key_value_pair))),
|
|
|
|
preceded(space0, tag("}")),
|
2020-05-30 18:39:31 +00:00
|
|
|
),
|
|
|
|
opt(body),
|
|
|
|
opt(preceded(tag("{:else}"), opt(body))),
|
|
|
|
delimited(tag("{/"), name_matcher, tag("}")),
|
|
|
|
)),
|
|
|
|
|(open, _maybe_explicit, _params, _inn, _maybe_else, close)| open == close,
|
|
|
|
)(i)?;
|
2020-04-06 03:47:55 +00:00
|
|
|
|
|
|
|
Ok((
|
|
|
|
i,
|
2020-05-30 18:39:31 +00:00
|
|
|
ParameterizedBlock {
|
|
|
|
path: opening_name,
|
2020-05-25 17:55:17 +00:00
|
|
|
explicit_context: maybe_explicit_context,
|
2020-05-30 18:39:31 +00:00
|
|
|
params: params.unwrap_or(Vec::new()),
|
|
|
|
contents: inner,
|
|
|
|
else_contents: maybe_else.flatten(),
|
|
|
|
},
|
2020-04-06 03:47:55 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-30 18:39:31 +00:00
|
|
|
fn parameterized_block_with_body_without_else<'a, F>(
|
2020-04-08 01:49:28 +00:00
|
|
|
open_matcher: &'static str,
|
2020-05-26 01:17:58 +00:00
|
|
|
name_matcher: F,
|
|
|
|
) -> impl Fn(&'a str) -> IResult<&'a str, ParameterizedBlock<'a>>
|
|
|
|
where
|
|
|
|
F: Copy + Fn(&'a str) -> IResult<&'a str, Path<'a>>,
|
|
|
|
{
|
2020-04-08 01:49:28 +00:00
|
|
|
move |i: &'a str| {
|
2020-05-30 18:39:31 +00:00
|
|
|
let (i, (opening_name, maybe_explicit_context, params, inner, _closing_name)) =
|
2020-05-26 01:17:58 +00:00
|
|
|
verify(
|
|
|
|
tuple((
|
|
|
|
preceded(tag(open_matcher), name_matcher),
|
|
|
|
opt(preceded(tag(":"), path)),
|
|
|
|
terminated(
|
2020-06-14 17:29:01 +00:00
|
|
|
opt(preceded(space1, separated_list1(space1, key_value_pair))),
|
|
|
|
preceded(space0, tag("}")),
|
2020-05-26 01:17:58 +00:00
|
|
|
),
|
|
|
|
opt(body),
|
|
|
|
delimited(tag("{/"), name_matcher, tag("}")),
|
|
|
|
)),
|
2020-05-30 18:39:31 +00:00
|
|
|
|(open, _maybe_explicit, _params, _inn, close)| open == close,
|
2020-05-26 01:17:58 +00:00
|
|
|
)(i)?;
|
2020-04-08 01:49:28 +00:00
|
|
|
|
|
|
|
Ok((
|
|
|
|
i,
|
2020-05-26 00:40:58 +00:00
|
|
|
ParameterizedBlock {
|
2020-05-26 01:17:58 +00:00
|
|
|
path: opening_name,
|
2020-05-25 18:14:17 +00:00
|
|
|
explicit_context: maybe_explicit_context,
|
2020-04-08 01:49:28 +00:00
|
|
|
params: params.unwrap_or(Vec::new()),
|
|
|
|
contents: inner,
|
2020-05-30 18:39:31 +00:00
|
|
|
else_contents: None,
|
2020-05-26 00:40:58 +00:00
|
|
|
},
|
2020-04-08 01:49:28 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-26 01:17:58 +00:00
|
|
|
fn parameterized_self_closing_block<'a, F>(
|
2020-04-07 02:02:10 +00:00
|
|
|
open_matcher: &'static str,
|
2020-05-26 01:17:58 +00:00
|
|
|
name_matcher: F,
|
|
|
|
) -> impl Fn(&'a str) -> IResult<&'a str, ParameterizedBlock<'a>>
|
|
|
|
where
|
|
|
|
F: Copy + Fn(&'a str) -> IResult<&'a str, Path<'a>>,
|
|
|
|
{
|
2020-04-07 02:02:10 +00:00
|
|
|
move |i: &'a str| {
|
2020-05-26 01:17:58 +00:00
|
|
|
let (i, (opening_name, maybe_explicit_context, params)) = delimited(
|
2020-04-07 02:47:24 +00:00
|
|
|
tag(open_matcher),
|
|
|
|
tuple((
|
2020-05-26 01:17:58 +00:00
|
|
|
name_matcher,
|
2020-05-25 18:14:17 +00:00
|
|
|
opt(preceded(tag(":"), path)),
|
2020-06-14 17:29:01 +00:00
|
|
|
opt(preceded(space1, separated_list1(space1, key_value_pair))),
|
2020-04-07 02:47:24 +00:00
|
|
|
)),
|
2020-06-14 17:29:01 +00:00
|
|
|
preceded(space0, tag("/}")),
|
2020-04-07 02:47:24 +00:00
|
|
|
)(i)?;
|
2020-04-07 02:02:10 +00:00
|
|
|
|
|
|
|
Ok((
|
|
|
|
i,
|
2020-05-26 00:40:58 +00:00
|
|
|
ParameterizedBlock {
|
2020-05-26 01:17:58 +00:00
|
|
|
path: opening_name,
|
2020-05-25 18:14:17 +00:00
|
|
|
explicit_context: maybe_explicit_context,
|
2020-04-08 00:21:26 +00:00
|
|
|
params: params.unwrap_or(Vec::new()),
|
2020-04-08 00:37:15 +00:00
|
|
|
contents: None,
|
|
|
|
else_contents: None,
|
2020-05-26 00:40:58 +00:00
|
|
|
},
|
2020-04-08 00:21:26 +00:00
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-18 01:17:34 +00:00
|
|
|
fn partial_with_plain_tag<'a>(
|
2020-05-18 00:44:17 +00:00
|
|
|
open_matcher: &'static str,
|
2020-05-18 01:17:34 +00:00
|
|
|
) -> impl Fn(&'a str) -> IResult<&'a str, Partial<'a>> {
|
2020-05-18 00:44:17 +00:00
|
|
|
move |i: &'a str| {
|
2020-05-25 18:05:46 +00:00
|
|
|
let (i, (name, maybe_explicit_context, params)) = delimited(
|
2020-05-18 00:44:17 +00:00
|
|
|
tag(open_matcher),
|
|
|
|
tuple((
|
|
|
|
key,
|
2020-05-25 18:05:46 +00:00
|
|
|
opt(preceded(tag(":"), path)),
|
2020-06-14 17:29:01 +00:00
|
|
|
opt(preceded(space1, separated_list1(space1, key_value_pair))),
|
2020-05-18 00:44:17 +00:00
|
|
|
)),
|
2020-06-14 17:29:01 +00:00
|
|
|
preceded(space0, tag("/}")),
|
2020-05-18 00:44:17 +00:00
|
|
|
)(i)?;
|
|
|
|
|
|
|
|
Ok((
|
|
|
|
i,
|
2020-05-18 01:17:34 +00:00
|
|
|
Partial {
|
2020-05-18 00:44:17 +00:00
|
|
|
name: vec![PartialNameElement::PNSpan {
|
|
|
|
contents: name.to_owned(),
|
|
|
|
}],
|
2020-05-25 18:05:46 +00:00
|
|
|
explicit_context: maybe_explicit_context,
|
2020-05-18 00:44:17 +00:00
|
|
|
params: params.unwrap_or(Vec::new()),
|
|
|
|
},
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-18 01:17:34 +00:00
|
|
|
fn partial_quoted_tag(i: &str) -> IResult<&str, Vec<TemplateElement>> {
|
2020-05-18 01:21:26 +00:00
|
|
|
all_consuming(many1(alt((
|
2020-05-18 00:44:17 +00:00
|
|
|
map(span, TemplateElement::TESpan),
|
|
|
|
map(map(reference, DustTag::DTReference), TemplateElement::TETag),
|
2020-05-18 01:21:26 +00:00
|
|
|
))))(i)
|
2020-05-18 00:44:17 +00:00
|
|
|
}
|
|
|
|
|
2020-05-18 01:17:34 +00:00
|
|
|
fn partial_with_quoted_tag<'a>(
|
2020-05-18 00:44:17 +00:00
|
|
|
open_matcher: &'static str,
|
2020-05-18 01:17:34 +00:00
|
|
|
) -> impl Fn(&'a str) -> IResult<&'a str, Partial<'a>> {
|
2020-05-18 00:44:17 +00:00
|
|
|
move |i: &'a str| {
|
2020-05-25 18:05:46 +00:00
|
|
|
let (i, (name, maybe_explicit_context, params)) = delimited(
|
2020-05-18 00:44:17 +00:00
|
|
|
tag(open_matcher),
|
|
|
|
tuple((
|
2020-05-30 19:45:44 +00:00
|
|
|
template_string_rvalue,
|
2020-05-25 18:05:46 +00:00
|
|
|
opt(preceded(tag(":"), path)),
|
2020-06-14 17:29:01 +00:00
|
|
|
opt(preceded(space1, separated_list1(space1, key_value_pair))),
|
2020-05-18 00:44:17 +00:00
|
|
|
)),
|
2020-06-14 17:29:01 +00:00
|
|
|
preceded(space0, tag("/}")),
|
2020-05-18 00:44:17 +00:00
|
|
|
)(i)?;
|
|
|
|
|
|
|
|
Ok((
|
|
|
|
i,
|
2020-05-18 01:17:34 +00:00
|
|
|
Partial {
|
2020-05-30 19:45:44 +00:00
|
|
|
name: name,
|
2020-05-25 18:05:46 +00:00
|
|
|
explicit_context: maybe_explicit_context,
|
2020-05-18 00:44:17 +00:00
|
|
|
params: params.unwrap_or(Vec::new()),
|
|
|
|
},
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-18 01:17:34 +00:00
|
|
|
fn partial<'a, F>(
|
2020-05-18 00:44:17 +00:00
|
|
|
open_matcher: &'static str,
|
|
|
|
constructor: F,
|
|
|
|
) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>>
|
|
|
|
where
|
2020-05-18 01:17:34 +00:00
|
|
|
F: Fn(Partial<'a>) -> DustTag<'a>,
|
2020-05-18 00:44:17 +00:00
|
|
|
{
|
2020-05-18 01:17:34 +00:00
|
|
|
let plain = partial_with_plain_tag(open_matcher);
|
|
|
|
let quoted = partial_with_quoted_tag(open_matcher);
|
2020-05-18 00:44:17 +00:00
|
|
|
move |i: &'a str| map(alt((&plain, "ed)), &constructor)(i)
|
|
|
|
}
|
|
|
|
|
2020-04-05 00:51:10 +00:00
|
|
|
fn filter(i: &str) -> IResult<&str, Filter> {
|
|
|
|
preceded(
|
|
|
|
tag("|"),
|
|
|
|
alt((
|
|
|
|
value(Filter::JsonStringify, tag("js")),
|
|
|
|
value(Filter::JsonParse, tag("jp")),
|
|
|
|
value(Filter::EncodeUriComponent, tag("uc")),
|
|
|
|
value(Filter::HtmlEncode, tag("h")),
|
|
|
|
value(Filter::DisableHtmlEncode, tag("s")),
|
|
|
|
value(Filter::JavascriptStringEncode, tag("j")),
|
|
|
|
value(Filter::EncodeUri, tag("u")),
|
|
|
|
)),
|
|
|
|
)(i)
|
2020-04-05 00:37:35 +00:00
|
|
|
}
|
2020-04-05 01:30:56 +00:00
|
|
|
|
2020-05-03 17:43:49 +00:00
|
|
|
/// Whitespace at the beginning of lines is ignored so we are matching
|
|
|
|
/// a newline character followed by as much contiguous whitespace as
|
|
|
|
/// possible, all of which will be thrown away by other parsers.
|
2020-05-03 18:44:09 +00:00
|
|
|
fn ignore_new_line_leading_whitespace(i: &str) -> IResult<&str, IgnoredWhitespace> {
|
|
|
|
map(
|
|
|
|
recognize(tuple((line_ending, multispace0))),
|
|
|
|
IgnoredWhitespace::StartOfLine,
|
|
|
|
)(i)
|
2020-05-03 16:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 03:41:05 +00:00
|
|
|
fn literal_string_block(i: &str) -> IResult<&str, &str> {
|
|
|
|
delimited(tag("{`"), take_until("`}"), tag("`}"))(i)
|
|
|
|
}
|
|
|
|
|
2020-05-03 18:44:09 +00:00
|
|
|
/// Any text that is not a Dust element or ignored whitespace
|
|
|
|
fn span(i: &str) -> IResult<&str, Span> {
|
|
|
|
let (remaining, line) = verify(
|
2020-05-03 16:28:55 +00:00
|
|
|
take_until_parser_matches(alt((
|
|
|
|
tag("{"),
|
|
|
|
line_ending,
|
|
|
|
recognize(all_consuming(eof_whitespace)),
|
|
|
|
))),
|
|
|
|
|s: &str| s.len() > 0,
|
|
|
|
)(i)?;
|
2020-05-03 18:44:09 +00:00
|
|
|
Ok((remaining, Span { contents: line }))
|
2020-04-05 01:30:56 +00:00
|
|
|
}
|
|
|
|
|
2020-04-05 23:29:16 +00:00
|
|
|
fn body(i: &str) -> IResult<&str, Body> {
|
2020-04-05 03:42:27 +00:00
|
|
|
let (remaining, template_elements) = many1(alt((
|
2020-05-03 18:44:09 +00:00
|
|
|
map(
|
|
|
|
ignore_new_line_leading_whitespace,
|
|
|
|
TemplateElement::TEIgnoredWhitespace,
|
|
|
|
),
|
2020-04-05 01:30:56 +00:00
|
|
|
map(span, TemplateElement::TESpan),
|
|
|
|
map(dust_tag, TemplateElement::TETag),
|
|
|
|
)))(i)?;
|
|
|
|
Ok((
|
|
|
|
remaining,
|
2020-04-05 23:29:16 +00:00
|
|
|
Body {
|
2020-04-05 01:30:56 +00:00
|
|
|
elements: template_elements,
|
|
|
|
},
|
|
|
|
))
|
|
|
|
}
|
2020-04-05 02:45:56 +00:00
|
|
|
|
2020-04-05 03:42:27 +00:00
|
|
|
pub fn template(i: &str) -> IResult<&str, Template> {
|
2020-04-12 20:02:26 +00:00
|
|
|
// DustJS ignores all preceding whitespace (tabs, newlines, spaces) but only ignores trailing newlines
|
2020-05-25 18:17:38 +00:00
|
|
|
let (remaining, contents) = all_consuming(delimited(multispace0, body, eof_whitespace))(i)?;
|
2020-04-05 03:42:27 +00:00
|
|
|
Ok((remaining, Template { contents: contents }))
|
|
|
|
}
|
|
|
|
|
2020-04-07 01:09:06 +00:00
|
|
|
fn quoted_string(i: &str) -> IResult<&str, String> {
|
2020-04-07 01:03:03 +00:00
|
|
|
delimited(
|
|
|
|
tag(r#"""#),
|
2020-04-07 01:09:06 +00:00
|
|
|
escaped_transform(is_not(r#"\""#), '\\', one_of(r#"\""#)),
|
2020-04-07 01:03:03 +00:00
|
|
|
tag(r#"""#),
|
|
|
|
)(i)
|
2020-04-07 00:20:53 +00:00
|
|
|
}
|
|
|
|
|
2020-04-12 20:02:26 +00:00
|
|
|
fn eof_whitespace(i: &str) -> IResult<&str, Vec<&str>> {
|
|
|
|
many0(line_ending)(i)
|
|
|
|
}
|
|
|
|
|
2020-04-05 02:45:56 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use nom::bytes::complete::is_a;
|
|
|
|
use nom::error::ErrorKind;
|
|
|
|
use nom::Err::Error;
|
|
|
|
|
2020-06-13 20:23:46 +00:00
|
|
|
#[test]
|
|
|
|
fn test_direct_literal() {
|
|
|
|
assert_eq!(super::float_literal("-17.4"), Ok(("", -17.4)));
|
|
|
|
assert_eq!(super::float_literal("17.1"), Ok(("", 17.1)));
|
|
|
|
assert_eq!(super::negative_integer_literal("-12"), Ok(("", -12)));
|
|
|
|
}
|
|
|
|
|
2020-04-05 02:45:56 +00:00
|
|
|
#[test]
|
|
|
|
fn test_reference() {
|
|
|
|
assert_eq!(
|
|
|
|
super::reference("{foo.bar.baz|js|s}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
|
|
|
Reference {
|
|
|
|
path: Path {
|
|
|
|
keys: vec!["foo", "bar", "baz"]
|
|
|
|
},
|
|
|
|
filters: vec![Filter::JsonStringify, Filter::DisableHtmlEncode],
|
|
|
|
}
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-05-24 17:58:20 +00:00
|
|
|
#[test]
|
|
|
|
fn test_reference_to_variable() {
|
|
|
|
assert_eq!(
|
|
|
|
super::reference("{$idx}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
|
|
|
Reference {
|
|
|
|
path: Path { keys: vec!["$idx"] },
|
|
|
|
filters: Vec::new(),
|
|
|
|
}
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-05 02:45:56 +00:00
|
|
|
#[test]
|
|
|
|
fn test_path() {
|
|
|
|
assert_eq!(
|
|
|
|
is_a::<_, _, (_, ErrorKind)>("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$")(
|
|
|
|
"foo"
|
|
|
|
),
|
|
|
|
Ok(("", "foo"))
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
super::path("foo.bar.baz"),
|
|
|
|
Ok((
|
|
|
|
"",
|
|
|
|
Path {
|
|
|
|
keys: vec!["foo", "bar", "baz"]
|
|
|
|
}
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_special() {
|
|
|
|
assert_eq!(super::special("{~s}"), Ok(("", Special::Space)));
|
|
|
|
assert_eq!(super::special("{~n}"), Ok(("", Special::NewLine)));
|
|
|
|
assert_eq!(super::special("{~r}"), Ok(("", Special::CarriageReturn)));
|
|
|
|
assert_eq!(super::special("{~lb}"), Ok(("", Special::LeftCurlyBrace)));
|
|
|
|
assert_eq!(super::special("{~rb}"), Ok(("", Special::RightCurlyBrace)));
|
|
|
|
assert_eq!(
|
|
|
|
super::special("{~zzz}"),
|
|
|
|
Err(Error(("zzz}", ErrorKind::Tag)))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_comment() {
|
|
|
|
assert_eq!(
|
|
|
|
super::comment("{! yo dawg} this is a comment !}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
|
|
|
Comment {
|
|
|
|
value: " yo dawg} this is a comment "
|
|
|
|
}
|
|
|
|
))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
super::special("{! this is a comment without a close"),
|
|
|
|
Err(Error((
|
|
|
|
"{! this is a comment without a close",
|
|
|
|
ErrorKind::Tag
|
|
|
|
)))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-05-03 16:28:55 +00:00
|
|
|
#[test]
|
|
|
|
fn test_span_end_of_line() {
|
|
|
|
assert_eq!(
|
2020-05-03 18:44:09 +00:00
|
|
|
super::ignore_new_line_leading_whitespace("\n \t \n\nfoo"),
|
|
|
|
Ok(("foo", IgnoredWhitespace::StartOfLine("\n \t \n\n")))
|
2020-05-03 16:28:55 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-05-03 16:48:02 +00:00
|
|
|
fn test_span() {
|
2020-05-03 16:28:55 +00:00
|
|
|
assert_eq!(
|
2020-05-03 16:48:02 +00:00
|
|
|
super::span("this is just some text"),
|
2020-05-03 16:28:55 +00:00
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-03 16:48:02 +00:00
|
|
|
Span {
|
2020-05-03 18:44:09 +00:00
|
|
|
contents: "this is just some text"
|
2020-05-03 16:28:55 +00:00
|
|
|
}
|
|
|
|
))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-05-03 16:48:02 +00:00
|
|
|
super::span("this is just some text {~lb}"),
|
2020-05-03 16:28:55 +00:00
|
|
|
Ok((
|
|
|
|
"{~lb}",
|
2020-05-03 16:48:02 +00:00
|
|
|
Span {
|
2020-05-03 18:44:09 +00:00
|
|
|
contents: "this is just some text "
|
2020-05-03 16:28:55 +00:00
|
|
|
}
|
|
|
|
))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-05-03 16:48:02 +00:00
|
|
|
super::span("{~lb}"),
|
2020-05-03 16:28:55 +00:00
|
|
|
Err(Error(("{~lb}", ErrorKind::Verify)))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-05-03 18:44:09 +00:00
|
|
|
super::body("this is \t \n\n \t \n \t multiline text\n {foo}"),
|
2020-05-03 16:28:55 +00:00
|
|
|
Ok((
|
2020-05-03 18:44:09 +00:00
|
|
|
"",
|
|
|
|
Body {
|
|
|
|
elements: vec![
|
|
|
|
TemplateElement::TESpan(Span {
|
|
|
|
contents: "this is \t "
|
|
|
|
}),
|
|
|
|
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
|
|
|
|
"\n\n \t \n \t "
|
|
|
|
)),
|
|
|
|
TemplateElement::TESpan(Span {
|
|
|
|
contents: "multiline text"
|
|
|
|
}),
|
|
|
|
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
|
|
|
|
"\n "
|
|
|
|
)),
|
|
|
|
TemplateElement::TETag(DustTag::DTReference(Reference {
|
|
|
|
path: Path { keys: vec!["foo"] },
|
|
|
|
filters: vec![]
|
|
|
|
}))
|
|
|
|
]
|
2020-05-03 16:28:55 +00:00
|
|
|
}
|
|
|
|
))
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-05-03 18:44:09 +00:00
|
|
|
super::body("\n leading whitespace"),
|
2020-05-03 16:28:55 +00:00
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-03 18:44:09 +00:00
|
|
|
Body {
|
|
|
|
elements: vec![
|
|
|
|
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
|
|
|
|
"\n "
|
|
|
|
)),
|
|
|
|
TemplateElement::TESpan(Span {
|
|
|
|
contents: "leading whitespace"
|
|
|
|
}),
|
|
|
|
]
|
2020-05-03 16:28:55 +00:00
|
|
|
}
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
2020-04-05 03:42:27 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_section_mismatched_paths() {
|
|
|
|
assert_eq!(
|
2020-04-05 21:05:22 +00:00
|
|
|
super::dust_tag("{#foo.bar}{/baz}"),
|
|
|
|
Err(Error(("{#foo.bar}{/baz}", ErrorKind::Tag)))
|
2020-04-05 03:42:27 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_empty_section() {
|
|
|
|
assert_eq!(
|
2020-04-05 21:05:22 +00:00
|
|
|
super::dust_tag("{#foo.bar}{/foo.bar}"),
|
2020-04-05 03:42:27 +00:00
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-26 01:26:16 +00:00
|
|
|
DustTag::DTSection(ParameterizedBlock {
|
2020-04-05 21:12:48 +00:00
|
|
|
path: Path {
|
|
|
|
keys: vec!["foo", "bar"]
|
|
|
|
},
|
2020-05-25 17:38:31 +00:00
|
|
|
explicit_context: None,
|
2020-05-26 01:26:16 +00:00
|
|
|
params: Vec::new(),
|
2020-04-05 21:24:13 +00:00
|
|
|
contents: None,
|
|
|
|
else_contents: None,
|
2020-04-05 21:12:48 +00:00
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_self_closing_section() {
|
|
|
|
assert_eq!(
|
|
|
|
super::dust_tag("{#foo.bar/}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-26 01:26:16 +00:00
|
|
|
DustTag::DTSection(ParameterizedBlock {
|
2020-04-05 03:42:27 +00:00
|
|
|
path: Path {
|
|
|
|
keys: vec!["foo", "bar"]
|
|
|
|
},
|
2020-05-25 17:38:31 +00:00
|
|
|
explicit_context: None,
|
2020-05-26 01:26:16 +00:00
|
|
|
params: Vec::new(),
|
2020-04-05 21:24:13 +00:00
|
|
|
contents: None,
|
|
|
|
else_contents: None,
|
2020-04-05 21:05:22 +00:00
|
|
|
})
|
2020-04-05 03:42:27 +00:00
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_section_with_body() {
|
|
|
|
assert_eq!(
|
2020-04-05 21:05:22 +00:00
|
|
|
super::dust_tag("{#foo.bar}hello {name}{/foo.bar}"),
|
2020-04-05 03:42:27 +00:00
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-26 01:26:16 +00:00
|
|
|
DustTag::DTSection(ParameterizedBlock {
|
2020-04-05 03:42:27 +00:00
|
|
|
path: Path {
|
|
|
|
keys: vec!["foo", "bar"]
|
|
|
|
},
|
2020-05-25 17:38:31 +00:00
|
|
|
explicit_context: None,
|
2020-05-26 01:26:16 +00:00
|
|
|
params: Vec::new(),
|
2020-04-05 23:29:16 +00:00
|
|
|
contents: Some(Body {
|
2020-04-05 03:42:27 +00:00
|
|
|
elements: vec![
|
2020-05-03 18:44:09 +00:00
|
|
|
TemplateElement::TESpan(Span { contents: "hello " }),
|
2020-04-05 03:42:27 +00:00
|
|
|
TemplateElement::TETag(DustTag::DTReference(Reference {
|
|
|
|
path: Path { keys: vec!["name"] },
|
|
|
|
filters: Vec::new()
|
|
|
|
}))
|
|
|
|
]
|
2020-04-05 21:24:13 +00:00
|
|
|
}),
|
|
|
|
else_contents: None,
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_section_with_else_body() {
|
|
|
|
assert_eq!(
|
|
|
|
super::dust_tag("{#greeting}hello {name}{:else}goodbye {name}{/greeting}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-26 01:26:16 +00:00
|
|
|
DustTag::DTSection(ParameterizedBlock {
|
2020-04-05 21:24:13 +00:00
|
|
|
path: Path {
|
|
|
|
keys: vec!["greeting"]
|
|
|
|
},
|
2020-05-25 17:38:31 +00:00
|
|
|
explicit_context: None,
|
2020-05-26 01:26:16 +00:00
|
|
|
params: Vec::new(),
|
2020-04-05 23:29:16 +00:00
|
|
|
contents: Some(Body {
|
2020-04-05 21:24:13 +00:00
|
|
|
elements: vec![
|
2020-05-03 18:44:09 +00:00
|
|
|
TemplateElement::TESpan(Span { contents: "hello " }),
|
2020-04-05 21:24:13 +00:00
|
|
|
TemplateElement::TETag(DustTag::DTReference(Reference {
|
|
|
|
path: Path { keys: vec!["name"] },
|
|
|
|
filters: Vec::new()
|
|
|
|
}))
|
|
|
|
]
|
|
|
|
}),
|
2020-04-05 23:29:16 +00:00
|
|
|
else_contents: Some(Body {
|
2020-04-05 21:24:13 +00:00
|
|
|
elements: vec![
|
|
|
|
TemplateElement::TESpan(Span {
|
2020-05-03 18:44:09 +00:00
|
|
|
contents: "goodbye "
|
2020-04-05 21:24:13 +00:00
|
|
|
}),
|
|
|
|
TemplateElement::TETag(DustTag::DTReference(Reference {
|
|
|
|
path: Path { keys: vec!["name"] },
|
|
|
|
filters: Vec::new()
|
|
|
|
}))
|
|
|
|
]
|
|
|
|
}),
|
2020-04-05 21:05:22 +00:00
|
|
|
})
|
2020-04-05 03:42:27 +00:00
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
2020-04-06 03:47:55 +00:00
|
|
|
|
2020-05-25 17:38:31 +00:00
|
|
|
#[test]
|
|
|
|
fn test_empty_section_with_explicit_context() {
|
|
|
|
assert_eq!(
|
|
|
|
super::dust_tag("{#foo.bar:baz.ipsum}{/foo.bar}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-26 01:26:16 +00:00
|
|
|
DustTag::DTSection(ParameterizedBlock {
|
2020-05-25 17:38:31 +00:00
|
|
|
path: Path {
|
|
|
|
keys: vec!["foo", "bar"]
|
|
|
|
},
|
|
|
|
explicit_context: Some(Path {
|
|
|
|
keys: vec!["baz", "ipsum"]
|
|
|
|
}),
|
2020-05-26 01:26:16 +00:00
|
|
|
params: Vec::new(),
|
2020-05-25 17:38:31 +00:00
|
|
|
contents: None,
|
|
|
|
else_contents: None,
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_self_closing_section_with_explicit_context() {
|
|
|
|
assert_eq!(
|
|
|
|
super::dust_tag("{#foo.bar:$idx/}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-26 01:26:16 +00:00
|
|
|
DustTag::DTSection(ParameterizedBlock {
|
2020-05-25 17:38:31 +00:00
|
|
|
path: Path {
|
|
|
|
keys: vec!["foo", "bar"]
|
|
|
|
},
|
|
|
|
explicit_context: Some(Path { keys: vec!["$idx"] }),
|
2020-05-26 01:26:16 +00:00
|
|
|
params: Vec::new(),
|
2020-05-25 17:38:31 +00:00
|
|
|
contents: None,
|
|
|
|
else_contents: None,
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-06 03:47:55 +00:00
|
|
|
#[test]
|
|
|
|
fn test_self_closing_block() {
|
|
|
|
assert_eq!(
|
|
|
|
super::dust_tag("{+foo/}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-30 18:39:31 +00:00
|
|
|
DustTag::DTBlock(ParameterizedBlock {
|
|
|
|
path: Path { keys: vec!["foo"] },
|
2020-05-25 17:55:17 +00:00
|
|
|
explicit_context: None,
|
2020-05-30 18:39:31 +00:00
|
|
|
params: Vec::new(),
|
|
|
|
contents: None,
|
|
|
|
else_contents: None
|
2020-04-06 03:47:55 +00:00
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_block() {
|
|
|
|
assert_eq!(
|
|
|
|
super::dust_tag("{+foo}hello {name}{/foo}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-30 18:39:31 +00:00
|
|
|
DustTag::DTBlock(ParameterizedBlock {
|
|
|
|
path: Path { keys: vec!["foo"] },
|
2020-05-25 17:55:17 +00:00
|
|
|
explicit_context: None,
|
2020-05-30 18:39:31 +00:00
|
|
|
params: Vec::new(),
|
2020-05-25 17:55:17 +00:00
|
|
|
contents: Some(Body {
|
|
|
|
elements: vec![
|
|
|
|
TemplateElement::TESpan(Span { contents: "hello " }),
|
|
|
|
TemplateElement::TETag(DustTag::DTReference(Reference {
|
|
|
|
path: Path { keys: vec!["name"] },
|
|
|
|
filters: Vec::new()
|
|
|
|
}))
|
|
|
|
]
|
2020-05-30 18:39:31 +00:00
|
|
|
}),
|
|
|
|
else_contents: None
|
2020-05-25 17:55:17 +00:00
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_self_closing_block_with_explicit_context() {
|
|
|
|
assert_eq!(
|
|
|
|
super::dust_tag("{+foo:bar.baz/}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-30 18:39:31 +00:00
|
|
|
DustTag::DTBlock(ParameterizedBlock {
|
|
|
|
path: Path { keys: vec!["foo"] },
|
2020-05-25 17:55:17 +00:00
|
|
|
explicit_context: Some(Path {
|
|
|
|
keys: vec!["bar", "baz"]
|
|
|
|
}),
|
2020-05-30 18:39:31 +00:00
|
|
|
params: Vec::new(),
|
|
|
|
contents: None,
|
|
|
|
else_contents: None
|
2020-05-25 17:55:17 +00:00
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_block_with_explicit_context() {
|
|
|
|
assert_eq!(
|
|
|
|
super::dust_tag("{+foo:bar.baz}hello {name}{/foo}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-30 18:39:31 +00:00
|
|
|
DustTag::DTBlock(ParameterizedBlock {
|
|
|
|
path: Path { keys: vec!["foo"] },
|
2020-05-25 17:55:17 +00:00
|
|
|
explicit_context: Some(Path {
|
|
|
|
keys: vec!["bar", "baz"]
|
|
|
|
}),
|
2020-05-30 18:39:31 +00:00
|
|
|
params: Vec::new(),
|
2020-04-06 03:47:55 +00:00
|
|
|
contents: Some(Body {
|
|
|
|
elements: vec![
|
2020-05-03 18:44:09 +00:00
|
|
|
TemplateElement::TESpan(Span { contents: "hello " }),
|
2020-04-06 03:47:55 +00:00
|
|
|
TemplateElement::TETag(DustTag::DTReference(Reference {
|
|
|
|
path: Path { keys: vec!["name"] },
|
|
|
|
filters: Vec::new()
|
|
|
|
}))
|
|
|
|
]
|
2020-05-30 18:39:31 +00:00
|
|
|
}),
|
|
|
|
else_contents: None
|
2020-04-06 03:47:55 +00:00
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_self_closing_inline_partial() {
|
|
|
|
assert_eq!(
|
|
|
|
super::dust_tag("{<foo/}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-30 18:39:31 +00:00
|
|
|
DustTag::DTInlinePartial(ParameterizedBlock {
|
|
|
|
path: Path { keys: vec!["foo"] },
|
2020-05-25 17:55:17 +00:00
|
|
|
explicit_context: None,
|
2020-05-30 18:39:31 +00:00
|
|
|
params: Vec::new(),
|
|
|
|
contents: None,
|
|
|
|
else_contents: None
|
2020-04-06 03:47:55 +00:00
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_inline_partial() {
|
|
|
|
assert_eq!(
|
|
|
|
super::dust_tag("{<foo}hello {name}{/foo}"),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-30 18:39:31 +00:00
|
|
|
DustTag::DTInlinePartial(ParameterizedBlock {
|
|
|
|
path: Path { keys: vec!["foo"] },
|
2020-05-25 17:55:17 +00:00
|
|
|
explicit_context: None,
|
2020-05-30 18:39:31 +00:00
|
|
|
params: Vec::new(),
|
2020-04-06 03:47:55 +00:00
|
|
|
contents: Some(Body {
|
|
|
|
elements: vec![
|
2020-05-03 18:44:09 +00:00
|
|
|
TemplateElement::TESpan(Span { contents: "hello " }),
|
2020-04-06 03:47:55 +00:00
|
|
|
TemplateElement::TETag(DustTag::DTReference(Reference {
|
|
|
|
path: Path { keys: vec!["name"] },
|
|
|
|
filters: Vec::new()
|
|
|
|
}))
|
|
|
|
]
|
2020-05-30 18:39:31 +00:00
|
|
|
}),
|
|
|
|
else_contents: None
|
2020-04-06 03:47:55 +00:00
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
2020-04-07 00:20:53 +00:00
|
|
|
|
|
|
|
#[test]
|
2020-04-07 01:26:08 +00:00
|
|
|
fn test_quoted_string() {
|
2020-04-07 01:09:06 +00:00
|
|
|
assert_eq!(
|
2020-04-07 01:26:08 +00:00
|
|
|
quoted_string(r#""foo\"bar\\baz""#),
|
|
|
|
Ok(("", r#"foo"bar\baz"#.to_owned()))
|
2020-04-07 01:09:06 +00:00
|
|
|
);
|
2020-04-07 00:20:53 +00:00
|
|
|
}
|
2020-04-07 02:02:10 +00:00
|
|
|
|
|
|
|
#[test]
|
2020-04-07 23:51:06 +00:00
|
|
|
fn test_unquoted_partial() {
|
2020-04-07 03:47:50 +00:00
|
|
|
assert_eq!(
|
2020-04-07 23:51:06 +00:00
|
|
|
dust_tag(r#"{>foo bar=baz animal="cat"/}"#),
|
2020-04-07 03:47:50 +00:00
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-18 01:17:34 +00:00
|
|
|
DustTag::DTPartial(Partial {
|
2020-05-18 00:44:17 +00:00
|
|
|
name: vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "foo".to_owned()
|
|
|
|
},],
|
2020-05-25 18:05:46 +00:00
|
|
|
explicit_context: None,
|
2020-04-07 23:51:06 +00:00
|
|
|
params: vec![
|
|
|
|
KVPair {
|
|
|
|
key: "bar",
|
|
|
|
value: RValue::RVPath(Path { keys: vec!["baz"] })
|
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "animal",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "cat".to_owned()
|
|
|
|
}])
|
2020-04-07 23:51:06 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
})
|
2020-04-07 03:47:50 +00:00
|
|
|
))
|
|
|
|
);
|
2020-04-07 02:02:10 +00:00
|
|
|
}
|
2020-04-07 23:53:57 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_quoted_partial() {
|
|
|
|
assert_eq!(
|
|
|
|
dust_tag(r#"{>"template name * with * special \" characters" bar=baz animal="cat"/}"#),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-18 01:17:34 +00:00
|
|
|
DustTag::DTPartial(Partial {
|
2020-05-18 00:44:17 +00:00
|
|
|
name: vec![PartialNameElement::PNSpan {
|
|
|
|
contents: r#"template name * with * special " characters"#.to_owned()
|
|
|
|
},],
|
2020-05-25 18:05:46 +00:00
|
|
|
explicit_context: None,
|
2020-05-18 00:44:17 +00:00
|
|
|
params: vec![
|
|
|
|
KVPair {
|
|
|
|
key: "bar",
|
|
|
|
value: RValue::RVPath(Path { keys: vec!["baz"] })
|
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "animal",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "cat".to_owned()
|
|
|
|
}])
|
2020-05-18 00:44:17 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_dynamic_partial() {
|
|
|
|
assert_eq!(
|
|
|
|
dust_tag(r#"{>"dynamic{ref}template" bar=baz animal="cat"/}"#),
|
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-18 01:17:34 +00:00
|
|
|
DustTag::DTPartial(Partial {
|
2020-05-18 00:44:17 +00:00
|
|
|
name: vec![
|
|
|
|
PartialNameElement::PNSpan {
|
|
|
|
contents: "dynamic".to_owned()
|
|
|
|
},
|
|
|
|
PartialNameElement::PNReference {
|
|
|
|
path: vec!["ref".to_owned()],
|
|
|
|
filters: Vec::new()
|
|
|
|
},
|
|
|
|
PartialNameElement::PNSpan {
|
|
|
|
contents: "template".to_owned()
|
|
|
|
}
|
|
|
|
],
|
2020-05-25 18:05:46 +00:00
|
|
|
explicit_context: None,
|
|
|
|
params: vec![
|
|
|
|
KVPair {
|
|
|
|
key: "bar",
|
|
|
|
value: RValue::RVPath(Path { keys: vec!["baz"] })
|
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "animal",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "cat".to_owned()
|
|
|
|
}])
|
2020-05-25 18:05:46 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_unquoted_partial_with_explicit_context() {
|
|
|
|
assert_eq!(
|
|
|
|
dust_tag(r#"{>foo:foo.bar bar=baz animal="cat"/}"#),
|
|
|
|
Ok((
|
|
|
|
"",
|
|
|
|
DustTag::DTPartial(Partial {
|
|
|
|
name: vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "foo".to_owned()
|
|
|
|
},],
|
|
|
|
explicit_context: Some(Path {
|
|
|
|
keys: vec!["foo", "bar"]
|
|
|
|
}),
|
|
|
|
params: vec![
|
|
|
|
KVPair {
|
|
|
|
key: "bar",
|
|
|
|
value: RValue::RVPath(Path { keys: vec!["baz"] })
|
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "animal",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "cat".to_owned()
|
|
|
|
}])
|
2020-05-25 18:05:46 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_quoted_partial_with_explicit_context() {
|
|
|
|
assert_eq!(
|
|
|
|
dust_tag(
|
|
|
|
r#"{>"template name * with * special \" characters":foo.bar bar=baz animal="cat"/}"#
|
|
|
|
),
|
|
|
|
Ok((
|
|
|
|
"",
|
|
|
|
DustTag::DTPartial(Partial {
|
|
|
|
name: vec![PartialNameElement::PNSpan {
|
|
|
|
contents: r#"template name * with * special " characters"#.to_owned()
|
|
|
|
},],
|
|
|
|
explicit_context: Some(Path {
|
|
|
|
keys: vec!["foo", "bar"]
|
|
|
|
}),
|
2020-04-07 23:53:57 +00:00
|
|
|
params: vec![
|
|
|
|
KVPair {
|
|
|
|
key: "bar",
|
|
|
|
value: RValue::RVPath(Path { keys: vec!["baz"] })
|
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "animal",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "cat".to_owned()
|
|
|
|
}])
|
2020-04-07 23:53:57 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
2020-04-08 01:49:28 +00:00
|
|
|
|
2020-05-11 03:42:56 +00:00
|
|
|
#[test]
|
|
|
|
fn test_literals() {
|
|
|
|
assert_eq!(
|
2020-06-13 20:18:48 +00:00
|
|
|
dust_tag(r#"{>foo a="foo" b=179 c=17.1 d=-12 e=-17.4/}"#),
|
2020-05-11 03:42:56 +00:00
|
|
|
Ok((
|
|
|
|
"",
|
2020-05-18 01:17:34 +00:00
|
|
|
DustTag::DTPartial(Partial {
|
2020-05-18 00:44:17 +00:00
|
|
|
name: vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "foo".to_owned()
|
|
|
|
},],
|
2020-05-25 18:05:46 +00:00
|
|
|
explicit_context: None,
|
2020-05-11 03:42:56 +00:00
|
|
|
params: vec![
|
|
|
|
KVPair {
|
|
|
|
key: "a",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "foo".to_owned()
|
|
|
|
}])
|
2020-05-11 03:42:56 +00:00
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "b",
|
2020-05-17 02:39:29 +00:00
|
|
|
value: RValue::RVLiteral(OwnedLiteral::LPositiveInteger(179))
|
2020-06-13 20:18:48 +00:00
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "c",
|
|
|
|
value: RValue::RVLiteral(OwnedLiteral::LFloat(17.1))
|
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "d",
|
|
|
|
value: RValue::RVLiteral(OwnedLiteral::LNegativeInteger(-12))
|
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "e",
|
|
|
|
value: RValue::RVLiteral(OwnedLiteral::LFloat(-17.4))
|
2020-05-11 03:42:56 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-08 01:49:28 +00:00
|
|
|
#[test]
|
|
|
|
fn test_helper() {
|
|
|
|
assert_eq!(
|
|
|
|
dust_tag(r#"{@eq key=name value="cat"}Pet the {name}!{/eq}"#),
|
|
|
|
Ok((
|
|
|
|
"",
|
|
|
|
DustTag::DTHelperEquals(ParameterizedBlock {
|
2020-05-26 01:17:58 +00:00
|
|
|
path: Path { keys: vec!["eq"] },
|
2020-05-25 18:14:17 +00:00
|
|
|
explicit_context: None,
|
2020-04-08 01:49:28 +00:00
|
|
|
params: vec![
|
|
|
|
KVPair {
|
|
|
|
key: "key",
|
|
|
|
value: RValue::RVPath(Path { keys: vec!["name"] })
|
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "value",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "cat".to_owned()
|
|
|
|
}])
|
2020-04-08 01:49:28 +00:00
|
|
|
}
|
|
|
|
],
|
|
|
|
contents: Some(Body {
|
|
|
|
elements: vec![
|
|
|
|
TemplateElement::TESpan(Span {
|
2020-05-03 18:44:09 +00:00
|
|
|
contents: "Pet the "
|
2020-04-08 01:49:28 +00:00
|
|
|
}),
|
|
|
|
TemplateElement::TETag(DustTag::DTReference(Reference {
|
|
|
|
path: Path { keys: vec!["name"] },
|
|
|
|
filters: Vec::new()
|
|
|
|
})),
|
2020-05-03 18:44:09 +00:00
|
|
|
TemplateElement::TESpan(Span { contents: "!" })
|
2020-04-08 01:49:28 +00:00
|
|
|
]
|
|
|
|
}),
|
|
|
|
else_contents: None
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_self_closing_helper() {
|
|
|
|
assert_eq!(
|
|
|
|
dust_tag(r#"{@eq key=name value="cat"/}"#),
|
|
|
|
Ok((
|
|
|
|
"",
|
|
|
|
DustTag::DTHelperEquals(ParameterizedBlock {
|
2020-05-26 01:17:58 +00:00
|
|
|
path: Path { keys: vec!["eq"] },
|
2020-05-25 18:14:17 +00:00
|
|
|
explicit_context: None,
|
|
|
|
params: vec![
|
|
|
|
KVPair {
|
|
|
|
key: "key",
|
|
|
|
value: RValue::RVPath(Path { keys: vec!["name"] })
|
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "value",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "cat".to_owned()
|
|
|
|
}])
|
2020-05-25 18:14:17 +00:00
|
|
|
}
|
|
|
|
],
|
|
|
|
contents: None,
|
|
|
|
else_contents: None
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_helper_with_explicit_context() {
|
|
|
|
assert_eq!(
|
|
|
|
dust_tag(r#"{@eq:foo.bar key=name value="cat"}Pet the {name}!{/eq}"#),
|
|
|
|
Ok((
|
|
|
|
"",
|
|
|
|
DustTag::DTHelperEquals(ParameterizedBlock {
|
2020-05-26 01:17:58 +00:00
|
|
|
path: Path { keys: vec!["eq"] },
|
2020-05-25 18:14:17 +00:00
|
|
|
explicit_context: Some(Path {
|
|
|
|
keys: vec!["foo", "bar"]
|
|
|
|
}),
|
|
|
|
params: vec![
|
|
|
|
KVPair {
|
|
|
|
key: "key",
|
|
|
|
value: RValue::RVPath(Path { keys: vec!["name"] })
|
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "value",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "cat".to_owned()
|
|
|
|
}])
|
2020-05-25 18:14:17 +00:00
|
|
|
}
|
|
|
|
],
|
|
|
|
contents: Some(Body {
|
|
|
|
elements: vec![
|
|
|
|
TemplateElement::TESpan(Span {
|
|
|
|
contents: "Pet the "
|
|
|
|
}),
|
|
|
|
TemplateElement::TETag(DustTag::DTReference(Reference {
|
|
|
|
path: Path { keys: vec!["name"] },
|
|
|
|
filters: Vec::new()
|
|
|
|
})),
|
|
|
|
TemplateElement::TESpan(Span { contents: "!" })
|
|
|
|
]
|
|
|
|
}),
|
|
|
|
else_contents: None
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_self_closing_helper_with_explicit_context() {
|
|
|
|
assert_eq!(
|
|
|
|
dust_tag(r#"{@eq:foo.bar key=name value="cat"/}"#),
|
|
|
|
Ok((
|
|
|
|
"",
|
|
|
|
DustTag::DTHelperEquals(ParameterizedBlock {
|
2020-05-26 01:17:58 +00:00
|
|
|
path: Path { keys: vec!["eq"] },
|
2020-05-25 18:14:17 +00:00
|
|
|
explicit_context: Some(Path {
|
|
|
|
keys: vec!["foo", "bar"]
|
|
|
|
}),
|
2020-04-08 01:49:28 +00:00
|
|
|
params: vec![
|
|
|
|
KVPair {
|
|
|
|
key: "key",
|
|
|
|
value: RValue::RVPath(Path { keys: vec!["name"] })
|
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "value",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "cat".to_owned()
|
|
|
|
}])
|
2020-04-08 01:49:28 +00:00
|
|
|
}
|
|
|
|
],
|
|
|
|
contents: None,
|
|
|
|
else_contents: None
|
|
|
|
})
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
2020-05-03 17:07:27 +00:00
|
|
|
|
|
|
|
#[test]
|
2020-05-03 17:21:02 +00:00
|
|
|
fn test_full_document_new_line_equality() {
|
2020-05-03 17:07:27 +00:00
|
|
|
assert_eq!(
|
|
|
|
super::template(
|
|
|
|
"- simple -{~n}
|
|
|
|
{#names}{.}{/names}
|
|
|
|
{~n}- new lines -{~n}
|
|
|
|
{#names}
|
|
|
|
{.}
|
|
|
|
{/names}"
|
|
|
|
),
|
|
|
|
Ok::<_, nom::Err<(&str, ErrorKind)>>((
|
|
|
|
"",
|
|
|
|
Template {
|
|
|
|
contents: Body {
|
|
|
|
elements: vec![
|
|
|
|
TemplateElement::TESpan(Span {
|
2020-05-03 18:44:09 +00:00
|
|
|
contents: "- simple -"
|
2020-05-03 17:07:27 +00:00
|
|
|
}),
|
2020-05-03 17:21:02 +00:00
|
|
|
TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)),
|
2020-05-03 18:44:09 +00:00
|
|
|
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
|
|
|
|
"\n"
|
|
|
|
)),
|
2020-05-26 01:26:16 +00:00
|
|
|
TemplateElement::TETag(DustTag::DTSection(ParameterizedBlock {
|
2020-05-03 17:21:02 +00:00
|
|
|
path: Path {
|
|
|
|
keys: vec!["names"]
|
|
|
|
},
|
2020-05-25 17:38:31 +00:00
|
|
|
explicit_context: None,
|
2020-05-26 01:26:16 +00:00
|
|
|
params: Vec::new(),
|
2020-05-03 17:21:02 +00:00
|
|
|
contents: Some(Body {
|
|
|
|
elements: vec![TemplateElement::TETag(DustTag::DTReference(
|
|
|
|
Reference {
|
2020-05-24 03:12:51 +00:00
|
|
|
path: Path { keys: vec!["."] },
|
2020-05-03 17:21:02 +00:00
|
|
|
filters: vec![]
|
|
|
|
}
|
|
|
|
))]
|
|
|
|
}),
|
|
|
|
else_contents: None,
|
|
|
|
})),
|
2020-05-03 18:44:09 +00:00
|
|
|
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
|
|
|
|
"\n"
|
|
|
|
)),
|
2020-05-03 17:21:02 +00:00
|
|
|
TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)),
|
|
|
|
TemplateElement::TESpan(Span {
|
2020-05-03 18:44:09 +00:00
|
|
|
contents: "- new lines -"
|
2020-05-03 17:21:02 +00:00
|
|
|
}),
|
|
|
|
TemplateElement::TETag(DustTag::DTSpecial(Special::NewLine)),
|
2020-05-03 18:44:09 +00:00
|
|
|
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
|
|
|
|
"\n"
|
|
|
|
)),
|
2020-05-26 01:26:16 +00:00
|
|
|
TemplateElement::TETag(DustTag::DTSection(ParameterizedBlock {
|
2020-05-03 17:21:02 +00:00
|
|
|
path: Path {
|
|
|
|
keys: vec!["names"]
|
|
|
|
},
|
2020-05-25 17:38:31 +00:00
|
|
|
explicit_context: None,
|
2020-05-26 01:26:16 +00:00
|
|
|
params: Vec::new(),
|
2020-05-03 17:21:02 +00:00
|
|
|
contents: Some(Body {
|
2020-05-03 18:44:09 +00:00
|
|
|
elements: vec![
|
|
|
|
TemplateElement::TEIgnoredWhitespace(
|
|
|
|
IgnoredWhitespace::StartOfLine("\n")
|
|
|
|
),
|
|
|
|
TemplateElement::TETag(DustTag::DTReference(Reference {
|
2020-05-24 03:12:51 +00:00
|
|
|
path: Path { keys: vec!["."] },
|
2020-05-03 17:21:02 +00:00
|
|
|
filters: vec![]
|
2020-05-03 18:44:09 +00:00
|
|
|
})),
|
|
|
|
TemplateElement::TEIgnoredWhitespace(
|
|
|
|
IgnoredWhitespace::StartOfLine("\n")
|
|
|
|
)
|
|
|
|
]
|
2020-05-03 17:21:02 +00:00
|
|
|
}),
|
|
|
|
else_contents: None,
|
|
|
|
})),
|
2020-05-03 17:07:27 +00:00
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
2020-05-09 19:02:54 +00:00
|
|
|
|
|
|
|
#[test]
|
2020-05-09 19:11:04 +00:00
|
|
|
fn test_full_document_parameterized_partial() {
|
2020-05-09 19:02:54 +00:00
|
|
|
assert_eq!(
|
2020-05-09 19:15:43 +00:00
|
|
|
super::template(
|
|
|
|
r#"{#level3.level4}{>partialtwo v1="b" v2="b" v3="b" v4="b" v5="b" /}{/level3.level4}"#
|
|
|
|
),
|
2020-05-09 19:02:54 +00:00
|
|
|
Ok::<_, nom::Err<(&str, ErrorKind)>>((
|
|
|
|
"",
|
|
|
|
Template {
|
2020-05-09 19:05:29 +00:00
|
|
|
contents: Body {
|
2020-05-26 01:26:16 +00:00
|
|
|
elements: vec![TemplateElement::TETag(DustTag::DTSection(
|
|
|
|
ParameterizedBlock {
|
|
|
|
path: Path {
|
|
|
|
keys: vec!["level3", "level4"]
|
|
|
|
},
|
|
|
|
explicit_context: None,
|
|
|
|
params: Vec::new(),
|
|
|
|
contents: Some(Body {
|
|
|
|
elements: vec![TemplateElement::TETag(DustTag::DTPartial(
|
|
|
|
Partial {
|
|
|
|
name: vec![PartialNameElement::PNSpan {
|
|
|
|
contents: "partialtwo".to_owned()
|
|
|
|
},],
|
|
|
|
explicit_context: None,
|
|
|
|
params: vec![
|
|
|
|
KVPair {
|
|
|
|
key: "v1",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![
|
|
|
|
PartialNameElement::PNSpan {
|
|
|
|
contents: "b".to_owned()
|
|
|
|
}
|
|
|
|
])
|
2020-05-26 01:26:16 +00:00
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "v2",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![
|
|
|
|
PartialNameElement::PNSpan {
|
|
|
|
contents: "b".to_owned()
|
|
|
|
}
|
|
|
|
])
|
2020-05-26 01:26:16 +00:00
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "v3",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![
|
|
|
|
PartialNameElement::PNSpan {
|
|
|
|
contents: "b".to_owned()
|
|
|
|
}
|
|
|
|
])
|
2020-05-26 01:26:16 +00:00
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "v4",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![
|
|
|
|
PartialNameElement::PNSpan {
|
|
|
|
contents: "b".to_owned()
|
|
|
|
}
|
|
|
|
])
|
2020-05-26 01:26:16 +00:00
|
|
|
},
|
|
|
|
KVPair {
|
|
|
|
key: "v5",
|
2020-06-13 20:18:48 +00:00
|
|
|
value: RValue::RVTemplate(vec![
|
|
|
|
PartialNameElement::PNSpan {
|
|
|
|
contents: "b".to_owned()
|
|
|
|
}
|
|
|
|
])
|
2020-05-26 01:26:16 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
))]
|
|
|
|
}),
|
|
|
|
else_contents: None
|
|
|
|
}
|
|
|
|
))]
|
2020-05-09 19:05:29 +00:00
|
|
|
}
|
2020-05-09 19:02:54 +00:00
|
|
|
}
|
|
|
|
))
|
|
|
|
);
|
|
|
|
}
|
2020-04-05 02:45:56 +00:00
|
|
|
}
|