Merge branch 'explicit_context_priority' into render

This commit is contained in:
Tom Alexander 2020-05-30 14:43:23 -04:00
commit d07ac3dcc9
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
11 changed files with 538 additions and 336 deletions

View File

@ -1,3 +1,11 @@
Priority
--------
Partials: Explicit context takes priority over parameters
Sections: New context takes priority, then parameters, then explicit
Sections with loops: Loop variables ($idx and $len) take priority over parameters and explicit context
$idx and $len $idx and $len
------------- -------------
@ -6,7 +14,7 @@ $idx and $len do not survive through an explicit context setting, which will wor
You can use $idx and $len as your explicit context, but as scalar values I do not think there is a way to access them anyway. You can use $idx and $len as your explicit context, but as scalar values I do not think there is a way to access them anyway.
Exists and Not-Exists Exists and Not-Exists
===================== ---------------------
Looks like you can exlicitly set a context with exists and not-exists tags too. This works out well in the parser because I am using the same code for those blocks. Looks like you can exlicitly set a context with exists and not-exists tags too. This works out well in the parser because I am using the same code for those blocks.
@ -33,6 +41,8 @@ Explicitly setting a context on a block does work.
The explicit context for blocks is evaluated in the context for that template file containing the block, not the inline partial (so, whatever the context is when we invoke the partial containing the block). The explicit context for blocks is evaluated in the context for that template file containing the block, not the inline partial (so, whatever the context is when we invoke the partial containing the block).
Parameters on blocks and inline partials appear to not be read but they do not error out.
References References
---------- ----------

View File

