Merge branch 'helper_select' into render

master
Tom Alexander 4 years ago
commit c06cc61a07
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE

@ -0,0 +1,12 @@
Depth
-----
Select blocks can contain any element type inside, just like most blocks. They, however, only apply their select-checking to immediate children. This prevents things like loops with conditionals inside of them.
Early termination
-----------------
Comparisons are done in-order and only the first matching comparison (eq/ne/gt/gte/lt/lte) is evaluated. All other non-comparison elements inside the select tag are still rendered normally.
Default vs none
---------------
Default was deprecated as of dust 1.6.0 and no longer does anything. It was deprecated because it was essentially the same as none except that there could only be one per select block and it had to be placed after all the other conditions to make sure they were all false. None is more flexible.

@ -0,0 +1,19 @@
{
"pet": "cat",
"person": "Alice",
"pet_names": [
{
"type": "cat",
"pet_name": "fluffy"
},
{
"type": "dog",
"pet_name": "rover"
},
{
"type": "lizard",
"pet_name": "dave"
}
],
"scalar": 7
}

@ -0,0 +1,110 @@
Simple select{~n}
============={~n}
{@select key=pet}
{@eq value="cat"}Lets name your pet fluffy{~n}{/eq}
{@eq value="dog"}Lets name your pet rover{~n}{/eq}
{@eq value="lizard"}Lets name your pet dave{~n}{/eq}
{/select}
non-comparison bodies inside select{~n}
==================================={~n}
{@select key=pet}
text not inside a comparison{~n}
{@eq value="cat"}Lets name your pet fluffy{~n}{/eq}
{@eq value="dog"}Lets name your pet rover{~n}{/eq}
{@eq value="lizard"}Lets name your pet dave{~n}{/eq}
{/select}
@any{~n}
===={~n}
{@select key=pet}
{@any}{person} has a pet!{~n}{/any}
{@eq value="cat"}Lets name your pet fluffy{~n}{/eq}
{@eq value="dog"}Lets name your pet rover{~n}{/eq}
{@eq value="lizard"}Lets name your pet dave{~n}{/eq}
{/select}
@none{~n}
====={~n}
{@select key=pet}
{@any}{person} has a pet!{~n}{/any}
{@none}I don't know what to name {person}'s pet...{~n}{/none}
{@eq value="dog"}Lets name your pet rover{~n}{/eq}
{@eq value="lizard"}Lets name your pet dave{~n}{/eq}
{/select}
Is key added to the context?{~n}
============================{~n}
{@select key=pet}
{key}{~n}
{@any}{person} has a pet!{~n}{/any}
{/select}{~n}
Conditionals inside loop sections{~n}
================================={~n}
{@select key=pet}
{@any}{person} has a pet!{~n}{/any}
{#pet_names}
{@eq value=type}Lets name your pet {pet_name}{~n}{/eq}
{/pet_names}
{@none}I don't know what to name {person}'s pet...{~n}{/none}
{/select}{~n}
Conditionals inside scalar sections{~n}
==================================={~n}
{@select key=pet}
{@any}{person} has a pet!{~n}{/any}
{#scalar}
{@eq value="cat"}Lets name your pet fluffy{~n}{/eq}
{@eq value="dog"}Lets name your pet rover{~n}{/eq}
{@eq value="lizard"}Lets name your pet dave{~n}{/eq}
{/scalar}
{/select}{~n}
Sections inside select without conditionals{~n}
==========================================={~n}
{@select key=pet}
{@any}{person} has a pet!{~n}{/any}
{#pet_names}
If your pet was a {type} we'd name it {pet_name}{~n}
{/pet_names}
{@eq value="cat"}Lets name your pet fluffy{~n}{/eq}
{@eq value="dog"}Lets name your pet rover{~n}{/eq}
{@eq value="lizard"}Lets name your pet dave{~n}{/eq}
{/select}{~n}
Early termination{~n}
================={~n}
{@select key=pet}
{@eq value="cat"}Lets name your pet fluffy{~n}{/eq}
{@eq value="cat"}Lets name your pet whiskers{~n}{/eq}
{@eq value="dog"}Lets name your pet rover{~n}{/eq}
{@eq value="lizard"}Lets name your pet dave{~n}{/eq}
{@any}{person} has a pet!{~n}{/any}
text not inside a comparison{~n}
{#pet_names}
If your pet was a {type} we'd name it {pet_name}{~n}
{/pet_names}
{/select}{~n}
Early termination stand-alone comparison{~n}
========================================{~n}
{@select key=pet}
{@eq key="duck" value="duck"}Lets name your pet quackers{~n}{/eq}
{@eq value="cat"}Lets name your pet whiskers{~n}{/eq}
{@eq value="dog"}Lets name your pet rover{~n}{/eq}
{@eq value="lizard"}Lets name your pet dave{~n}{/eq}
{@any}{person} has a pet!{~n}{/any}
text not inside a comparison{~n}
{#pet_names}
If your pet was a {type} we'd name it {pet_name}{~n}
{/pet_names}
{/select}{~n}
@any alone{~n}
=========={~n}
{@any}{person} has a pet!{~n}{/any}
@none alone{~n}
==========={~n}
{@none}I don't know what to name {person}'s pet...{~n}{/none}

@ -44,6 +44,9 @@ pub enum DustTag<'a> {
DTHelperSep(ParameterizedBlock<'a>),
DTHelperFirst(ParameterizedBlock<'a>),
DTHelperLast(ParameterizedBlock<'a>),
DTHelperSelect(ParameterizedBlock<'a>),
DTHelperAny(ParameterizedBlock<'a>),
DTHelperNone(ParameterizedBlock<'a>),
}
#[derive(Clone, Debug, PartialEq)]
@ -219,6 +222,15 @@ fn dust_tag(i: &str) -> IResult<&str, DustTag> {
DustTag::DTInlinePartial,
),
partial("{>", DustTag::DTPartial),
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((
map(
parameterized_block("{@", &tag_to_path("gte")),
DustTag::DTHelperGreaterThanOrEquals,
@ -255,6 +267,18 @@ fn dust_tag(i: &str) -> IResult<&str, DustTag> {
parameterized_block("{@", &tag_to_path("last")),
DustTag::DTHelperLast,
),
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,
),
))(i)
}

@ -13,7 +13,6 @@ pub trait ContextElement:
+ Walkable
+ Renderable
+ Loopable
// + CloneIntoBoxedContextElement
+ CompareContextElement
+ FromContextElement
+ IntoRcIce
@ -64,16 +63,6 @@ pub trait CompareContextElement: CastToAny {
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering>;
}
// pub trait CloneIntoBoxedContextElement {
// fn clone_to_box(&self) -> Box<dyn IntoContextElement>;
// }
// impl<C: 'static + IntoContextElement + Clone> CloneIntoBoxedContextElement for C {
// fn clone_to_box(&self) -> Box<dyn IntoContextElement> {
// Box::new(self.clone())
// }
// }
impl<C: 'static + ContextElement> CastToAny for C {
fn to_any(&self) -> &dyn Any {
self

@ -100,87 +100,18 @@ fn extract_inline_partials_from_tag<'a, 'b>(
blocks.insert(&named_block.path.keys[0], &named_block.contents);
}
DustTag::DTBlock(..) => (),
DustTag::DTHelperEquals(parameterized_block) => {
match &parameterized_block.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
match &parameterized_block.else_contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
}
DustTag::DTHelperNotEquals(parameterized_block) => {
match &parameterized_block.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
match &parameterized_block.else_contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
}
DustTag::DTHelperGreaterThan(parameterized_block) => {
match &parameterized_block.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
match &parameterized_block.else_contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
}
DustTag::DTHelperLessThan(parameterized_block) => {
match &parameterized_block.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
match &parameterized_block.else_contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
}
DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => {
match &parameterized_block.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
match &parameterized_block.else_contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
}
DustTag::DTHelperLessThanOrEquals(parameterized_block) => {
match &parameterized_block.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
match &parameterized_block.else_contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
}
DustTag::DTHelperSep(parameterized_block) => {
match &parameterized_block.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
match &parameterized_block.else_contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
}
DustTag::DTHelperFirst(parameterized_block) => {
match &parameterized_block.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
match &parameterized_block.else_contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),
};
}
DustTag::DTHelperLast(parameterized_block) => {
DustTag::DTHelperEquals(parameterized_block)
| DustTag::DTHelperNotEquals(parameterized_block)
| DustTag::DTHelperGreaterThan(parameterized_block)
| DustTag::DTHelperLessThan(parameterized_block)
| DustTag::DTHelperGreaterThanOrEquals(parameterized_block)
| DustTag::DTHelperLessThanOrEquals(parameterized_block)
| DustTag::DTHelperSep(parameterized_block)
| DustTag::DTHelperFirst(parameterized_block)
| DustTag::DTHelperLast(parameterized_block)
| DustTag::DTHelperSelect(parameterized_block)
| DustTag::DTHelperAny(parameterized_block)
| DustTag::DTHelperNone(parameterized_block) => {
match &parameterized_block.contents {
None => (),
Some(body) => extract_inline_partials_from_body(blocks, &body),

@ -7,9 +7,9 @@ mod inline_partial_tree;
mod iteration_context;
mod parameters_context;
mod renderer;
mod select_context;
mod walking;
// pub use context_element::CloneIntoBoxedContextElement;
pub use context_element::CompareContextElement;
pub use context_element::ContextElement;
pub use context_element::IntoContextElement;
@ -21,5 +21,5 @@ pub use errors::CompileError;
pub use errors::RenderError;
pub use errors::WalkError;
pub use renderer::compile_template;
// pub use renderer::CompiledTemplate;
pub use renderer::DustRenderer;
pub use select_context::SelectContext;

@ -21,6 +21,7 @@ use std::collections::HashMap;
#[derive(Debug)]
pub struct ParametersContext<'a> {
parent: Option<&'a ParametersContext<'a>>,
params: HashMap<&'a str, (&'a RValue<'a>, Option<BreadcrumbTreeElement<'a>>)>,
}
@ -29,6 +30,7 @@ impl<'a> ParametersContext<'a> {
renderer: &DustRenderer,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
params: &'a Vec<KVPair>,
parent: Option<&'a ParametersContext<'a>>,
) -> Self {
// If the parameter is a Path, then we resolve it immediately
// to a context element because those are resolved using the
@ -60,12 +62,17 @@ impl<'a> ParametersContext<'a> {
.collect();
ParametersContext {
parent: parent,
params: rendered_params,
}
}
pub fn contains_key(&self, segment: &str) -> bool {
self.params.contains_key(segment)
|| self
.parent
.map(|p| p.contains_key(segment))
.unwrap_or(false)
}
}
@ -83,7 +90,11 @@ impl<'a> Walkable for ParametersContext<'a> {
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
match self.params.get(segment).map(|(_rvalue, bte)| bte) {
Some(Some(bte)) => Ok(bte.borrow()),
_ => Err(WalkError::CantWalk),
Some(None) => Err(WalkError::CantWalk),
None => self
.parent
.map(|p| p.walk(segment))
.unwrap_or(Err(WalkError::CantWalk)),
}
}

@ -20,6 +20,7 @@ use crate::renderer::inline_partial_tree::extract_inline_partials;
use crate::renderer::inline_partial_tree::InlinePartialTreeElement;
use crate::renderer::iteration_context::IterationContext;
use crate::renderer::parameters_context::ParametersContext;
use crate::renderer::select_context::SelectContext;
use crate::renderer::walking::walk_path;
use std::borrow::Borrow;
use std::collections::HashMap;
@ -75,7 +76,12 @@ impl<'a> DustRenderer<'a> {
breadcrumbs: breadcrumbs,
blocks: &new_blocks,
};
self.render_body(&main_template.contents, breadcrumbs, &new_block_context)
self.render_body(
&main_template.contents,
breadcrumbs,
&new_block_context,
&mut None,
)
}
fn render_maybe_body(
@ -83,10 +89,11 @@ impl<'a> DustRenderer<'a> {
body: &'a Option<Body>,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
blocks: &'a BlockContext<'a>,
select_context: &mut Option<SelectContext<'a>>,
) -> Result<String, RenderError> {
match body {
None => Ok("".to_owned()),
Some(body) => Ok(self.render_body(body, breadcrumbs, blocks)?),
Some(body) => Ok(self.render_body(body, breadcrumbs, blocks, select_context)?),
}
}
@ -95,6 +102,7 @@ impl<'a> DustRenderer<'a> {
body: &'a Body,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
blocks: &'a BlockContext<'a>,
select_context: &mut Option<SelectContext<'a>>,
) -> Result<String, RenderError> {
let mut output = String::new();
for elem in &body.elements {
@ -102,7 +110,7 @@ impl<'a> DustRenderer<'a> {
TemplateElement::TEIgnoredWhitespace(_) => {}
TemplateElement::TESpan(span) => output.push_str(span.contents),
TemplateElement::TETag(dt) => {
output.push_str(&self.render_tag(dt, breadcrumbs, blocks)?);
output.push_str(&self.render_tag(dt, breadcrumbs, blocks, select_context)?);
}
}
}
@ -130,6 +138,7 @@ impl<'a> DustRenderer<'a> {
},
breadcrumbs,
&empty_block_context,
&mut None,
)
}
@ -138,6 +147,7 @@ impl<'a> DustRenderer<'a> {
tag: &'a DustTag,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
blocks: &'a BlockContext<'a>,
select_context: &mut Option<SelectContext<'a>>,
) -> Result<String, RenderError> {
match tag {
DustTag::DTComment(_comment) => (),
@ -169,7 +179,8 @@ impl<'a> DustRenderer<'a> {
}
}
DustTag::DTSection(container) => {
let injected_context = ParametersContext::new(self, breadcrumbs, &container.params);
let injected_context =
ParametersContext::new(self, breadcrumbs, &container.params, None);
let val = walk_path(breadcrumbs, &container.path.keys)
.map(|ice| ice.into_context_element(self, breadcrumbs));
match val {
@ -185,6 +196,7 @@ impl<'a> DustRenderer<'a> {
&container.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
);
}
Ok(Some(final_val)) => {
@ -208,6 +220,7 @@ impl<'a> DustRenderer<'a> {
body,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
)
} else {
// Array-like value
@ -233,6 +246,7 @@ impl<'a> DustRenderer<'a> {
.as_ref()
.unwrap_or(breadcrumbs),
blocks,
&mut None,
)
})
.collect();
@ -257,6 +271,7 @@ impl<'a> DustRenderer<'a> {
&container.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
)
};
}
@ -277,11 +292,13 @@ impl<'a> DustRenderer<'a> {
&container.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
),
_ => self.render_maybe_body(
&container.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
),
};
}
@ -300,11 +317,13 @@ impl<'a> DustRenderer<'a> {
&container.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
),
_ => self.render_maybe_body(
&container.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
),
};
}
@ -325,7 +344,7 @@ impl<'a> DustRenderer<'a> {
return Ok(rendered_content);
} else {
let injected_context =
ParametersContext::new(self, breadcrumbs, &partial.params);
ParametersContext::new(self, breadcrumbs, &partial.params, None);
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
@ -356,15 +375,28 @@ impl<'a> DustRenderer<'a> {
&named_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
),
Some(inline_partial) => self.render_maybe_body(
inline_partial,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
),
};
}
DustTag::DTHelperEquals(parameterized_block) => {
DustTag::DTHelperEquals(parameterized_block)
| DustTag::DTHelperNotEquals(parameterized_block)
| DustTag::DTHelperGreaterThan(parameterized_block)
| DustTag::DTHelperLessThan(parameterized_block)
| DustTag::DTHelperGreaterThanOrEquals(parameterized_block)
| DustTag::DTHelperLessThanOrEquals(parameterized_block) => {
match select_context {
Some(sc) if sc.allowed_to_render_any_more_conditionals == false => {
return Ok("".to_owned())
}
_ => (),
}
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
@ -372,265 +404,33 @@ impl<'a> DustRenderer<'a> {
&parameterized_block.explicit_context,
);
let param_map =
ParametersContext::new(self, breadcrumbs, &parameterized_block.params);
let left_side = self.tap(breadcrumbs, &param_map, "key");
let right_side = self.tap(breadcrumbs, &param_map, "value");
let left_side_ce = left_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
let right_side_ce = right_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
return match (left_side_ce, right_side_ce) {
// If "key" is not present at all, return a blank string
(None, _) => Ok("".to_owned()),
(Some(_maybe_left_ce), _) => {
// Special case: when comparing two RVPaths, if the
// path points to the same value then they are
// equal. This is particularly important for objects
// which compare memory locations rather than contents
// (javascript object equality).
if Self::are_paths_identical(&left_side_ce, &right_side_ce)
|| left_side_ce.unwrap_or(Err(&WalkError::CantWalk))
== right_side_ce.unwrap_or(Err(&WalkError::CantWalk))
{
return self
.perform_comparison_check(
tag,
breadcrumbs,
select_context.as_ref().map(|sc| sc.select_parameters),
)
.map(|check_result| {
if check_result {
select_context.as_mut().map(|sc| {
sc.allowed_to_render_any_more_conditionals = false;
});
self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
)
} else {
self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
)
}
}
};
}
DustTag::DTHelperNotEquals(parameterized_block) => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&parameterized_block.explicit_context,
);
let param_map =
ParametersContext::new(self, breadcrumbs, &parameterized_block.params);
let left_side = self.tap(breadcrumbs, &param_map, "key");
let right_side = self.tap(breadcrumbs, &param_map, "value");
let left_side_ce = left_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
let right_side_ce = right_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
return match (left_side_ce, right_side_ce) {
// If "key" is not present at all, return a blank string
(None, _) => Ok("".to_owned()),
(Some(_maybe_left_ce), _) => {
// Special case: when comparing two RVPaths, if the
// path points to the same value then they are
// equal. This is particularly important for objects
// which compare memory locations rather than contents
// (javascript object equality).
if Self::are_paths_identical(&left_side_ce, &right_side_ce)
|| left_side_ce.unwrap_or(Err(&WalkError::CantWalk))
== right_side_ce.unwrap_or(Err(&WalkError::CantWalk))
{
self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
)
} else {
self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
)
}
}
};
}
DustTag::DTHelperGreaterThan(parameterized_block) => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&parameterized_block.explicit_context,
);
let param_map =
ParametersContext::new(self, breadcrumbs, &parameterized_block.params);
let left_side = self.tap(breadcrumbs, &param_map, "key");
let right_side = self.tap(breadcrumbs, &param_map, "value");
let left_side_ce = left_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
let right_side_ce = right_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
return match (left_side_ce, right_side_ce) {
// If "key" is not present at all, return a blank string
(None, _) => Ok("".to_owned()),
(Some(Ok(left_value)), Some(Ok(right_value))) if left_value > right_value => {
self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
)
}
_ => self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
),
};
}
DustTag::DTHelperGreaterThanOrEquals(parameterized_block) => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&parameterized_block.explicit_context,
);
let param_map =
ParametersContext::new(self, breadcrumbs, &parameterized_block.params);
let left_side = self.tap(breadcrumbs, &param_map, "key");
let right_side = self.tap(breadcrumbs, &param_map, "value");
let left_side_ce = left_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
let right_side_ce = right_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
return match (left_side_ce, right_side_ce) {
// If "key" is not present at all, return a blank string
(None, _) => Ok("".to_owned()),
(Some(Ok(left_value)), Some(Ok(right_value))) if left_value >= right_value => {
self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
)
}
_ => self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
),
};
}
DustTag::DTHelperLessThan(parameterized_block) => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&parameterized_block.explicit_context,
);
let param_map =
ParametersContext::new(self, breadcrumbs, &parameterized_block.params);
let left_side = self.tap(breadcrumbs, &param_map, "key");
let right_side = self.tap(breadcrumbs, &param_map, "value");
let left_side_ce = left_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
let right_side_ce = right_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
return match (left_side_ce, right_side_ce) {
// If "key" is not present at all, return a blank string
(None, _) => Ok("".to_owned()),
(Some(Ok(left_value)), Some(Ok(right_value))) if left_value < right_value => {
self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
)
}
_ => self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
),
};
}
DustTag::DTHelperLessThanOrEquals(parameterized_block) => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&parameterized_block.explicit_context,
);
let param_map =
ParametersContext::new(self, breadcrumbs, &parameterized_block.params);
let left_side = self.tap(breadcrumbs, &param_map, "key");
let right_side = self.tap(breadcrumbs, &param_map, "value");
let left_side_ce = left_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
let right_side_ce = right_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
return match (left_side_ce, right_side_ce) {
// If "key" is not present at all, return a blank string
(None, _) => Ok("".to_owned()),
(Some(Ok(left_value)), Some(Ok(right_value))) if left_value <= right_value => {
self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
)
}
_ => self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
),
};
})
.unwrap_or(Ok("".to_owned()));
}
DustTag::DTHelperSep(parameterized_block) => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
@ -648,6 +448,7 @@ impl<'a> DustRenderer<'a> {
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
)
}
(Ok(index_resolved), Ok(len_resolved)) => {
@ -672,6 +473,7 @@ impl<'a> DustRenderer<'a> {
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
);
}
}
@ -706,6 +508,7 @@ impl<'a> DustRenderer<'a> {
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
);
}
}
@ -748,6 +551,7 @@ impl<'a> DustRenderer<'a> {
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
);
}
}
@ -756,6 +560,90 @@ impl<'a> DustRenderer<'a> {
}
}
}
DustTag::DTHelperSelect(parameterized_block) => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&parameterized_block.explicit_context,
);
let new_breadcrumbs_ref = new_breadcrumbs.as_ref().unwrap_or(breadcrumbs);
match &parameterized_block.contents {
None => return Ok("".to_owned()),
Some(body) => {
let param_map = ParametersContext::new(
self,
breadcrumbs,
&parameterized_block.params,
None,
);
let are_any_checks_true = body
.elements
.iter()
.filter_map(|te| match te {
TemplateElement::TETag(dt) => match dt {
DustTag::DTHelperEquals(_)
| DustTag::DTHelperNotEquals(_)
| DustTag::DTHelperGreaterThan(_)
| DustTag::DTHelperLessThan(_)
| DustTag::DTHelperGreaterThanOrEquals(_)
| DustTag::DTHelperLessThanOrEquals(_) => Some(dt),
_ => None,
},
_ => None,
})
.map(|dt| {
self.perform_comparison_check(
dt,
new_breadcrumbs_ref,
Some(&param_map),
)
})
.any(|check_result| check_result.unwrap_or(false));
let select_context = SelectContext::new(&param_map, are_any_checks_true);
return self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs_ref,
blocks,
&mut Some(select_context),
);
}
}
}
DustTag::DTHelperAny(parameterized_block) => match select_context {
Some(sc) if sc.were_any_true => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&parameterized_block.explicit_context,
);
return self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
);
}
_ => return Ok("".to_owned()),
},
DustTag::DTHelperNone(parameterized_block) => match select_context {
Some(sc) if !sc.were_any_true => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&parameterized_block.explicit_context,
);
return self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
&mut None,
);
}
_ => return Ok("".to_owned()),
},
}
Ok("".to_owned())
@ -941,6 +829,78 @@ impl<'a> DustRenderer<'a> {
!std::borrow::Borrow::<dyn IntoContextElement + 'b>::borrow(b).is_pseudo_element()
})
}
/// Performs a comparison (eq, ne, gt, gte, lt, lte) and returns
/// whether or not the comparison was true.
///
/// Returns None in the special case that the "key" parameter did
/// not exist at all. This means that key was not in the original
/// dust template source, which is not the same as key referencing
/// a value which does not exist.
fn perform_comparison_check(
&'a self,
tag: &'a DustTag,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
select_parameters: Option<&'a ParametersContext<'a>>,
) -> Option<bool> {
let param_map = match tag {
DustTag::DTHelperEquals(parameterized_block)
| DustTag::DTHelperNotEquals(parameterized_block)
| DustTag::DTHelperGreaterThan(parameterized_block)
| DustTag::DTHelperLessThan(parameterized_block)
| DustTag::DTHelperGreaterThanOrEquals(parameterized_block)
| DustTag::DTHelperLessThanOrEquals(parameterized_block) => ParametersContext::new(self, breadcrumbs, &parameterized_block.params, select_parameters),
_ => panic!("perform_comparison_check only implemented for comparison helpers (eq, ne, gt, gte, lt, lte)")
};
let left_side = self.tap(breadcrumbs, &param_map, "key");
let right_side = self.tap(breadcrumbs, &param_map, "value");
let left_side_ce = left_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
let right_side_ce = right_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
if left_side_ce.is_none() {
// If key did not exist at all, return None
return None;
}
match tag {
// Special case: when comparing two RVPaths, if the path
// points to the same value then they are equal. This is
// particularly important for objects which compare memory
// locations rather than contents (javascript object
// equality).
DustTag::DTHelperEquals(_) => Some(Self::are_paths_identical(&left_side_ce, &right_side_ce)
|| left_side_ce.unwrap_or(Err(&WalkError::CantWalk))
== right_side_ce.unwrap_or(Err(&WalkError::CantWalk))),
DustTag::DTHelperNotEquals(_) => Some(!Self::are_paths_identical(&left_side_ce, &right_side_ce)
&& left_side_ce.unwrap_or(Err(&WalkError::CantWalk))
!= right_side_ce.unwrap_or(Err(&WalkError::CantWalk))),
DustTag::DTHelperGreaterThan(_) => Some(match (left_side_ce, right_side_ce) {
(Some(Ok(left_value)), Some(Ok(right_value))) if left_value > right_value => true,
_ => false
}),
DustTag::DTHelperLessThan(_) => Some(match (left_side_ce, right_side_ce) {
(Some(Ok(left_value)), Some(Ok(right_value))) if left_value < right_value => true,
_ => false
}),
DustTag::DTHelperGreaterThanOrEquals(_) => Some(match (left_side_ce, right_side_ce) {
(Some(Ok(left_value)), Some(Ok(right_value))) if left_value >= right_value => true,
_ => false
}),
DustTag::DTHelperLessThanOrEquals(_) => Some(match (left_side_ce, right_side_ce) {
(Some(Ok(left_value)), Some(Ok(right_value))) if left_value <= right_value => true,
_ => false
}),
_ => panic!("perform_comparison_check only implemented for comparison helpers (eq, ne, gt, gte, lt, lte)")
}
}
}
struct BlockContext<'a> {

@ -0,0 +1,18 @@
use crate::renderer::parameters_context::ParametersContext;
#[derive(Debug)]
pub struct SelectContext<'a> {
pub select_parameters: &'a ParametersContext<'a>,
pub were_any_true: bool,
pub allowed_to_render_any_more_conditionals: bool,
}
impl<'a> SelectContext<'a> {
pub fn new(select_parameters: &'a ParametersContext<'a>, were_any_true: bool) -> Self {
SelectContext {
select_parameters: select_parameters,
were_any_true: were_any_true,
allowed_to_render_any_more_conditionals: true,
}
}
}
Loading…
Cancel
Save