duster/src/renderer/renderer.rs

901 lines
38 KiB
Rust
Raw Normal View History

use crate::parser::template;
use crate::parser::Body;
use crate::parser::DustTag;
2020-05-31 21:26:31 -04:00
use crate::parser::Filter;
2020-06-07 15:09:37 -04:00
use crate::parser::OwnedLiteral;
use crate::parser::PartialNameElement;
use crate::parser::Path;
2020-05-31 21:26:31 -04:00
use crate::parser::Special;
use crate::parser::Template;
use crate::parser::TemplateElement;
use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement;
use crate::renderer::context_element::ContextElement;
2020-06-07 01:35:58 -04:00
use crate::renderer::context_element::IceResult;
use crate::renderer::context_element::IntoContextElement;
use crate::renderer::context_element::Walkable;
2020-04-11 18:25:48 -04:00
use crate::renderer::errors::CompileError;
use crate::renderer::errors::RenderError;
2020-05-31 21:26:31 -04:00
use crate::renderer::errors::WalkError;
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::walking::walk_path;
2020-06-07 14:45:32 -04:00
use std::borrow::Borrow;
use std::collections::HashMap;
#[derive(Clone, Debug)]
2020-04-10 20:58:55 -04:00
pub struct DustRenderer<'a> {
2020-04-11 18:25:48 -04:00
templates: HashMap<String, &'a Template<'a>>,
}
pub fn compile_template<'a>(source: &'a str) -> Result<Template<'a>, CompileError> {
let (_remaining, parsed_template) = template(source).map_err(|_err| CompileError {
message: "Failed to compile template".to_owned(),
})?;
Ok(parsed_template)
}
2020-04-10 20:58:55 -04:00
impl<'a> DustRenderer<'a> {
pub fn new() -> DustRenderer<'a> {
DustRenderer {
2020-04-11 18:25:48 -04:00
templates: HashMap::new(),
}
}
pub fn load_source(&mut self, template: &'a Template, name: String) {
self.templates.insert(name, template);
}
2020-04-10 20:58:55 -04:00
pub fn render<C>(&'a self, name: &str, context: Option<&C>) -> Result<String, RenderError>
where
C: IntoContextElement,
{
let breadcrumbs = context
.map(|ctx| vec![BreadcrumbTreeElement::from_borrowed(ctx)])
.unwrap_or(Vec::new());
self.render_template(name, breadcrumbs.as_ref(), None)
}
pub fn render_template(
&'a self,
name: &str,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
blocks: Option<&'a InlinePartialTreeElement<'a>>,
) -> Result<String, RenderError> {
let main_template = match self.templates.get(name) {
Some(tmpl) => tmpl,
None => {
return Err(RenderError::TemplateNotFound(name.to_owned()));
}
};
let extracted_inline_partials = extract_inline_partials(main_template);
let new_blocks = InlinePartialTreeElement::new(blocks, extracted_inline_partials);
let new_block_context = BlockContext {
breadcrumbs: breadcrumbs,
blocks: &new_blocks,
};
self.render_body(&main_template.contents, breadcrumbs, &new_block_context)
}
fn render_maybe_body(
&'a self,
body: &'a Option<Body>,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
blocks: &'a BlockContext<'a>,
) -> Result<String, RenderError> {
match body {
None => Ok("".to_owned()),
Some(body) => Ok(self.render_body(body, breadcrumbs, blocks)?),
}
}
fn render_body(
&'a self,
body: &'a Body,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
blocks: &'a BlockContext<'a>,
) -> Result<String, RenderError> {
let mut output = String::new();
for elem in &body.elements {
match elem {
TemplateElement::TEIgnoredWhitespace(_) => {}
TemplateElement::TESpan(span) => output.push_str(span.contents),
TemplateElement::TETag(dt) => {
output.push_str(&self.render_tag(dt, breadcrumbs, blocks)?);
}
}
}
Ok(output)
}
/// For rendering a dynamic partial's name or an rvalue template
pub fn render_partial_name(
&'a self,
body: &'a Vec<PartialNameElement>,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
) -> Result<String, RenderError> {
let converted_to_template_elements: Vec<TemplateElement<'a>> =
body.into_iter().map(|e| e.into()).collect();
// Simple templates like partial names and reference rvalues
// cannot contain blocks or inline partials, so we use a blank
// BlockContext.
let empty_block_context = BlockContext {
breadcrumbs: &Vec::new(),
blocks: &InlinePartialTreeElement::new(None, HashMap::new()),
};
self.render_body(
&Body {
elements: converted_to_template_elements,
},
breadcrumbs,
&empty_block_context,
)
}
fn render_tag(
&'a self,
tag: &'a DustTag,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
blocks: &'a BlockContext<'a>,
) -> Result<String, RenderError> {
match tag {
2020-05-31 21:26:31 -04:00
DustTag::DTComment(_comment) => (),
DustTag::DTSpecial(special) => {
return Ok(match special {
Special::Space => " ",
Special::NewLine => "\n",
Special::CarriageReturn => "\r",
Special::LeftCurlyBrace => "{",
Special::RightCurlyBrace => "}",
}
.to_owned())
}
DustTag::DTLiteralStringBlock(literal) => return Ok((*literal).to_owned()),
DustTag::DTReference(reference) => {
let val = walk_path(breadcrumbs, &reference.path.keys)
.map(|ice| ice.into_context_element(self, breadcrumbs));
match val {
Err(WalkError::CantWalk) | Ok(None) => return Ok("".to_owned()),
Ok(Some(final_val)) => {
2020-05-31 23:47:20 -04:00
return if final_val.get_context_element_reference().is_truthy() {
final_val
.get_context_element_reference()
.render(&Self::preprocess_filters(&reference.filters))
2020-05-31 21:26:31 -04:00
} else {
Ok("".to_owned())
};
}
}
}
2020-05-31 21:30:36 -04:00
DustTag::DTSection(container) => {
let injected_context = ParametersContext::new(self, breadcrumbs, &container.params);
2020-05-31 21:30:36 -04:00
let val = walk_path(breadcrumbs, &container.path.keys)
.map(|ice| ice.into_context_element(self, breadcrumbs));
match val {
Err(WalkError::CantWalk) | Ok(None) => {
let new_breadcrumbs = self.new_breadcrumbs_section(
breadcrumbs,
None,
Some(&injected_context),
&container.explicit_context,
None,
);
return self.render_maybe_body(
&container.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
2020-05-31 21:30:36 -04:00
}
Ok(Some(final_val)) => {
let context_element = final_val.get_context_element_reference();
return if context_element.is_truthy() {
match &container.contents {
None => Ok("".to_owned()),
Some(body) => {
let loop_elements: Vec<&dyn ContextElement> =
context_element.get_loop_elements();
if loop_elements.is_empty() {
// Scalar value
let new_breadcrumbs = self.new_breadcrumbs_section(
breadcrumbs,
None,
Some(&injected_context),
&container.explicit_context,
Some(context_element),
);
self.render_body(
body,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
)
} else {
// Array-like value
let total_length = loop_elements.len();
let rendered_results: Result<Vec<String>, RenderError> =
loop_elements
.into_iter()
.enumerate()
.map(|(i, array_elem)| {
let index_context =
IterationContext::new(i, total_length);
let new_breadcrumbs = self
.new_breadcrumbs_section(
breadcrumbs,
Some(&index_context),
Some(&injected_context),
&container.explicit_context,
Some(array_elem),
);
self.render_body(
&body,
new_breadcrumbs
.as_ref()
.unwrap_or(breadcrumbs),
blocks,
)
})
.collect();
let rendered_slice: &[String] = &rendered_results?;
return Ok(rendered_slice.join(""));
}
}
}
} else {
// Oddly enough if the value is falsey (like
// an empty array or null), Dust uses the
// original context before walking the path as
// the context for rendering the else block
let new_breadcrumbs = self.new_breadcrumbs_section(
breadcrumbs,
None,
Some(&injected_context),
&container.explicit_context,
None,
);
self.render_maybe_body(
&container.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
)
};
2020-05-31 21:30:36 -04:00
}
}
}
DustTag::DTExists(container) => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&container.explicit_context,
);
let val = walk_path(breadcrumbs, &container.path.keys)
.map(|ice| ice.into_context_element(self, breadcrumbs));
return match val {
Ok(Some(v)) if v.get_context_element_reference().is_truthy() => self
.render_maybe_body(
&container.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
),
_ => self.render_maybe_body(
&container.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
),
};
}
DustTag::DTNotExists(container) => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&container.explicit_context,
);
let val = walk_path(breadcrumbs, &container.path.keys)
.map(|ice| ice.into_context_element(self, breadcrumbs));
return match val {
Ok(Some(v)) if v.get_context_element_reference().is_truthy() => self
.render_maybe_body(
&container.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
),
_ => self.render_maybe_body(
&container.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
),
};
}
DustTag::DTPartial(partial) => {
let partial_name = self.render_partial_name(&partial.name, breadcrumbs)?;
if partial.params.is_empty() {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&partial.explicit_context,
);
let rendered_content = self.render_template(
&partial_name,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
Some(blocks.blocks),
)?;
return Ok(rendered_content);
} else {
let injected_context =
ParametersContext::new(self, breadcrumbs, &partial.params);
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
Some(&injected_context),
&partial.explicit_context,
);
let rendered_content = self.render_template(
&partial_name,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
Some(blocks.blocks),
)?;
return Ok(rendered_content);
}
}
DustTag::DTInlinePartial(_named_block) => {
// Inline partials are blank during rendering (they get injected into blocks)
return Ok("".to_owned());
}
DustTag::DTBlock(named_block) => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
blocks.breadcrumbs,
None,
&named_block.explicit_context,
);
return match blocks.blocks.get_block(named_block.path.keys[0]) {
None => self.render_maybe_body(
&named_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
),
Some(inline_partial) => self.render_maybe_body(
inline_partial,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
),
};
}
DustTag::DTHelperEquals(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);
2020-06-07 01:04:00 -04:00
if !param_map.contains_key("key") {
return Ok("".to_owned());
}
let left_side = param_map
.walk("key")
2020-06-07 01:04:00 -04:00
.map(|ice| ice.into_context_element(self, breadcrumbs));
let right_side = param_map
.walk("value")
.map(|ice| ice.into_context_element(self, breadcrumbs));
2020-06-07 01:35:58 -04:00
// 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, &right_side)
2020-06-07 01:35:58 -04:00
|| left_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
}) == right_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
})
{
return self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
} else {
return self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
}
}
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);
2020-06-07 01:04:00 -04:00
if !param_map.contains_key("key") {
return Ok("".to_owned());
}
let left_side = param_map
.walk("key")
2020-06-07 01:04:00 -04:00
.map(|ice| ice.into_context_element(self, breadcrumbs));
let right_side = param_map
.walk("value")
.map(|ice| ice.into_context_element(self, breadcrumbs));
2020-06-07 01:35:58 -04:00
// 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, &right_side)
2020-06-07 01:35:58 -04:00
|| left_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
}) == right_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
})
{
return self.render_maybe_body(
2020-06-07 01:35:58 -04:00
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
} else {
return self.render_maybe_body(
2020-06-07 01:35:58 -04:00
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
}
}
2020-06-07 00:12:01 -04:00
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);
2020-06-07 01:04:00 -04:00
if !param_map.contains_key("key") {
return Ok("".to_owned());
}
let left_side = param_map
2020-06-07 00:12:01 -04:00
.walk("key")
2020-06-07 01:04:00 -04:00
.map(|ice| ice.into_context_element(self, breadcrumbs));
2020-06-07 00:12:01 -04:00
let right_side = param_map
.walk("value")
.map(|ice| ice.into_context_element(self, breadcrumbs));
2020-06-07 01:04:00 -04:00
match (left_side, right_side) {
(Ok(Some(left_side_unwrapped)), Ok(Some(right_side_unwrapped))) => {
if left_side_unwrapped.get_context_element_reference()
2020-06-07 00:12:01 -04:00
> right_side_unwrapped.get_context_element_reference()
{
return self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
} else {
return self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
}
}
_ => {
return 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);
2020-06-07 01:04:00 -04:00
if !param_map.contains_key("key") {
return Ok("".to_owned());
}
let left_side = param_map
2020-06-07 00:12:01 -04:00
.walk("key")
2020-06-07 01:04:00 -04:00
.map(|ice| ice.into_context_element(self, breadcrumbs));
2020-06-07 00:12:01 -04:00
let right_side = param_map
.walk("value")
.map(|ice| ice.into_context_element(self, breadcrumbs));
2020-06-07 01:04:00 -04:00
match (left_side, right_side) {
(Ok(Some(left_side_unwrapped)), Ok(Some(right_side_unwrapped))) => {
if left_side_unwrapped.get_context_element_reference()
2020-06-07 00:12:01 -04:00
>= right_side_unwrapped.get_context_element_reference()
{
return self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
} else {
return self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
}
}
_ => {
return 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);
2020-06-07 01:04:00 -04:00
if !param_map.contains_key("key") {
return Ok("".to_owned());
}
let left_side = param_map
2020-06-07 00:12:01 -04:00
.walk("key")
2020-06-07 01:04:00 -04:00
.map(|ice| ice.into_context_element(self, breadcrumbs));
2020-06-07 00:12:01 -04:00
let right_side = param_map
.walk("value")
.map(|ice| ice.into_context_element(self, breadcrumbs));
2020-06-07 01:04:00 -04:00
match (left_side, right_side) {
(Ok(Some(left_side_unwrapped)), Ok(Some(right_side_unwrapped))) => {
if left_side_unwrapped.get_context_element_reference()
2020-06-07 00:12:01 -04:00
< right_side_unwrapped.get_context_element_reference()
{
return self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
} else {
return self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
}
}
_ => {
return 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);
2020-06-07 01:04:00 -04:00
if !param_map.contains_key("key") {
return Ok("".to_owned());
}
let left_side = param_map
2020-06-07 00:12:01 -04:00
.walk("key")
2020-06-07 01:04:00 -04:00
.map(|ice| ice.into_context_element(self, breadcrumbs));
2020-06-07 00:12:01 -04:00
let right_side = param_map
.walk("value")
.map(|ice| ice.into_context_element(self, breadcrumbs));
2020-06-07 01:04:00 -04:00
match (left_side, right_side) {
(Ok(Some(left_side_unwrapped)), Ok(Some(right_side_unwrapped))) => {
if left_side_unwrapped.get_context_element_reference()
2020-06-07 00:12:01 -04:00
<= right_side_unwrapped.get_context_element_reference()
{
return self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
} else {
return self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
}
}
_ => {
return self.render_maybe_body(
&parameterized_block.else_contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
)
}
}
}
2020-06-07 15:09:37 -04:00
DustTag::DTHelperSep(parameterized_block) => {
let new_breadcrumbs = self.new_breadcrumbs_partial(
breadcrumbs,
breadcrumbs,
None,
&parameterized_block.explicit_context,
);
let index = self.get(breadcrumbs, &vec!["$idx"]);
let len = self.get(breadcrumbs, &vec!["$len"]);
match (index, len) {
(Err(_), _) | (_, Err(_)) => {
return self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
)
}
(Ok(index_resolved), Ok(len_resolved)) => {
// Iteration contexts use OwnedLiteral::LPositiveinteger
let index_cast = index_resolved
.get_context_element_reference()
.to_any()
.downcast_ref::<OwnedLiteral>();
let len_cast = len_resolved
.get_context_element_reference()
.to_any()
.downcast_ref::<OwnedLiteral>();
match (index_cast, len_cast) {
(
Some(OwnedLiteral::LPositiveInteger(index_number)),
Some(OwnedLiteral::LPositiveInteger(len_number)),
) => {
if *index_number == len_number - 1 {
return Ok("".to_owned());
} else {
return self.render_maybe_body(
&parameterized_block.contents,
new_breadcrumbs.as_ref().unwrap_or(breadcrumbs),
blocks,
);
}
}
_ => return Ok("".to_owned()),
}
}
}
}
2020-06-07 14:45:32 -04:00
DustTag::DTHelperFirst(parameterized_block) => {}
DustTag::DTHelperLast(parameterized_block) => {}
}
2020-06-07 00:12:01 -04:00
Ok("".to_owned())
}
2020-06-07 14:45:32 -04:00
/// Read a value from the context tree
pub fn get<P>(
&'a self,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
name: &Vec<P>,
) -> Result<IceResult<'a>, WalkError>
where
P: Borrow<str>,
{
let val =
walk_path(breadcrumbs, name).map(|ice| ice.into_context_element(self, breadcrumbs));
match val {
Ok(Some(ice_result)) => Ok(ice_result),
Ok(None) => Err(WalkError::CantWalk),
Err(walk_error) => Err(walk_error),
}
}
/// Read a value from the parameters context
///
/// Returns None if the key is not present at all
pub fn tap<P>(
&'a self,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
parameters: &'a ParametersContext<'a>,
name: &P,
) -> Option<Result<IceResult<'a>, WalkError>>
where
P: Borrow<str>,
{
if !parameters.contains_key(name.borrow()) {
return None;
}
let val = parameters
.walk("key")
.map(|ice| ice.into_context_element(self, breadcrumbs));
match val {
Ok(Some(ice_result)) => Some(Ok(ice_result)),
Ok(None) => Some(Err(WalkError::CantWalk)),
Err(walk_error) => Some(Err(walk_error)),
}
}
fn new_breadcrumbs_section<'b>(
&'b self,
breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>,
index_context: Option<&'b dyn IntoContextElement>,
injected_context: Option<&'b dyn IntoContextElement>,
explicit_context: &Option<Path<'b>>,
new_context_element: Option<&'b dyn ContextElement>,
) -> Option<Vec<BreadcrumbTreeElement<'b>>> {
// 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,
_ => (),
}
// If there is an explicit context, then drop all the current
// context
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(|ice| ice.into_context_element(self, breadcrumbs))
.ok()
.flatten()
.map(|val| {
2020-05-31 23:47:20 -04:00
if val.get_context_element_reference().is_truthy() {
new_stack.push(std::convert::From::from(val))
}
});
});
injected_context.map(|ctx| new_stack.push(BreadcrumbTreeElement::from_borrowed(ctx)));
new_context_element.map(|ctx| {
new_stack.push(BreadcrumbTreeElement::from_borrowed(
ctx.from_context_element(),
))
});
index_context.map(|ctx| new_stack.push(BreadcrumbTreeElement::from_borrowed(ctx)));
Some(new_stack)
2020-05-05 20:22:25 -04:00
}
fn new_breadcrumbs_partial<'b>(
&'b self,
2020-06-06 22:45:58 -04:00
breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>,
explicit_context_breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
injected_context: Option<&'b dyn IntoContextElement>,
explicit_context: &Option<Path<'b>>,
2020-06-06 22:45:58 -04:00
) -> Option<Vec<BreadcrumbTreeElement<'b>>> {
// 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,
_ => (),
};
// If there is an explicit context, then drop all the current
// context
2020-06-06 22:45:58 -04:00
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),
BreadcrumbTreeElement::from_borrowed(ctx),
),
_ => new_stack.push(BreadcrumbTreeElement::from_borrowed(ctx)),
}
});
2020-06-06 22:45:58 -04:00
explicit_context.as_ref().map(|path| {
// TODO: should resolving the value here use
// explicit_context_maybe_breadcrumbs or
// maybe_breadcrumbs?
2020-06-06 22:45:58 -04:00
walk_path(explicit_context_breadcrumbs, &path.keys)
.map(|ice| ice.into_context_element(self, breadcrumbs))
.ok()
.flatten()
.map(|val| {
2020-06-06 15:20:43 -04:00
if val.get_context_element_reference().is_truthy() {
2020-06-06 22:45:58 -04:00
new_stack.push(std::convert::From::from(val));
}
});
});
2020-06-06 22:45:58 -04:00
Some(new_stack)
}
2020-05-31 21:26:31 -04:00
fn are_paths_identical<'b>(
2020-06-07 01:35:58 -04:00
left_side: &Result<Option<IceResult<'b>>, WalkError>,
right_side: &Result<Option<IceResult<'b>>, WalkError>,
) -> bool {
let left_resolved = left_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
let right_resolved = right_side.as_ref().map(|maybe_ice| {
maybe_ice
.as_ref()
.map(|ice| ice.get_context_element_reference())
});
match (left_resolved, right_resolved) {
(Ok(Some(lce)), Ok(Some(rce))) => lce as *const _ == rce as *const _,
_ => false,
}
}
2020-05-31 21:26:31 -04:00
fn preprocess_filters(filters: &Vec<Filter>) -> Vec<Filter> {
let mut final_filters: Vec<Filter> = filters
.into_iter()
.filter(|f| f != &&Filter::DisableHtmlEncode)
.map(|f| f.clone())
.collect();
// If the user has not specified any escaping filter (|s or
// |h), automatically add an html escape filter
if !filters.iter().any(|f| f == &Filter::DisableHtmlEncode) {
final_filters.push(Filter::HtmlEncode);
}
final_filters
}
2020-06-06 22:45:58 -04:00
fn get_index_of_first_non_pseudo_element<'b>(
breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>,
) -> Option<usize> {
breadcrumbs.iter().rposition(|b| {
2020-06-07 00:37:47 -04:00
!std::borrow::Borrow::<dyn IntoContextElement + 'b>::borrow(b).is_pseudo_element()
})
2020-06-06 22:45:58 -04:00
}
2020-04-11 20:31:44 -04:00
}
struct BlockContext<'a> {
/// The breadcrumbs at the time of entering the current partial
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
blocks: &'a InlinePartialTreeElement<'a>,
}