@ -1,4 +1,9 @@
{ {
"another_idx": {
"$idx": 21,
"$len": 22,
"other": 23
},
"block": { "block": {
"message": { "message": {
"contents": "Explicit contexts are evaluated in the context of the block." "contents": "Explicit contexts are evaluated in the context of the block."
@ -14,6 +19,9 @@
"explicit": { "explicit": {
"pet": "cat" "pet": "cat"
}, },
"has_idx": {
"$idx": 14
},
"inline_partial": { "inline_partial": {
"message": { "message": {
"contents": "Explicit contexts are evaluated in the context of the inline partial." "contents": "Explicit contexts are evaluated in the context of the inline partial."

View File

@ -296,3 +296,120 @@ Explicit Evaluation Time Split Partial Context OVerride{~n}
{>explicit_evaluation_time_split_override/} {>explicit_evaluation_time_split_override/}
{/inline_partial} {/inline_partial}
{/partial_context} {/partial_context}
{! What happens with sections with explicit context and parameters !}
Section set $idx as a parameter{~n}
==============================={~n}
{#explicit $idx="7"}
$idx is {$idx}{~n}
$len is {$len}{~n}
{/explicit}
Section set $idx as a parameter and new context{~n}
==============================================={~n}
{#has_idx $idx="7"}
$idx is {$idx}{~n}
$len is {$len}{~n}
{/has_idx}
Section set $idx as a parameter, new context, and explicit context{~n}
=================================================================={~n}
{#has_idx:another_idx $idx="7" $len="8"}
$idx is {$idx}{~n}
$len is {$len}{~n}
other is {other}{~n}
{/has_idx}
Section vs Partial priority{~n}
==========================={~n}
{#some_global:explicit pet="snake"}
{pet}{~n}
{/some_global}
{>priority:explicit pet="snake"/}
Exists vs Partial priority{~n}
=========================={~n}
{?some_global:explicit pet="snake"}
{pet}{~n}
{/some_global}
{>priority:explicit pet="snake"/}
Section vs NotExists priority{~n}
=========================={~n}
{^some_global:explicit pet="snake"}
MAIN {pet}{~n}
{:else}
ELSE {pet}{~n}
{/some_global}
{>priority:explicit pet="snake"/}
Section vs Partial priority Failed Walk{~n}
======================================={~n}
{#doesnotexist:explicit pet="snake"}
MAIN {pet}{~n}
{:else}
ELSE {pet}{~n}
{/doesnotexist}
{>priority:explicit pet="snake"/}
Section Loop set $idx as a parameter{~n}
===================================={~n}
{#loop $idx="7"}
$idx is {$idx}{~n}
$len is {$len}{~n}
{/loop}
Section Loop set $idx in explicit context{~n}
========================================={~n}
{#loop:has_idx}
$idx is {$idx}{~n}
$len is {$len}{~n}
{/loop}
Section Loop set $idx in explicit context and parameter{~n}
======================================================={~n}
{#loop:has_idx $idx="7"}
$idx is {$idx}{~n}
$len is {$len}{~n}
{/loop}
{! Lets check the priority order for all the tag types now that we know that sections and partials have a different order !}
Section vs Partial priority{~n}
==========================={~n}
{#some_global:explicit pet="snake"}
{pet}{~n}
{/some_global}
{>priority:explicit pet="snake"/}
Exists vs Section priority{~n}
=========================={~n}
{?some_global:explicit pet="snake"}
{pet}{~n}
{/some_global}
{>priority:explicit pet="snake"/}
Not Exists vs Section priority{~n}
=============================={~n}
{^empty_array:explicit pet="snake"}
{pet}{~n}
{/empty_array}
{>priority:explicit pet="snake"/}
{! TODO: Add helpers once we have a helper that sets a variable !}
{! Do blocks or inline partials have parameters? !}
Do blocks or inline partials have parameters{~n}
============================================{~n}
{+some_block_override pet="snake" name="cuddlebunny"}
{pet}, {name}{~n}
{/some_block_override}
{<some_block_override pet="lizard" name="rover"}
{pet}, {name}{~n}
{/some_block_override}
Does exists have parameters{~n}
==========================={~n}
{?some_global pet="snake"}
{pet}{~n}
{/some_global}

View File

@ -0,0 +1 @@
{pet}{~n}

View File

@ -29,11 +29,11 @@ pub enum DustTag<'a> {
DTComment(Comment<'a>), DTComment(Comment<'a>),
DTLiteralStringBlock(&'a str), DTLiteralStringBlock(&'a str),
DTReference(Reference<'a>), DTReference(Reference<'a>),
DTSection(Container<'a>), DTSection(ParameterizedBlock<'a>),
DTExists(Container<'a>), DTExists(ParameterizedBlock<'a>),
DTNotExists(Container<'a>), DTNotExists(ParameterizedBlock<'a>),
DTBlock(NamedBlock<'a>), DTBlock(ParameterizedBlock<'a>),
DTInlinePartial(NamedBlock<'a>), DTInlinePartial(ParameterizedBlock<'a>),
DTPartial(Partial<'a>), DTPartial(Partial<'a>),
DTHelperEquals(ParameterizedBlock<'a>), DTHelperEquals(ParameterizedBlock<'a>),
DTHelperNotEquals(ParameterizedBlock<'a>), DTHelperNotEquals(ParameterizedBlock<'a>),
@ -92,24 +92,9 @@ pub struct Span<'a> {
pub contents: &'a str, pub contents: &'a str,
} }
#[derive(Clone, Debug, PartialEq)]
pub struct Container<'a> {
pub path: Path<'a>,
pub explicit_context: Option<Path<'a>>,
pub contents: Option<Body<'a>>,
pub else_contents: Option<Body<'a>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct NamedBlock<'a> {
pub name: &'a str,
pub explicit_context: Option<Path<'a>>,
pub contents: Option<Body<'a>>,
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct ParameterizedBlock<'a> { pub struct ParameterizedBlock<'a> {
pub name: &'a str, pub path: Path<'a>,
pub explicit_context: Option<Path<'a>>, pub explicit_context: Option<Path<'a>>,
pub params: Vec<KVPair<'a>>, pub params: Vec<KVPair<'a>>,
pub contents: Option<Body<'a>>, pub contents: Option<Body<'a>>,
@ -218,18 +203,42 @@ fn dust_tag(i: &str) -> IResult<&str, DustTag> {
map(comment, DustTag::DTComment), map(comment, DustTag::DTComment),
map(literal_string_block, DustTag::DTLiteralStringBlock), map(literal_string_block, DustTag::DTLiteralStringBlock),
map(reference, DustTag::DTReference), map(reference, DustTag::DTReference),
conditional("{#", DustTag::DTSection), map(parameterized_block("{#", path), DustTag::DTSection),
conditional("{?", DustTag::DTExists), map(parameterized_block("{?", path), DustTag::DTExists),
conditional("{^", DustTag::DTNotExists), map(parameterized_block("{^", path), DustTag::DTNotExists),
named_block("{+", DustTag::DTBlock), map(
named_block("{<", DustTag::DTInlinePartial), parameterized_block_without_else("{+", key_to_path),
DustTag::DTBlock,
),
map(
parameterized_block_without_else("{<", key_to_path),
DustTag::DTInlinePartial,
),
partial("{>", DustTag::DTPartial), partial("{>", DustTag::DTPartial),
parameterized_block("{@", "gte", DustTag::DTHelperGreaterThanOrEquals), map(
parameterized_block("{@", "lte", DustTag::DTHelperLessThanOrEquals), parameterized_block("{@", &tag_to_path("gte")),
parameterized_block("{@", "eq", DustTag::DTHelperEquals), DustTag::DTHelperGreaterThanOrEquals,
parameterized_block("{@", "ne", DustTag::DTHelperNotEquals), ),
parameterized_block("{@", "gt", DustTag::DTHelperGreaterThan), map(
parameterized_block("{@", "lt", DustTag::DTHelperLessThan), parameterized_block("{@", &tag_to_path("lte")),
DustTag::DTHelperLessThanOrEquals,
),
map(
parameterized_block("{@", &tag_to_path("eq")),
DustTag::DTHelperEquals,
),
map(
parameterized_block("{@", &tag_to_path("ne")),
DustTag::DTHelperNotEquals,
),
map(
parameterized_block("{@", &tag_to_path("gt")),
DustTag::DTHelperGreaterThan,
),
map(
parameterized_block("{@", &tag_to_path("lt")),
DustTag::DTHelperLessThan,
),
))(i) ))(i)
} }
@ -280,6 +289,14 @@ fn path(i: &str) -> IResult<&str, Path> {
))(i) ))(i)
} }
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)
}
/// Just digits, no signs or decimals /// Just digits, no signs or decimals
fn postitive_integer_literal(i: &str) -> IResult<&str, u64> { fn postitive_integer_literal(i: &str) -> IResult<&str, u64> {
map( map(
@ -324,208 +341,125 @@ fn reference(i: &str) -> IResult<&str, Reference> {
)) ))
} }
fn conditional<'a, F>(
open_matcher: &'static str,
constructor: F,
) -> impl FnMut(&'a str) -> IResult<&'a str, DustTag<'a>>
where
F: Copy + Fn(Container<'a>) -> DustTag<'a>,
{
alt((
conditional_with_body(open_matcher, constructor),
self_closing_conditional(open_matcher, constructor),
))
}
fn conditional_with_body<'a, F>(
open_matcher: &'static str,
constructor: F,
) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>>
where
F: Fn(Container<'a>) -> DustTag<'a>,
{
move |i: &'a str| {
let (i, (opening_name, maybe_explicit_context, inner, maybe_else, _closing_name)) =
verify(
tuple((
preceded(tag(open_matcher), path),
terminated(opt(preceded(tag(":"), path)), tag("}")),
opt(body),
opt(preceded(tag("{:else}"), opt(body))),
delimited(tag("{/"), path, tag("}")),
)),
|(open, _maybe_explicit, _inn, _maybe_else, close)| open == close,
)(i)?;
Ok((
i,
constructor(Container {
path: opening_name,
explicit_context: maybe_explicit_context,
contents: inner,
else_contents: maybe_else.flatten(),
}),
))
}
}
fn self_closing_conditional<'a, F>(
open_matcher: &'static str,
constructor: F,
) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>>
where
F: Fn(Container<'a>) -> DustTag<'a>,
{
move |i: &'a str| {
let (i, (opening_name, maybe_explicit_context)) = tuple((
preceded(tag(open_matcher), path),
terminated(opt(preceded(tag(":"), path)), tag("/}")),
))(i)?;
Ok((
i,
constructor(Container {
path: opening_name,
explicit_context: maybe_explicit_context,
contents: None,
else_contents: None,
}),
))
}
}
fn named_block<'a, F>(
open_matcher: &'static str,
constructor: F,
) -> impl FnMut(&'a str) -> IResult<&'a str, DustTag<'a>>
where
F: Copy + Fn(NamedBlock<'a>) -> DustTag<'a>,
{
alt((
named_block_with_body(open_matcher, constructor),
self_closing_named_block(open_matcher, constructor),
))
}
fn named_block_with_body<'a, F>(
open_matcher: &'static str,
constructor: F,
) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>>
where
F: Fn(NamedBlock<'a>) -> DustTag<'a>,
{
move |i: &'a str| {
let (i, (opening_name, maybe_explicit_context, inner, _closing_name)) = verify(
tuple((
preceded(tag(open_matcher), key),
terminated(opt(preceded(tag(":"), path)), tag("}")),
opt(body),
delimited(tag("{/"), key, tag("}")),
)),
|(open, _maybe_explicit, _inn, close)| open == close,
)(i)?;
Ok((
i,
constructor(NamedBlock {
name: opening_name,
explicit_context: maybe_explicit_context,
contents: inner,
}),
))
}
}
fn self_closing_named_block<'a, F>(
open_matcher: &'static str,
constructor: F,
) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>>
where
F: Fn(NamedBlock<'a>) -> DustTag<'a>,
{
move |i: &'a str| {
let (i, (opening_name, maybe_explicit_context)) = tuple((
preceded(tag(open_matcher), key),
terminated(opt(preceded(tag(":"), path)), tag("/}")),
))(i)?;
Ok((
i,
constructor(NamedBlock {
name: opening_name,
explicit_context: maybe_explicit_context,
contents: None,
}),
))
}
}
fn parameterized_block<'a, F>( fn parameterized_block<'a, F>(
open_matcher: &'static str, open_matcher: &'static str,
tag_name: &'static str, name_matcher: F,
constructor: F, ) -> impl FnMut(&'a str) -> IResult<&'a str, ParameterizedBlock<'a>>
) -> impl FnMut(&'a str) -> IResult<&'a str, DustTag<'a>>
where where
F: Copy + Fn(ParameterizedBlock<'a>) -> DustTag<'a>, F: Copy + Fn(&'a str) -> IResult<&'a str, Path<'a>>,
{ {
alt(( alt((
parameterized_block_with_body(open_matcher, tag_name, constructor), parameterized_block_with_body(open_matcher, name_matcher),
parameterized_self_closing_block(open_matcher, tag_name, constructor), parameterized_self_closing_block(open_matcher, name_matcher),
))
}
fn parameterized_block_without_else<'a, F>(
open_matcher: &'static str,
name_matcher: F,
) -> impl FnMut(&'a str) -> IResult<&'a str, ParameterizedBlock<'a>>
where
F: Copy + Fn(&'a str) -> IResult<&'a str, Path<'a>>,
{
alt((
parameterized_block_with_body_without_else(open_matcher, name_matcher),
parameterized_self_closing_block(open_matcher, name_matcher),
)) ))
} }
fn parameterized_block_with_body<'a, F>( fn parameterized_block_with_body<'a, F>(
open_matcher: &'static str, open_matcher: &'static str,
tag_name: &'static str, name_matcher: F,
constructor: F, ) -> impl Fn(&'a str) -> IResult<&'a str, ParameterizedBlock<'a>>
) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>>
where where
F: Fn(ParameterizedBlock<'a>) -> DustTag<'a>, F: Copy + Fn(&'a str) -> IResult<&'a str, Path<'a>>,
{ {
move |i: &'a str| { move |i: &'a str| {
let (i, (name, maybe_explicit_context, params, inner, maybe_else, _closing_name)) = let (i, (opening_name, maybe_explicit_context, params, inner, maybe_else, _closing_name)) =
tuple(( verify(
preceded(tag(open_matcher), tag(tag_name)), tuple((
opt(preceded(tag(":"), path)), preceded(tag(open_matcher), name_matcher),
terminated( opt(preceded(tag(":"), path)),
opt(delimited( terminated(
space1, opt(delimited(
separated_list1(space1, key_value_pair), space1,
space0, separated_list1(space1, key_value_pair),
)), space0,
tag("}"), )),
), tag("}"),
opt(body), ),
opt(preceded(tag("{:else}"), opt(body))), opt(body),
delimited(tag("{/"), tag(tag_name), tag("}")), opt(preceded(tag("{:else}"), opt(body))),
))(i)?; delimited(tag("{/"), name_matcher, tag("}")),
)),
|(open, _maybe_explicit, _params, _inn, _maybe_else, close)| open == close,
)(i)?;
Ok(( Ok((
i, i,
constructor(ParameterizedBlock { ParameterizedBlock {
name: name, path: opening_name,
explicit_context: maybe_explicit_context, explicit_context: maybe_explicit_context,
params: params.unwrap_or(Vec::new()), params: params.unwrap_or(Vec::new()),
contents: inner, contents: inner,
else_contents: maybe_else.flatten(), else_contents: maybe_else.flatten(),
}), },
))
}
}
fn parameterized_block_with_body_without_else<'a, F>(
open_matcher: &'static str,
name_matcher: F,
) -> impl Fn(&'a str) -> IResult<&'a str, ParameterizedBlock<'a>>
where
F: Copy + Fn(&'a str) -> IResult<&'a str, Path<'a>>,
{
move |i: &'a str| {
let (i, (opening_name, maybe_explicit_context, params, inner, _closing_name)) =
verify(
tuple((
preceded(tag(open_matcher), name_matcher),
opt(preceded(tag(":"), path)),
terminated(
opt(delimited(
space1,
separated_list1(space1, key_value_pair),
space0,
)),
tag("}"),
),
opt(body),
delimited(tag("{/"), name_matcher, tag("}")),
)),
|(open, _maybe_explicit, _params, _inn, close)| open == close,
)(i)?;
Ok((
i,
ParameterizedBlock {
path: opening_name,
explicit_context: maybe_explicit_context,
params: params.unwrap_or(Vec::new()),
contents: inner,
else_contents: None,
},
)) ))
} }
} }
fn parameterized_self_closing_block<'a, F>( fn parameterized_self_closing_block<'a, F>(
open_matcher: &'static str, open_matcher: &'static str,
tag_name: &'static str, name_matcher: F,
constructor: F, ) -> impl Fn(&'a str) -> IResult<&'a str, ParameterizedBlock<'a>>
) -> impl Fn(&'a str) -> IResult<&'a str, DustTag<'a>>
where where
F: Fn(ParameterizedBlock<'a>) -> DustTag<'a>, F: Copy + Fn(&'a str) -> IResult<&'a str, Path<'a>>,
{ {
move |i: &'a str| { move |i: &'a str| {
let (i, (name, maybe_explicit_context, params)) = delimited( let (i, (opening_name, maybe_explicit_context, params)) = delimited(
tag(open_matcher), tag(open_matcher),
tuple(( tuple((
tag(tag_name), name_matcher,
opt(preceded(tag(":"), path)), opt(preceded(tag(":"), path)),
opt(delimited( opt(delimited(
space1, space1,
@ -538,13 +472,13 @@ where
Ok(( Ok((
i, i,
constructor(ParameterizedBlock { ParameterizedBlock {
name: name, path: opening_name,
explicit_context: maybe_explicit_context, explicit_context: maybe_explicit_context,
params: params.unwrap_or(Vec::new()), params: params.unwrap_or(Vec::new()),
contents: None, contents: None,
else_contents: None, else_contents: None,
}), },
)) ))
} }
} }
@ -894,11 +828,12 @@ mod tests {
super::dust_tag("{#foo.bar}{/foo.bar}"), super::dust_tag("{#foo.bar}{/foo.bar}"),
Ok(( Ok((
"", "",
DustTag::DTSection(Container { DustTag::DTSection(ParameterizedBlock {
path: Path { path: Path {
keys: vec!["foo", "bar"] keys: vec!["foo", "bar"]
}, },
explicit_context: None, explicit_context: None,
params: Vec::new(),
contents: None, contents: None,
else_contents: None, else_contents: None,
}) })
@ -912,11 +847,12 @@ mod tests {
super::dust_tag("{#foo.bar/}"), super::dust_tag("{#foo.bar/}"),
Ok(( Ok((
"", "",
DustTag::DTSection(Container { DustTag::DTSection(ParameterizedBlock {
path: Path { path: Path {
keys: vec!["foo", "bar"] keys: vec!["foo", "bar"]
}, },
explicit_context: None, explicit_context: None,
params: Vec::new(),
contents: None, contents: None,
else_contents: None, else_contents: None,
}) })
@ -930,11 +866,12 @@ mod tests {
super::dust_tag("{#foo.bar}hello {name}{/foo.bar}"), super::dust_tag("{#foo.bar}hello {name}{/foo.bar}"),
Ok(( Ok((
"", "",
DustTag::DTSection(Container { DustTag::DTSection(ParameterizedBlock {
path: Path { path: Path {
keys: vec!["foo", "bar"] keys: vec!["foo", "bar"]
}, },
explicit_context: None, explicit_context: None,
params: Vec::new(),
contents: Some(Body { contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { contents: "hello " }), TemplateElement::TESpan(Span { contents: "hello " }),
@ -956,11 +893,12 @@ mod tests {
super::dust_tag("{#greeting}hello {name}{:else}goodbye {name}{/greeting}"), super::dust_tag("{#greeting}hello {name}{:else}goodbye {name}{/greeting}"),
Ok(( Ok((
"", "",
DustTag::DTSection(Container { DustTag::DTSection(ParameterizedBlock {
path: Path { path: Path {
keys: vec!["greeting"] keys: vec!["greeting"]
}, },
explicit_context: None, explicit_context: None,
params: Vec::new(),
contents: Some(Body { contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { contents: "hello " }), TemplateElement::TESpan(Span { contents: "hello " }),
@ -992,13 +930,14 @@ mod tests {
super::dust_tag("{#foo.bar:baz.ipsum}{/foo.bar}"), super::dust_tag("{#foo.bar:baz.ipsum}{/foo.bar}"),
Ok(( Ok((
"", "",
DustTag::DTSection(Container { DustTag::DTSection(ParameterizedBlock {
path: Path { path: Path {
keys: vec!["foo", "bar"] keys: vec!["foo", "bar"]
}, },
explicit_context: Some(Path { explicit_context: Some(Path {
keys: vec!["baz", "ipsum"] keys: vec!["baz", "ipsum"]
}), }),
params: Vec::new(),
contents: None, contents: None,
else_contents: None, else_contents: None,
}) })
@ -1012,11 +951,12 @@ mod tests {
super::dust_tag("{#foo.bar:$idx/}"), super::dust_tag("{#foo.bar:$idx/}"),
Ok(( Ok((
"", "",
DustTag::DTSection(Container { DustTag::DTSection(ParameterizedBlock {
path: Path { path: Path {
keys: vec!["foo", "bar"] keys: vec!["foo", "bar"]
}, },
explicit_context: Some(Path { keys: vec!["$idx"] }), explicit_context: Some(Path { keys: vec!["$idx"] }),
params: Vec::new(),
contents: None, contents: None,
else_contents: None, else_contents: None,
}) })
@ -1030,10 +970,12 @@ mod tests {
super::dust_tag("{+foo/}"), super::dust_tag("{+foo/}"),
Ok(( Ok((
"", "",
DustTag::DTBlock(NamedBlock { DustTag::DTBlock(ParameterizedBlock {
name: "foo", path: Path { keys: vec!["foo"] },
explicit_context: None, explicit_context: None,
contents: None params: Vec::new(),
contents: None,
else_contents: None
}) })
)) ))
); );
@ -1045,9 +987,10 @@ mod tests {
super::dust_tag("{+foo}hello {name}{/foo}"), super::dust_tag("{+foo}hello {name}{/foo}"),
Ok(( Ok((
"", "",
DustTag::DTBlock(NamedBlock { DustTag::DTBlock(ParameterizedBlock {
name: "foo", path: Path { keys: vec!["foo"] },
explicit_context: None, explicit_context: None,
params: Vec::new(),
contents: Some(Body { contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { contents: "hello " }), TemplateElement::TESpan(Span { contents: "hello " }),
@ -1056,7 +999,8 @@ mod tests {
filters: Vec::new() filters: Vec::new()
})) }))
] ]
}) }),
else_contents: None
}) })
)) ))
); );
@ -1068,12 +1012,14 @@ mod tests {
super::dust_tag("{+foo:bar.baz/}"), super::dust_tag("{+foo:bar.baz/}"),
Ok(( Ok((
"", "",
DustTag::DTBlock(NamedBlock { DustTag::DTBlock(ParameterizedBlock {
name: "foo", path: Path { keys: vec!["foo"] },
explicit_context: Some(Path { explicit_context: Some(Path {
keys: vec!["bar", "baz"] keys: vec!["bar", "baz"]
}), }),
contents: None params: Vec::new(),
contents: None,
else_contents: None
}) })
)) ))
); );
@ -1085,11 +1031,12 @@ mod tests {
super::dust_tag("{+foo:bar.baz}hello {name}{/foo}"), super::dust_tag("{+foo:bar.baz}hello {name}{/foo}"),
Ok(( Ok((
"", "",
DustTag::DTBlock(NamedBlock { DustTag::DTBlock(ParameterizedBlock {
name: "foo", path: Path { keys: vec!["foo"] },
explicit_context: Some(Path { explicit_context: Some(Path {
keys: vec!["bar", "baz"] keys: vec!["bar", "baz"]
}), }),
params: Vec::new(),
contents: Some(Body { contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { contents: "hello " }), TemplateElement::TESpan(Span { contents: "hello " }),
@ -1098,7 +1045,8 @@ mod tests {
filters: Vec::new() filters: Vec::new()
})) }))
] ]
}) }),
else_contents: None
}) })
)) ))
); );
@ -1110,10 +1058,12 @@ mod tests {
super::dust_tag("{<foo/}"), super::dust_tag("{<foo/}"),
Ok(( Ok((
"", "",
DustTag::DTInlinePartial(NamedBlock { DustTag::DTInlinePartial(ParameterizedBlock {
name: "foo", path: Path { keys: vec!["foo"] },
explicit_context: None, explicit_context: None,
contents: None params: Vec::new(),
contents: None,
else_contents: None
}) })
)) ))
); );
@ -1125,9 +1075,10 @@ mod tests {
super::dust_tag("{<foo}hello {name}{/foo}"), super::dust_tag("{<foo}hello {name}{/foo}"),
Ok(( Ok((
"", "",
DustTag::DTInlinePartial(NamedBlock { DustTag::DTInlinePartial(ParameterizedBlock {
name: "foo", path: Path { keys: vec!["foo"] },
explicit_context: None, explicit_context: None,
params: Vec::new(),
contents: Some(Body { contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TESpan(Span { contents: "hello " }), TemplateElement::TESpan(Span { contents: "hello " }),
@ -1136,7 +1087,8 @@ mod tests {
filters: Vec::new() filters: Vec::new()
})) }))
] ]
}) }),
else_contents: None
}) })
)) ))
); );
@ -1328,7 +1280,7 @@ mod tests {
Ok(( Ok((
"", "",
DustTag::DTHelperEquals(ParameterizedBlock { DustTag::DTHelperEquals(ParameterizedBlock {
name: "eq", path: Path { keys: vec!["eq"] },
explicit_context: None, explicit_context: None,
params: vec![ params: vec![
KVPair { KVPair {
@ -1365,7 +1317,7 @@ mod tests {
Ok(( Ok((
"", "",
DustTag::DTHelperEquals(ParameterizedBlock { DustTag::DTHelperEquals(ParameterizedBlock {
name: "eq", path: Path { keys: vec!["eq"] },
explicit_context: None, explicit_context: None,
params: vec![ params: vec![
KVPair { KVPair {
@ -1391,7 +1343,7 @@ mod tests {
Ok(( Ok((
"", "",
DustTag::DTHelperEquals(ParameterizedBlock { DustTag::DTHelperEquals(ParameterizedBlock {
name: "eq", path: Path { keys: vec!["eq"] },
explicit_context: Some(Path { explicit_context: Some(Path {
keys: vec!["foo", "bar"] keys: vec!["foo", "bar"]
}), }),
@ -1430,7 +1382,7 @@ mod tests {
Ok(( Ok((
"", "",
DustTag::DTHelperEquals(ParameterizedBlock { DustTag::DTHelperEquals(ParameterizedBlock {
name: "eq", path: Path { keys: vec!["eq"] },
explicit_context: Some(Path { explicit_context: Some(Path {
keys: vec!["foo", "bar"] keys: vec!["foo", "bar"]
}), }),
@ -1474,11 +1426,12 @@ mod tests {
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine( TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n" "\n"
)), )),
TemplateElement::TETag(DustTag::DTSection(Container { TemplateElement::TETag(DustTag::DTSection(ParameterizedBlock {
path: Path { path: Path {
keys: vec!["names"] keys: vec!["names"]
}, },
explicit_context: None, explicit_context: None,
params: Vec::new(),
contents: Some(Body { contents: Some(Body {
elements: vec![TemplateElement::TETag(DustTag::DTReference( elements: vec![TemplateElement::TETag(DustTag::DTReference(
Reference { Reference {
@ -1500,11 +1453,12 @@ mod tests {
TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine( TemplateElement::TEIgnoredWhitespace(IgnoredWhitespace::StartOfLine(
"\n" "\n"
)), )),
TemplateElement::TETag(DustTag::DTSection(Container { TemplateElement::TETag(DustTag::DTSection(ParameterizedBlock {
path: Path { path: Path {
keys: vec!["names"] keys: vec!["names"]
}, },
explicit_context: None, explicit_context: None,
params: Vec::new(),
contents: Some(Body { contents: Some(Body {
elements: vec![ elements: vec![
TemplateElement::TEIgnoredWhitespace( TemplateElement::TEIgnoredWhitespace(
@ -1538,55 +1492,58 @@ mod tests {
"", "",
Template { Template {
contents: Body { contents: Body {
elements: vec![TemplateElement::TETag(DustTag::DTSection(Container { elements: vec![TemplateElement::TETag(DustTag::DTSection(
path: Path { ParameterizedBlock {
keys: vec!["level3", "level4"] path: Path {
}, keys: vec!["level3", "level4"]
explicit_context: None, },
contents: Some(Body { explicit_context: None,
elements: vec![TemplateElement::TETag(DustTag::DTPartial( params: Vec::new(),
Partial { contents: Some(Body {
name: vec![PartialNameElement::PNSpan { elements: vec![TemplateElement::TETag(DustTag::DTPartial(
contents: "partialtwo".to_owned() Partial {
},], name: vec![PartialNameElement::PNSpan {
explicit_context: None, contents: "partialtwo".to_owned()
params: vec![ },],
KVPair { explicit_context: None,
key: "v1", params: vec![
value: RValue::RVLiteral(OwnedLiteral::LString( KVPair {
"b".to_owned() key: "v1",
)) value: RValue::RVLiteral(
}, OwnedLiteral::LString("b".to_owned())
KVPair { )
key: "v2", },
value: RValue::RVLiteral(OwnedLiteral::LString( KVPair {
"b".to_owned() key: "v2",
)) value: RValue::RVLiteral(
}, OwnedLiteral::LString("b".to_owned())
KVPair { )
key: "v3", },
value: RValue::RVLiteral(OwnedLiteral::LString( KVPair {
"b".to_owned() key: "v3",
)) value: RValue::RVLiteral(
}, OwnedLiteral::LString("b".to_owned())
KVPair { )
key: "v4", },
value: RValue::RVLiteral(OwnedLiteral::LString( KVPair {
"b".to_owned() key: "v4",
)) value: RValue::RVLiteral(
}, OwnedLiteral::LString("b".to_owned())
KVPair { )
key: "v5", },
value: RValue::RVLiteral(OwnedLiteral::LString( KVPair {
"b".to_owned() key: "v5",
)) value: RValue::RVLiteral(
} OwnedLiteral::LString("b".to_owned())
] )
} }
))] ]
}), }
else_contents: None ))]
}))] }),
else_contents: None
}
))]
} }
} }
)) ))

View File

@ -21,6 +21,16 @@ pub trait Truthiness {
pub trait Walkable { pub trait Walkable {
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError>; fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError>;
/// If an element contains meta information and should not be
/// returned as the final result of a walk, this function should
/// return true.
///
/// For example, the iteration context contains $idx and $len but
/// it should not be the result of a dot-reference like `{.}`.
fn is_pseudo_element(&self) -> bool {
false
}
} }
pub trait Renderable { pub trait Renderable {

View File

@ -97,7 +97,7 @@ fn extract_inline_partials_from_tag<'a, 'b>(
} }
DustTag::DTPartial(..) => (), DustTag::DTPartial(..) => (),
DustTag::DTInlinePartial(named_block) => { DustTag::DTInlinePartial(named_block) => {
blocks.insert(&named_block.name, &named_block.contents); blocks.insert(&named_block.path.keys[0], &named_block.contents);
} }
DustTag::DTBlock(..) => (), DustTag::DTBlock(..) => (),
DustTag::DTHelperEquals(parameterized_block) => { DustTag::DTHelperEquals(parameterized_block) => {

View File

@ -68,6 +68,10 @@ impl Walkable for IterationContext {
_ => Err(WalkError::CantWalk), _ => Err(WalkError::CantWalk),
} }
} }
fn is_pseudo_element(&self) -> bool {
true
}
} }
impl CompareContextElement for IterationContext { impl CompareContextElement for IterationContext {

View File

@ -103,6 +103,10 @@ impl Walkable for ParametersContext {
OwnedRValue::RVLiteral(literal) => Ok(literal), OwnedRValue::RVLiteral(literal) => Ok(literal),
} }
} }
fn is_pseudo_element(&self) -> bool {
true
}
} }
impl Clone for ParametersContext { impl Clone for ParametersContext {

View File

@ -17,6 +17,7 @@ use crate::renderer::inline_partial_tree::InlinePartialTreeElement;
use crate::renderer::iteration_context::IterationContext; use crate::renderer::iteration_context::IterationContext;
use crate::renderer::parameters_context::ParametersContext; use crate::renderer::parameters_context::ParametersContext;
use crate::renderer::walking::walk_path; use crate::renderer::walking::walk_path;
use std::borrow::Borrow;
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -165,13 +166,14 @@ impl<'a> DustRenderer<'a> {
} }
} }
DustTag::DTSection(container) => { DustTag::DTSection(container) => {
let injected_context = ParametersContext::new(breadcrumbs, &container.params);
let val = walk_path(breadcrumbs, &container.path.keys); let val = walk_path(breadcrumbs, &container.path.keys);
match val { match val {
Err(WalkError::CantWalk) => { Err(WalkError::CantWalk) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_section(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
Some(&injected_context),
&container.explicit_context, &container.explicit_context,
None, None,
); );
@ -194,10 +196,10 @@ impl<'a> DustRenderer<'a> {
final_val.get_loop_elements(); final_val.get_loop_elements();
if loop_elements.is_empty() { if loop_elements.is_empty() {
// Scalar value // Scalar value
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_section(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
Some(&injected_context),
&container.explicit_context, &container.explicit_context,
Some(final_val), Some(final_val),
); );
@ -214,15 +216,16 @@ impl<'a> DustRenderer<'a> {
.into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(i, array_elem)| { .map(|(i, array_elem)| {
let injected_context = let index_context =
IterationContext::new(i, total_length); IterationContext::new(i, total_length);
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs =
breadcrumbs, Self::new_breadcrumbs_section(
breadcrumbs, breadcrumbs,
Some(&injected_context), Some(&index_context),
&container.explicit_context, Some(&injected_context),
Some(array_elem), &container.explicit_context,
); Some(array_elem),
);
self.render_body( self.render_body(
&body, &body,
new_breadcrumbs new_breadcrumbs
@ -242,10 +245,10 @@ impl<'a> DustRenderer<'a> {
// an empty array or null), Dust uses the // an empty array or null), Dust uses the
// original context before walking the path as // original context before walking the path as
// the context for rendering the else block // the context for rendering the else block
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_section(
breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
Some(&injected_context),
&container.explicit_context, &container.explicit_context,
None, None,
); );
@ -259,12 +262,11 @@ impl<'a> DustRenderer<'a> {
} }
} }
DustTag::DTExists(container) => { DustTag::DTExists(container) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_partial(
breadcrumbs, breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&container.explicit_context, &container.explicit_context,
None,
); );
let val = walk_path(breadcrumbs, &container.path.keys); let val = walk_path(breadcrumbs, &container.path.keys);
return if val.map(|v| v.is_truthy()).unwrap_or(false) { return if val.map(|v| v.is_truthy()).unwrap_or(false) {
@ -282,12 +284,11 @@ impl<'a> DustRenderer<'a> {
}; };
} }
DustTag::DTNotExists(container) => { DustTag::DTNotExists(container) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_partial(
breadcrumbs, breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&container.explicit_context, &container.explicit_context,
None,
); );
let val = walk_path(breadcrumbs, &container.path.keys); let val = walk_path(breadcrumbs, &container.path.keys);
return if !val.map(|v| v.is_truthy()).unwrap_or(false) { return if !val.map(|v| v.is_truthy()).unwrap_or(false) {
@ -307,12 +308,11 @@ impl<'a> DustRenderer<'a> {
DustTag::DTPartial(partial) => { DustTag::DTPartial(partial) => {
let partial_name = self.render_partial_name(&partial.name, breadcrumbs, blocks)?; let partial_name = self.render_partial_name(&partial.name, breadcrumbs, blocks)?;
if partial.params.is_empty() { if partial.params.is_empty() {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_partial(
breadcrumbs, breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&partial.explicit_context, &partial.explicit_context,
None,
); );
let rendered_content = self.render_template( let rendered_content = self.render_template(
&partial_name, &partial_name,
@ -322,12 +322,11 @@ impl<'a> DustRenderer<'a> {
return Ok(rendered_content); return Ok(rendered_content);
} else { } else {
let injected_context = ParametersContext::new(breadcrumbs, &partial.params); let injected_context = ParametersContext::new(breadcrumbs, &partial.params);
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_partial(
breadcrumbs, breadcrumbs,
breadcrumbs, breadcrumbs,
Some(&injected_context), Some(&injected_context),
&partial.explicit_context, &partial.explicit_context,
None,
); );
let rendered_content = self.render_template( let rendered_content = self.render_template(
&partial_name, &partial_name,
@ -342,14 +341,13 @@ impl<'a> DustRenderer<'a> {
return Ok("".to_owned()); return Ok("".to_owned());
} }
DustTag::DTBlock(named_block) => { DustTag::DTBlock(named_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_partial(
breadcrumbs, breadcrumbs,
blocks.breadcrumbs, blocks.breadcrumbs,
None, None,
&named_block.explicit_context, &named_block.explicit_context,
None,
); );
return match blocks.blocks.get_block(named_block.name) { return match blocks.blocks.get_block(named_block.path.keys[0]) {
None => self.render_maybe_body( None => self.render_maybe_body(
&named_block.contents, &named_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs), new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
@ -363,12 +361,11 @@ impl<'a> DustRenderer<'a> {
}; };
} }
DustTag::DTHelperEquals(parameterized_block) => { DustTag::DTHelperEquals(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_partial(
breadcrumbs, breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
None,
); );
let param_map: HashMap<&str, &RValue<'a>> = let param_map: HashMap<&str, &RValue<'a>> =
Self::get_rval_map(&parameterized_block.params); Self::get_rval_map(&parameterized_block.params);
@ -414,12 +411,11 @@ impl<'a> DustRenderer<'a> {
} }
} }
DustTag::DTHelperNotEquals(parameterized_block) => { DustTag::DTHelperNotEquals(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_partial(
breadcrumbs, breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
None,
); );
let param_map: HashMap<&str, &RValue<'a>> = let param_map: HashMap<&str, &RValue<'a>> =
Self::get_rval_map(&parameterized_block.params); Self::get_rval_map(&parameterized_block.params);
@ -464,12 +460,11 @@ impl<'a> DustRenderer<'a> {
} }
} }
DustTag::DTHelperGreaterThan(parameterized_block) => { DustTag::DTHelperGreaterThan(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_partial(
breadcrumbs, breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
None,
); );
let param_map: HashMap<&str, &RValue<'a>> = let param_map: HashMap<&str, &RValue<'a>> =
Self::get_rval_map(&parameterized_block.params); Self::get_rval_map(&parameterized_block.params);
@ -507,12 +502,11 @@ impl<'a> DustRenderer<'a> {
} }
} }
DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => { DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_partial(
breadcrumbs, breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
None,
); );
let param_map: HashMap<&str, &RValue<'a>> = let param_map: HashMap<&str, &RValue<'a>> =
Self::get_rval_map(&parameterized_block.params); Self::get_rval_map(&parameterized_block.params);
@ -550,12 +544,11 @@ impl<'a> DustRenderer<'a> {
} }
} }
DustTag::DTHelperLessThan(parameterized_block) => { DustTag::DTHelperLessThan(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_partial(
breadcrumbs, breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
None,
); );
let param_map: HashMap<&str, &RValue<'a>> = let param_map: HashMap<&str, &RValue<'a>> =
Self::get_rval_map(&parameterized_block.params); Self::get_rval_map(&parameterized_block.params);
@ -593,12 +586,11 @@ impl<'a> DustRenderer<'a> {
} }
} }
DustTag::DTHelperLessThanOrEquals(parameterized_block) => { DustTag::DTHelperLessThanOrEquals(parameterized_block) => {
let new_breadcrumbs = Self::new_breadcrumbs( let new_breadcrumbs = Self::new_breadcrumbs_partial(
breadcrumbs, breadcrumbs,
breadcrumbs, breadcrumbs,
None, None,
&parameterized_block.explicit_context, &parameterized_block.explicit_context,
None,
); );
let param_map: HashMap<&str, &RValue<'a>> = let param_map: HashMap<&str, &RValue<'a>> =
Self::get_rval_map(&parameterized_block.params); Self::get_rval_map(&parameterized_block.params);
@ -744,7 +736,7 @@ impl<'a> DustRenderer<'a> {
/// explicit_context /// explicit_context
/// new_context_element /// new_context_element
/// ``` /// ```
fn new_breadcrumbs<'b>( fn new_breadcrumbs_deprecated<'b>(
breadcrumbs: &'b Vec<&'b dyn ContextElement>, breadcrumbs: &'b Vec<&'b dyn ContextElement>,
explicit_context_breadcrumbs: &'b Vec<&'b dyn ContextElement>, explicit_context_breadcrumbs: &'b Vec<&'b dyn ContextElement>,
injected_context: Option<&'b dyn ContextElement>, injected_context: Option<&'b dyn ContextElement>,
@ -785,6 +777,92 @@ impl<'a> DustRenderer<'a> {
new_context_element.map(|ctx| new_stack.push(ctx)); new_context_element.map(|ctx| new_stack.push(ctx));
Some(new_stack) Some(new_stack)
} }
fn new_breadcrumbs_section<'b>(
breadcrumbs: &'b Vec<&'b dyn ContextElement>,
index_context: Option<&'b dyn ContextElement>,
injected_context: Option<&'b dyn ContextElement>,
explicit_context: &Option<Path<'b>>,
new_context_element: Option<&'b dyn ContextElement>,
) -> Option<Vec<&'b dyn ContextElement>> {
// If none of the additional contexts are present, return None
// to signal that the original breadcrumbs should be used
// rather than incurring a copy here.
match (
index_context,
injected_context,
explicit_context,
new_context_element,
) {
(None, None, None, None) => return None,
_ => (),
}
let mut new_stack = match explicit_context {
Some(_) => Vec::with_capacity(4),
None => breadcrumbs.clone(),
};
explicit_context.as_ref().map(|path| {
walk_path(breadcrumbs, &path.keys).map(|val| {
if val.is_truthy() {
new_stack.push(val)
}
});
});
injected_context.map(|ctx| new_stack.push(ctx));
new_context_element.map(|ctx| new_stack.push(ctx));
index_context.map(|ctx| new_stack.push(ctx));
Some(new_stack)
}
fn get_index_of_first_non_pseudo_element<'b, B>(breadcrumbs: &'b Vec<B>) -> Option<usize>
where
B: Borrow<dyn ContextElement + 'a>,
{
breadcrumbs
.iter()
.rposition(|b| !(*b).borrow().is_pseudo_element())
}
fn new_breadcrumbs_partial<'b>(
breadcrumbs: &'b Vec<&'b dyn ContextElement>,
explicit_context_breadcrumbs: &'b Vec<&'b dyn ContextElement>,
injected_context: Option<&'b dyn ContextElement>,
explicit_context: &Option<Path<'b>>,
) -> Option<Vec<&'b dyn ContextElement>> {
// If none of the additional contexts are present, return None
// to signal that the original breadcrumbs should be used
// rather than incurring a copy here.
match (injected_context, explicit_context) {
(None, None) => return None,
_ => (),
};
let mut new_stack = match explicit_context {
Some(_) => Vec::with_capacity(3),
None => breadcrumbs.clone(),
};
injected_context.map(|ctx| {
// Special case: when there is no explicit context, the
// injected context gets inserted 1 spot behind the
// current context. Otherwise, the injected context gets
// added after the current context but before the explicit
// context.
match explicit_context {
None => new_stack.insert(
Self::get_index_of_first_non_pseudo_element(&new_stack).unwrap_or(0),
ctx,
),
_ => new_stack.push(ctx),
}
});
explicit_context.as_ref().map(|path| {
walk_path(explicit_context_breadcrumbs, &path.keys).map(|val| {
if val.is_truthy() {
new_stack.push(val)
}
});
});
Some(new_stack)
}
} }
struct BlockContext<'a> { struct BlockContext<'a> {

View File

@ -1,4 +1,5 @@
use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::ContextElement;
use crate::renderer::context_element::Walkable;
use crate::renderer::WalkError; use crate::renderer::WalkError;
use std::borrow::Borrow; use std::borrow::Borrow;
@ -34,6 +35,17 @@ where
WalkResult::FullyWalked(output) WalkResult::FullyWalked(output)
} }
pub fn get_first_non_pseudo_element<'a, B>(breadcrumbs: &'a Vec<B>) -> Option<&B>
where
B: Borrow<dyn ContextElement + 'a>,
{
breadcrumbs
.iter()
.rev()
.filter(|b| !(*b).borrow().is_pseudo_element())
.next()
}
pub fn walk_path<'a, B, P>( pub fn walk_path<'a, B, P>(
breadcrumbs: &'a Vec<B>, breadcrumbs: &'a Vec<B>,
path: &Vec<P>, path: &Vec<P>,
@ -60,17 +72,18 @@ where
.borrow() .borrow()
== "." == "."
{ {
return match walk_path_from_single_level( let first_non_pseudo_element = get_first_non_pseudo_element(breadcrumbs);
breadcrumbs return match first_non_pseudo_element {
.last() None => Err(WalkError::CantWalk),
.expect("Breadcrumbs should never be empty"), Some(current_context) => {
&path[1..], match walk_path_from_single_level(current_context, &path[1..]) {
) { // If no walking was done at all or we partially walked
// If no walking was done at all or we partially walked // then stop trying to find anything because '.' restricts
// then stop trying to find anything because '.' restricts // us to the current scope
// us to the current scope WalkResult::NoWalk | WalkResult::PartialWalk => Err(WalkError::CantWalk),
WalkResult::NoWalk | WalkResult::PartialWalk => Err(WalkError::CantWalk), WalkResult::FullyWalked(new_context) => Ok(new_context),
WalkResult::FullyWalked(new_context) => Ok(new_context), }
}
}; };
} }
for context in breadcrumbs.iter().rev() { for context in breadcrumbs.iter().rev() {