Merge branch 'into_context_element' into render

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

@ -21,3 +21,9 @@
{@eq key=array_of_some_obj value=array_of_some_obj}array_of_some_obj is equal to array_of_some_obj{:else}array_of_some_obj does not equal array_of_some_obj{/eq}{~n}
{@eq key=array_of_some_obj value=copy_array_of_some_obj}array_of_some_obj is equal to copy_array_of_some_obj{:else}array_of_some_obj does not equal copy_array_of_some_obj{/eq}{~n}
{@eq key=array_of_some_obj value=array_of_other_obj}array_of_some_obj is equal to array_of_other_obj{:else}array_of_some_obj does not equal array_of_other_obj{/eq}{~n}
Do objects with different paths referencing the same variable match?{~n}
===================================================================={~n}
{#int renamed=some_obj}
{@eq key=some_obj value=renamed}some_obj equals renamed{:else}some_obj does not equal renamed{/eq}{~n}
{/int}

@ -0,0 +1 @@
Reference parameters are evaluated at the time of render, as opposed to direct parameters which are evaluated at the time of assignment.

@ -0,0 +1,32 @@
{
"name": "Bob",
"people": [
{
"name": "Alice",
"petname": "rover"
}
],
"truthy": "some truthy value",
"other_petname": [
{
"petname": "spot"
}
],
"array_petname": [
{
"petname": [
"foo",
"bar"
]
}
],
"some_object": {
"foo": "bar"
},
"some_same_object": {
"foo": "bar"
},
"some_different_object": {
"foo": "baz"
}
}

@ -0,0 +1,64 @@
Hello {name}, nice {pet}{~n}
{#people}
Hello {name}, nice {pet}{~n}
{/people}
{#people name="chris" pet="cat"}
Hello {name}, nice {pet}{~n}
{/people}
Direct Parameters{~n}
================={~n}
{#people name="chris" pet=petname petname="whiskers"}
Hello {name}, nice {pet}{~n}
{/people}
{#people}
{#truthy name="chris" pet=petname petname="whiskers"}
Hello {name}, nice {pet}{~n}
{/truthy}
{/people}
{#people name="chris" pet=petname petname="whiskers"}
{#other_petname}
Hello {name}, nice {pet}{~n}
{/other_petname}
{/people}
Reference Parameters{~n}
===================={~n}
{#people name="chris" pet="{petname}" petname="whiskers"}
Hello {name}, nice {pet}{~n}
{/people}
{#people}
{#truthy name="chris" pet="{petname}" petname="whiskers"}
Hello {name}, nice {pet}{~n}
{/truthy}
{/people}
{#people name="chris" pet="{petname}" petname="whiskers"}
{#other_petname}
Hello {name}, nice {pet}{~n}
{/other_petname}
{/people}
{! Can you have additional text in reference parameters, or just the reference !}
{#people name="chris" pet="{petname}!" petname="whiskers"}
{#other_petname}
Hello {name}, nice {pet}{~n}
{/other_petname}
{/people}
{! Can you have filters !}
{#people name="chris" pet="{petname|js}" petname="whiskers"}
{#other_petname}
Hello {name}, nice {pet}{~n}
{/other_petname}
{/people}
{! Can you go through multiple levels of references !}
{#truthy name="chris" pet="{petname}" petname="{deeperpetname}" deeperpetname="fluffy"}
Hello {name}, nice {pet}{~n}
{/truthy}
Equality{~n}
========{~n}
{@eq key=some_object value=some_object}some_object equals some_object{:else}some_object does not equal some_object{/eq}{~n}
{@eq key=some_object value=some_same_object}some_object equals some_same_object{:else}some_object does not equal some_same_object{/eq}{~n}
{@eq key=some_object value="{some_object}"}some_object equals reference(some_object){:else}some_object does not equal reference(some_object){/eq}{~n}
{@eq key="{some_object}" value="{some_object}"}reference(some_object) equals reference(some_object){:else}reference(some_object) does not equal reference(some_object){/eq}{~n}
{@eq key="{some_object}" value="{some_same_object}"}reference(some_object) equals reference(some_same_object){:else}reference(some_object) does not equal reference(some_same_object){/eq}{~n}
{@eq key="{some_object}" value="{some_different_object}"}reference(some_object) equals reference(some_different_object){:else}reference(some_object) does not equal reference(some_different_object){/eq}{~n}

@ -3,10 +3,12 @@ extern crate nom;
use crate::renderer::CompareContextElement;
use parser::Filter;
use parser::OwnedLiteral;
use parser::Template;
use renderer::compile_template;
use renderer::CompiledTemplate;
use renderer::CompileError;
use renderer::ContextElement;
use renderer::DustRenderer;
use renderer::IntoContextElement;
use renderer::Loopable;
use renderer::RenderError;
use renderer::Renderable;
@ -37,32 +39,40 @@ fn main() {
(p.to_string(), template_content)
})
.collect();
let compiled_templates: Vec<CompiledTemplate> = template_contents
.iter()
.map(|(p, contents)| template_from_file(p, contents))
.collect();
let mut dust_renderer = DustRenderer::new();
compiled_templates.iter().for_each(|template| {
dust_renderer.load_source(template);
});
let compiled_templates_result: Result<Vec<(String, Template)>, CompileError> =
template_contents
.iter()
.map(|(p, contents)| template_from_file(p, contents))
.collect();
let compiled_templates = compiled_templates_result.unwrap();
let main_template_name = &compiled_templates
.first()
.expect("There should be more than 1 template")
.name;
let breadcrumbs = vec![&context as &dyn ContextElement];
.0;
let mut dust_renderer = DustRenderer::new();
compiled_templates.iter().for_each(|(name, template)| {
dust_renderer.load_source(template, name.to_owned());
});
println!(
"{}",
dust_renderer
.render(main_template_name, &breadcrumbs)
.render(main_template_name, Some(&context))
.expect("Failed to render")
);
}
fn template_from_file<'a>(file_path: &str, file_contents: &'a str) -> CompiledTemplate<'a> {
fn template_from_file<'a>(
file_path: &str,
file_contents: &'a str,
) -> Result<(String, Template<'a>), CompileError> {
let path: &Path = Path::new(file_path);
let name = path.file_stem().unwrap();
compile_template(file_contents, name.to_string_lossy().to_string())
.expect("Failed to compile template")
let name = path.file_stem().ok_or(CompileError {
message: format!("Failed to get file stem on {}", file_path),
})?;
Ok((
name.to_string_lossy().to_string(),
compile_template(file_contents)?,
))
}
fn read_context_from_stdin() -> serde_json::Value {
@ -165,61 +175,61 @@ fn apply_filter(
filter: &Filter,
) -> Result<serde_json::Value, RenderError> {
match (json_value, filter) {
// Html escape filter
(serde_json::Value::String(string), Filter::HtmlEncode) => {
Ok(serde_json::Value::String(html_escape(string)))
}
(_, Filter::HtmlEncode) => Ok(serde_json::Value::String(html_escape(
&json_value.render(&Vec::new())?,
))),
// Disable html escape filter
(_, Filter::DisableHtmlEncode) => panic!("The |s filter is automatically removed by the renderer since it is a no-op during rendering."),
// Parse JSON filter
(serde_json::Value::String(string), Filter::JsonParse) => {
serde_json::from_str(&string).or(Err(RenderError::InvalidJson(string.to_owned())))
}
(_, Filter::JsonParse) => {
let rendered_value = json_value.render(&Vec::new())?;
serde_json::from_str(&rendered_value).or(Err(RenderError::InvalidJson(rendered_value)))
}
// Json Stringify filter
(_, Filter::JsonStringify) => {
Ok(serde_json::Value::String(json_value.to_string()))
}
// Javascript escape filter
(serde_json::Value::String(string), Filter::JavascriptStringEncode) => {
Ok(serde_json::Value::String(javascript_escape(string)))
}
(serde_json::Value::Bool(boolean), Filter::JavascriptStringEncode) => {
Ok(serde_json::Value::Bool(*boolean))
}
(serde_json::Value::Number(number), Filter::JavascriptStringEncode) => {
Ok(serde_json::Value::Number(number.clone()))
}
(serde_json::Value::Array(arr), Filter::JavascriptStringEncode) => {
Ok(serde_json::Value::Array(arr.clone()))
}
(serde_json::Value::Object(obj), Filter::JavascriptStringEncode) => {
Ok(serde_json::Value::Object(obj.clone()))
}
(_, Filter::JavascriptStringEncode) => Ok(serde_json::Value::String(javascript_escape(
&json_value.render(&Vec::new())?,
))),
// EncodeURI filter
(serde_json::Value::String(string), Filter::EncodeUri) => {
Ok(serde_json::Value::String(encode_uri(string)))
}
(_, Filter::EncodeUri) => Ok(serde_json::Value::String(encode_uri(
&json_value.render(&Vec::new())?,
))),
// EncodeURIComponent filter
(serde_json::Value::String(string), Filter::EncodeUriComponent) => {
Ok(serde_json::Value::String(encode_uri_component(string)))
}
(_, Filter::EncodeUriComponent) => Ok(serde_json::Value::String(encode_uri_component(
&json_value.render(&Vec::new())?,
))),
}
// Html escape filter
(serde_json::Value::String(string), Filter::HtmlEncode) => {
Ok(serde_json::Value::String(html_escape(string)))
}
(_, Filter::HtmlEncode) => Ok(serde_json::Value::String(html_escape(
&json_value.render(&Vec::new())?,
))),
// Disable html escape filter
(_, Filter::DisableHtmlEncode) => panic!("The |s filter is automatically removed by the renderer since it is a no-op during rendering."),
// Parse JSON filter
(serde_json::Value::String(string), Filter::JsonParse) => {
serde_json::from_str(&string).or(Err(RenderError::InvalidJson(string.to_owned())))
}
(_, Filter::JsonParse) => {
let rendered_value = json_value.render(&Vec::new())?;
serde_json::from_str(&rendered_value).or(Err(RenderError::InvalidJson(rendered_value)))
}
// Json Stringify filter
(_, Filter::JsonStringify) => {
Ok(serde_json::Value::String(json_value.to_string()))
}
// Javascript escape filter
(serde_json::Value::String(string), Filter::JavascriptStringEncode) => {
Ok(serde_json::Value::String(javascript_escape(string)))
}
(serde_json::Value::Bool(boolean), Filter::JavascriptStringEncode) => {
Ok(serde_json::Value::Bool(*boolean))
}
(serde_json::Value::Number(number), Filter::JavascriptStringEncode) => {
Ok(serde_json::Value::Number(number.clone()))
}
(serde_json::Value::Array(arr), Filter::JavascriptStringEncode) => {
Ok(serde_json::Value::Array(arr.clone()))
}
(serde_json::Value::Object(obj), Filter::JavascriptStringEncode) => {
Ok(serde_json::Value::Object(obj.clone()))
}
(_, Filter::JavascriptStringEncode) => Ok(serde_json::Value::String(javascript_escape(
&json_value.render(&Vec::new())?,
))),
// EncodeURI filter
(serde_json::Value::String(string), Filter::EncodeUri) => {
Ok(serde_json::Value::String(encode_uri(string)))
}
(_, Filter::EncodeUri) => Ok(serde_json::Value::String(encode_uri(
&json_value.render(&Vec::new())?,
))),
// EncodeURIComponent filter
(serde_json::Value::String(string), Filter::EncodeUriComponent) => {
Ok(serde_json::Value::String(encode_uri_component(string)))
}
(_, Filter::EncodeUriComponent) => Ok(serde_json::Value::String(encode_uri_component(
&json_value.render(&Vec::new())?,
))),
}
}
fn apply_filters(
@ -276,7 +286,7 @@ impl Renderable for serde_json::Value {
}
impl Walkable for serde_json::Value {
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
match self {
serde_json::Value::Null => Err(WalkError::CantWalk),
serde_json::Value::Bool(_boolean) => Err(WalkError::CantWalk),

@ -23,7 +23,7 @@ use nom::sequence::terminated;
use nom::sequence::tuple;
use nom::IResult;
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub enum DustTag<'a> {
DTSpecial(Special),
DTComment(Comment<'a>),
@ -52,12 +52,12 @@ pub enum Special {
RightCurlyBrace,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub enum IgnoredWhitespace<'a> {
StartOfLine(&'a str),
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct Comment<'a> {
value: &'a str,
}
@ -65,12 +65,12 @@ pub struct Comment<'a> {
/// A series of keys separated by '.' to reference a variable in the context
///
/// Special case: If the path is just "." then keys will be an empty vec
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct Path<'a> {
pub keys: Vec<&'a str>,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct Reference<'a> {
pub path: Path<'a>,
pub filters: Vec<Filter>,
@ -87,12 +87,12 @@ pub enum Filter {
JsonParse,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct Span<'a> {
pub contents: &'a str,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct ParameterizedBlock<'a> {
pub path: Path<'a>,
pub explicit_context: Option<Path<'a>>,
@ -101,32 +101,33 @@ pub struct ParameterizedBlock<'a> {
pub else_contents: Option<Body<'a>>,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct Partial<'a> {
pub name: Vec<PartialNameElement>,
pub explicit_context: Option<Path<'a>>,
pub params: Vec<KVPair<'a>>,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub enum OwnedLiteral {
LString(String),
LPositiveInteger(u64),
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub enum RValue<'a> {
RVPath(Path<'a>),
RVTemplate(Vec<PartialNameElement>),
RVLiteral(OwnedLiteral),
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct KVPair<'a> {
pub key: &'a str,
pub value: RValue<'a>,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub enum PartialNameElement {
PNSpan {
contents: String,
@ -137,17 +138,17 @@ pub enum PartialNameElement {
},
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct Body<'a> {
pub elements: Vec<TemplateElement<'a>>,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub struct Template<'a> {
pub contents: Body<'a>,
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Debug, PartialEq)]
pub enum TemplateElement<'a> {
TESpan(Span<'a>),
TETag(DustTag<'a>),
@ -308,13 +309,25 @@ fn postitive_integer_literal(i: &str) -> IResult<&str, u64> {
)(i)
}
fn template_string_rvalue(i: &str) -> IResult<&str, Vec<PartialNameElement>> {
let (i, template_string) = verify(quoted_string, |s: &String| {
partial_quoted_tag(s.as_str()).is_ok()
})(i)?;
let (_remaining, parsed_template_elements) = partial_quoted_tag(template_string.as_str())
.expect("A successful parse was verified earlier with a call to verify()");
let converted_template_elements = parsed_template_elements
.into_iter()
.map(|e| e.into())
.collect();
Ok((i, converted_template_elements))
}
/// Either a literal or a path to a value
fn rvalue(i: &str) -> IResult<&str, RValue> {
alt((
map(path, RValue::RVPath),
map(quoted_string, |s| {
RValue::RVLiteral(OwnedLiteral::LString(s))
}),
map(template_string_rvalue, RValue::RVTemplate),
map(postitive_integer_literal, |num| {
RValue::RVLiteral(OwnedLiteral::LPositiveInteger(num))
}),
@ -528,9 +541,7 @@ fn partial_with_quoted_tag<'a>(
let (i, (name, maybe_explicit_context, params)) = delimited(
tag(open_matcher),
tuple((
verify(quoted_string, |s: &String| {
partial_quoted_tag(s.as_str()).is_ok()
}),
template_string_rvalue,
opt(preceded(tag(":"), path)),
opt(delimited(
space1,
@ -541,17 +552,10 @@ fn partial_with_quoted_tag<'a>(
tag("/}"),
)(i)?;
let (_remaining, template_name_elements) = partial_quoted_tag(name.as_str())
.expect("A successful parse was verified earlier with a call to verify()");
let partial_name_elements = template_name_elements
.into_iter()
.map(|e| e.into())
.collect();
Ok((
i,
Partial {
name: partial_name_elements,
name: name,
explicit_context: maybe_explicit_context,
params: params.unwrap_or(Vec::new()),
},

@ -0,0 +1,59 @@
use crate::renderer::context_element::ContextElement;
use crate::renderer::context_element::IceResult;
use crate::renderer::context_element::IntoContextElement;
use crate::renderer::context_element::IntoRcIce;
use std::borrow::Borrow;
use std::rc::Rc;
#[derive(Clone, Debug)]
pub enum BreadcrumbTreeElement<'a> {
// Using Rc so that when we need to create BreadcrumbTrees with
// the same BreadcrumbTreeElement but a different parent (for
// example, when inserting behind the tail), we don't need to the
// copy the already owned/malloc'd data.
Owned(Rc<dyn IntoContextElement + 'a>),
Borrowed(&'a dyn IntoContextElement),
}
impl<'a> BreadcrumbTreeElement<'a> {
pub fn from_owned<I: 'a + IntoContextElement>(val: I) -> BreadcrumbTreeElement<'a> {
BreadcrumbTreeElement::Owned(Rc::new(val))
}
pub fn from_borrowed(val: &'a dyn IntoContextElement) -> BreadcrumbTreeElement<'a> {
BreadcrumbTreeElement::Borrowed(val)
}
}
impl<'a> From<&'a IceResult<'a>> for BreadcrumbTreeElement<'a> {
fn from(inp: &'a IceResult<'a>) -> Self {
match inp {
IceResult::Owned(rc_ce) => {
BreadcrumbTreeElement::from_borrowed(rc_ce.from_context_element())
}
IceResult::Borrowed(ce) => {
BreadcrumbTreeElement::from_borrowed(ce.from_context_element())
}
}
}
}
impl<'a> From<IceResult<'a>> for BreadcrumbTreeElement<'a> {
fn from(inp: IceResult<'a>) -> Self {
match inp {
IceResult::Owned(rc_ce) => BreadcrumbTreeElement::Owned(rc_ce.into_rc_ice()),
IceResult::Borrowed(ce) => {
BreadcrumbTreeElement::from_borrowed(ce.from_context_element())
}
}
}
}
impl<'a> Borrow<dyn IntoContextElement + 'a> for BreadcrumbTreeElement<'a> {
fn borrow(&self) -> &(dyn IntoContextElement + 'a) {
match self {
BreadcrumbTreeElement::Owned(ice) => ice.as_ref(),
BreadcrumbTreeElement::Borrowed(ice) => *ice,
}
}
}

@ -1,7 +1,10 @@
use crate::parser::Filter;
use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement;
use crate::renderer::errors::RenderError;
use crate::renderer::errors::WalkError;
use crate::renderer::DustRenderer;
use std::any::Any;
use std::rc::Rc;
use std::{cmp::Ordering, fmt::Debug};
pub trait ContextElement:
@ -10,8 +13,10 @@ pub trait ContextElement:
+ Walkable
+ Renderable
+ Loopable
+ CloneIntoBoxedContextElement
// + CloneIntoBoxedContextElement
+ CompareContextElement
+ FromContextElement
+ IntoRcIce
{
}
@ -20,7 +25,7 @@ pub trait Truthiness {
}
pub trait Walkable {
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError>;
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError>;
/// If an element contains meta information and should not be
/// returned as the final result of a walk, this function should
@ -59,15 +64,15 @@ pub trait CompareContextElement: CastToAny {
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering>;
}
pub trait CloneIntoBoxedContextElement {
fn clone_to_box(&self) -> Box<dyn ContextElement>;
}
// pub trait CloneIntoBoxedContextElement {
// fn clone_to_box(&self) -> Box<dyn IntoContextElement>;
// }
impl<C: 'static + ContextElement + Clone> CloneIntoBoxedContextElement for C {
fn clone_to_box(&self) -> Box<dyn ContextElement> {
Box::new(self.clone())
}
}
// 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 {
@ -86,3 +91,87 @@ impl<'a, 'b> PartialOrd<&'b dyn ContextElement> for &'a dyn ContextElement {
self.partial_compare(*other)
}
}
pub trait FromContextElement {
fn from_context_element(&self) -> &dyn IntoContextElement;
}
impl<C: ContextElement> FromContextElement for C {
fn from_context_element(&self) -> &dyn IntoContextElement {
self
}
}
pub trait IntoContextElement: Debug + Walkable /* + CloneIntoBoxedContextElement*/ {
fn into_context_element<'a>(
&'a self,
renderer: &DustRenderer,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
) -> Option<IceResult<'a>>;
}
impl<C: ContextElement> IntoContextElement for C {
fn into_context_element<'a>(
&'a self,
renderer: &DustRenderer,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
) -> Option<IceResult<'a>> {
Some(IceResult::from_borrowed(self))
}
}
pub trait IntoRcIce {
fn into_rc_ice(self: Rc<Self>) -> Rc<dyn IntoContextElement>;
}
impl<C: 'static + ContextElement> IntoRcIce for C {
fn into_rc_ice(self: Rc<C>) -> Rc<dyn IntoContextElement> {
Rc::clone(&self) as Rc<dyn IntoContextElement>
}
}
#[derive(Clone, Debug)]
pub enum IceResult<'a> {
// Using Rc so that when we need to create BreadcrumbTrees with
// the same BreadcrumbTreeElement but a different parent (for
// example, when inserting behind the tail), we don't need to the
// copy the already owned/malloc'd data.
Owned(Rc<dyn ContextElement>),
Borrowed(&'a dyn ContextElement),
}
impl<'a> IceResult<'a> {
pub fn from_owned<C: 'static + ContextElement>(val: C) -> IceResult<'a> {
IceResult::Owned(Rc::new(val))
}
pub fn from_borrowed(val: &'a dyn ContextElement) -> IceResult<'a> {
IceResult::Borrowed(val)
}
pub fn get_context_element_reference(&self) -> &dyn ContextElement {
match self {
IceResult::Owned(rc_ce) => rc_ce.as_ref(),
IceResult::Borrowed(ce) => *ce,
}
}
}
impl<'a> IntoContextElement for IceResult<'a> {
fn into_context_element<'b>(
&'b self,
renderer: &DustRenderer,
breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>,
) -> Option<IceResult<'b>> {
match self {
IceResult::Owned(rc_ce) => Some(IceResult::from_borrowed(rc_ce.as_ref())),
IceResult::Borrowed(ce) => Some(IceResult::from_borrowed(*ce)),
}
}
}
impl<'a> Walkable for IceResult<'a> {
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
self.get_context_element_reference().walk(segment)
}
}

@ -1,5 +1,9 @@
use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement;
use crate::renderer::context_element::CompareContextElement;
use crate::renderer::context_element::ContextElement;
use crate::renderer::context_element::IceResult;
use crate::renderer::context_element::IntoContextElement;
use crate::renderer::DustRenderer;
use crate::renderer::Loopable;
use crate::renderer::RenderError;
use crate::renderer::Renderable;
@ -15,7 +19,7 @@ use std::cmp::Ordering;
/// Functions the same as the injected parameters contexts for
/// helpers/partials with parameters but this has no need for storing
/// breadcrumbs since its simply storing two integers.
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct IterationContext {
idx: OwnedLiteral,
len: OwnedLiteral,
@ -31,37 +35,18 @@ impl IterationContext {
}
}
impl ContextElement for IterationContext {}
impl Truthiness for IterationContext {
fn is_truthy(&self) -> bool {
// TODO: Would this even ever be called? Won't matter, but I'd
// like to know. Since it is injected 1 above the current
// context, we wouldn't be able to access it with `{.}`.
true
}
}
impl Renderable for IterationContext {
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
// TODO: Would this even ever be called? Won't matter, but I'd
// like to know. Since it is injected 1 above the current
// context, we wouldn't be able to access it with `{.}`.
Ok("[object Object]".to_owned())
}
}
impl Loopable for IterationContext {
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
// TODO: Would this even ever be called? Won't matter, but I'd
// like to know. Since it is injected 1 above the current
// context, we wouldn't be able to access it with `{.}`.
Vec::new()
impl IntoContextElement for IterationContext {
fn into_context_element<'b>(
&'b self,
renderer: &DustRenderer,
breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>,
) -> Option<IceResult<'b>> {
panic!("into_context_element cannot be called on pseudo elements");
}
}
impl Walkable for IterationContext {
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
match segment {
"$idx" => Ok(&self.idx),
"$len" => Ok(&self.len),
@ -73,15 +58,3 @@ impl Walkable for IterationContext {
true
}
}
impl CompareContextElement for IterationContext {
fn equals(&self, other: &dyn ContextElement) -> bool {
// TODO: Does this ever happen? perhaps I should have a panic here.
false
}
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
// TODO: Does this ever happen? perhaps I should have a panic here.
None
}
}

@ -1,16 +1,19 @@
//! This module contains a renderer for a rust implementation of LinkedIn Dust
mod breadcrumb_tree;
mod context_element;
mod errors;
mod inline_partial_tree;
mod iteration_context;
mod parameters_context;
mod renderer;
mod tree_walking;
mod walking;
pub use context_element::CloneIntoBoxedContextElement;
// pub use context_element::CloneIntoBoxedContextElement;
pub use context_element::CompareContextElement;
pub use context_element::ContextElement;
pub use context_element::IntoContextElement;
pub use context_element::Loopable;
pub use context_element::Renderable;
pub use context_element::Truthiness;
@ -19,5 +22,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::CompiledTemplate;
pub use renderer::DustRenderer;

@ -1,106 +1,94 @@
use crate::parser::Filter;
use crate::parser::KVPair;
use crate::parser::{Filter, OwnedLiteral, RValue};
use crate::parser::OwnedLiteral;
use crate::parser::RValue;
use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement;
use crate::renderer::context_element::CompareContextElement;
use crate::renderer::context_element::ContextElement;
use crate::renderer::walking::walk_path;
use crate::renderer::context_element::IceResult;
use crate::renderer::context_element::IntoContextElement;
use crate::renderer::tree_walking::walk_path;
use crate::renderer::DustRenderer;
use crate::renderer::Loopable;
use crate::renderer::RenderError;
use crate::renderer::Renderable;
use crate::renderer::Truthiness;
use crate::renderer::WalkError;
use crate::renderer::Walkable;
use std::{cmp::Ordering, collections::HashMap};
/// Copy the data from an RValue to an Owned struct
///
/// In order to get comparisons to work for our `ContextElement` trait
/// objects, we need to be able to use `std::any::Any`. Unfortunately,
/// `Any` requires that the structs do not have a lifetime (so they
/// will have a `'static` lifetime. This means that we cannot have a
/// `<'a>` appended to the struct type, so the struct cannot contain
/// any borrows. Rather than impose the copy cost in the parser, we
/// are imposing the cost of copying the data in the renderer because
/// the parser has no reason to not be able to reference data from the
/// input string.
#[derive(Clone, Debug)]
pub enum OwnedRValue {
RVPath(OwnedPath),
RVLiteral(OwnedLiteral),
}
#[derive(Clone, Debug, PartialEq)]
pub struct OwnedPath {
pub keys: Vec<String>,
}
impl From<&RValue<'_>> for OwnedRValue {
fn from(original: &RValue<'_>) -> Self {
match original {
RValue::RVLiteral(literal) => OwnedRValue::RVLiteral(literal.clone()),
RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath {
keys: path.keys.iter().map(|k| k.to_string()).collect(),
}),
}
}
}
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Debug)]
pub struct ParametersContext {
params: HashMap<String, OwnedRValue>,
breadcrumbs: Vec<Box<dyn ContextElement>>,
}
impl ParametersContext {
pub fn new(breadcrumbs: &Vec<&dyn ContextElement>, params: &Vec<KVPair>) -> ParametersContext {
let owned_params: HashMap<String, OwnedRValue> = params
.iter()
.map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value)))
.collect();
let owned_breadcrumbs: Vec<Box<dyn ContextElement>> =
breadcrumbs.iter().map(|ce| ce.clone_to_box()).collect();
pub struct ParametersContext<'a> {
params: HashMap<&'a str, (&'a RValue<'a>, Option<BreadcrumbTreeElement<'a>>)>,
}
impl<'a> ParametersContext<'a> {
pub fn new(
renderer: &DustRenderer,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
params: &'a Vec<KVPair>,
) -> Self {
// If the parameter is a Path, then we resolve it immediately
// to a context element because those are resolved using the
// breadcrumbs at the time of assignment.
//
// If the parameter is a template (for example `foo="{bar}"`)
// then those are resolved at the time of access rather than
// the time of assignment, so we leave them into their
// original IntoContextElement state.
let rendered_params: HashMap<&'a str, (&'a RValue<'a>, Option<BreadcrumbTreeElement<'a>>)> =
params
.iter()
.map(|kvpair| {
let k = kvpair.key;
let v: Option<BreadcrumbTreeElement<'a>> = match &kvpair.value {
RValue::RVLiteral(owned_literal) => {
Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value))
}
RValue::RVPath(path) => kvpair
.value
.into_context_element(renderer, breadcrumbs)
.map(std::convert::From::from),
RValue::RVTemplate(template) => {
Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value))
}
};
(k, (&kvpair.value, v))
})
.collect();
ParametersContext {
params: owned_params,
breadcrumbs: owned_breadcrumbs,
params: rendered_params,
}
}
}
impl ContextElement for ParametersContext {}
impl Truthiness for ParametersContext {
fn is_truthy(&self) -> bool {
// TODO: Would this even ever be called? Won't matter, but I'd
// like to know. Since it is injected 1 above the current
// context, we wouldn't be able to access it with `{.}`.
true
pub fn get_original_rvalue(&self, segment: &str) -> Option<&'a RValue<'a>> {
self.params.get(segment).map(|(rvalue, _bte)| *rvalue)
}
}
impl Renderable for ParametersContext {
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
// TODO: Would this even ever be called? Won't matter, but I'd
// like to know. Since it is injected 1 above the current
// context, we wouldn't be able to access it with `{.}`.
Ok("[object Object]".to_owned())
pub fn contains_key(&self, segment: &str) -> bool {
self.params.contains_key(segment)
}
}
impl Loopable for ParametersContext {
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
// TODO: Would this even ever be called? Won't matter, but I'd
// like to know. Since it is injected 1 above the current
// context, we wouldn't be able to access it with `{.}`.
Vec::new()
impl<'a> IntoContextElement for ParametersContext<'a> {
fn into_context_element<'b>(
&'b self,
renderer: &DustRenderer,
breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>,
) -> Option<IceResult<'b>> {
panic!("into_context_element cannot be called on pseudo elements");
}
}
impl Walkable for ParametersContext {
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?;
match rval {
OwnedRValue::RVPath(path) => walk_path(&self.breadcrumbs, &path.keys),
OwnedRValue::RVLiteral(literal) => Ok(literal),
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),
}
}
@ -109,34 +97,30 @@ impl Walkable for ParametersContext {
}
}
impl Clone for ParametersContext {
fn clone(&self) -> Self {
let new_params: HashMap<String, OwnedRValue> = self
.params
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
let new_breadcrumbs: Vec<Box<dyn ContextElement>> = self
.breadcrumbs
.iter()
.map(|bread| bread.clone_to_box())
.collect();
ParametersContext {
params: new_params,
breadcrumbs: new_breadcrumbs,
impl<'a> IntoContextElement for RValue<'a> {
fn into_context_element<'b>(
&'b self,
renderer: &DustRenderer,
breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>,
) -> Option<IceResult<'b>> {
match self {
RValue::RVLiteral(owned_literal) => Some(IceResult::from_borrowed(owned_literal)),
RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys)
.map(|ice| ice.into_context_element(renderer, breadcrumbs))
.ok()
.flatten(),
RValue::RVTemplate(template) => renderer
.render_partial_name(template, breadcrumbs)
.map(|rendered| OwnedLiteral::LString(rendered))
.ok()
.map(|owned_literal| IceResult::from_owned(owned_literal)),
}
}
}
impl CompareContextElement for ParametersContext {
fn equals(&self, other: &dyn ContextElement) -> bool {
// TODO: Does this ever happen? perhaps I should have a panic here.
false
}
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
// TODO: Does this ever happen? perhaps I should have a panic here.
None
impl<'a> Walkable for RValue<'a> {
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
Err(WalkError::CantWalk)
}
}
@ -167,7 +151,7 @@ impl Loopable for OwnedLiteral {
}
impl Walkable for OwnedLiteral {
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
Err(WalkError::CantWalk)
}
}

@ -0,0 +1,264 @@
use crate::parser::KVPair;
use crate::parser::{Filter, OwnedLiteral, PartialNameElement, RValue};
use crate::renderer::context_element::CompareContextElement;
use crate::renderer::context_element::ContextElement;
use crate::renderer::context_element::IntoContextElement;
use crate::renderer::walking::walk_path;
use crate::renderer::DustRenderer;
use crate::renderer::Loopable;
use crate::renderer::RenderError;
use crate::renderer::Renderable;
use crate::renderer::Truthiness;
use crate::renderer::WalkError;
use crate::renderer::Walkable;
use std::{cmp::Ordering, collections::HashMap};
/// Copy the data from an RValue to an Owned struct
///
/// In order to get comparisons to work for our `ContextElement` trait
/// objects, we need to be able to use `std::any::Any`. Unfortunately,
/// `Any` requires that the structs do not have a lifetime (so they
/// will have a `'static` lifetime. This means that we cannot have a
/// `<'a>` appended to the struct type, so the struct cannot contain
/// any borrows. Rather than impose the copy cost in the parser, we
/// are imposing the cost of copying the data in the renderer because
/// the parser has no reason to not be able to reference data from the
/// input string.
#[derive(Clone, Debug)]
pub enum OwnedRValue {
RVPath(OwnedPath),
RVTemplate(Vec<PartialNameElement>),
RVLiteral(OwnedLiteral),
}
#[derive(Clone, Debug, PartialEq)]
pub struct OwnedPath {
pub keys: Vec<String>,
}
impl From<&RValue<'_>> for OwnedRValue {
fn from(original: &RValue<'_>) -> Self {
match original {
RValue::RVLiteral(literal) => OwnedRValue::RVLiteral(literal.clone()),
RValue::RVTemplate(template) => OwnedRValue::RVTemplate(template.clone()),
RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath {
keys: path.keys.iter().map(|k| k.to_string()).collect(),
}),
}
}
}
#[derive(Debug)]
pub struct ParametersContext {
params: HashMap<String, OwnedRValue>,
breadcrumbs: Vec<Box<dyn IntoContextElement>>,
}
impl ParametersContext {
pub fn new(
breadcrumbs: &Vec<&dyn IntoContextElement>,
params: &Vec<KVPair>,
) -> ParametersContext {
let owned_params: HashMap<String, OwnedRValue> = params
.iter()
.map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value)))
.collect();
let owned_breadcrumbs: Vec<Box<dyn IntoContextElement>> =
breadcrumbs.iter().map(|ce| ce.clone_to_box()).collect();
ParametersContext {
params: owned_params,
breadcrumbs: owned_breadcrumbs,
}
}
}
impl ContextElement for ParametersContext {}
impl Truthiness for ParametersContext {
fn is_truthy(&self) -> bool {
// TODO: Would this even ever be called? Won't matter, but I'd
// like to know. Since it is injected 1 above the current
// context, we wouldn't be able to access it with `{.}`.
true
}
}
impl Renderable for ParametersContext {
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
// TODO: Would this even ever be called? Won't matter, but I'd
// like to know. Since it is injected 1 above the current
// context, we wouldn't be able to access it with `{.}`.
Ok("[object Object]".to_owned())
}
}
impl Loopable for ParametersContext {
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
// TODO: Would this even ever be called? Won't matter, but I'd
// like to know. Since it is injected 1 above the current
// context, we wouldn't be able to access it with `{.}`.
Vec::new()
}
}
impl Walkable for ParametersContext {
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?;
match rval {
OwnedRValue::RVPath(path) => walk_path(&self.breadcrumbs, &path.keys),
OwnedRValue::RVTemplate(template) => Ok(template),
OwnedRValue::RVLiteral(literal) => Ok(literal),
}
}
fn is_pseudo_element(&self) -> bool {
true
}
}
impl Clone for ParametersContext {
fn clone(&self) -> Self {
let new_params: HashMap<String, OwnedRValue> = self
.params
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
let new_breadcrumbs: Vec<Box<dyn IntoContextElement>> = self
.breadcrumbs
.iter()
.map(|bread| bread.clone_to_box())
.collect();
ParametersContext {
params: new_params,
breadcrumbs: new_breadcrumbs,
}
}
}
impl CompareContextElement for ParametersContext {
fn equals(&self, other: &dyn ContextElement) -> bool {
// TODO: Does this ever happen? perhaps I should have a panic here.
false
}
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
// TODO: Does this ever happen? perhaps I should have a panic here.
None
}
}
impl ContextElement for OwnedLiteral {}
impl Truthiness for OwnedLiteral {
fn is_truthy(&self) -> bool {
match self {
OwnedLiteral::LString(text) => !text.is_empty(),
OwnedLiteral::LPositiveInteger(num) => true,
}
}
}
impl Renderable for OwnedLiteral {
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
match self {
OwnedLiteral::LString(text) => Ok(text.clone()),
OwnedLiteral::LPositiveInteger(num) => Ok(num.to_string()),
}
}
}
impl Loopable for OwnedLiteral {
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
Vec::new()
}
}
impl Walkable for OwnedLiteral {
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
Err(WalkError::CantWalk)
}
}
impl CompareContextElement for OwnedLiteral {
fn equals(&self, other: &dyn ContextElement) -> bool {
// println!("equals literal {:?} | {:?}", self, other);
// If its an OwnedLiteral then compare them directly,
// otherwise defer to the other type's implementation of
// CompareContextElement since the end user could add any
// type.
match other.to_any().downcast_ref::<Self>() {
None => other.equals(self),
Some(other_literal) => match (self, other_literal) {
(OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => {
self_text == other_text
}
(OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => {
&self_num.to_string() == other_text
}
(OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => {
self_text == &other_num.to_string()
}
(
OwnedLiteral::LPositiveInteger(self_num),
OwnedLiteral::LPositiveInteger(other_num),
) => self_num == other_num,
},
}
}
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering> {
// println!("partial_compare literal {:?} | {:?}", self, other);
// If its an OwnedLiteral then compare them directly,
// otherwise defer to the other type's implementation of
// CompareContextElement since the end user could add any
// type.
match other.to_any().downcast_ref::<Self>() {
None => match other.partial_compare(self) {
None => None,
Some(ord) => match ord {
Ordering::Equal => Some(Ordering::Equal),
Ordering::Greater => Some(Ordering::Less),
Ordering::Less => Some(Ordering::Greater),
},
},
Some(other_literal) => match (self, other_literal) {
(OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => {
self_text.partial_cmp(other_text)
}
(OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => {
self_num.to_string().partial_cmp(other_text)
}
(OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => {
self_text.partial_cmp(&other_num.to_string())
}
(
OwnedLiteral::LPositiveInteger(self_num),
OwnedLiteral::LPositiveInteger(other_num),
) => self_num.partial_cmp(other_num),
},
}
}
}
impl IntoContextElement for Vec<PartialNameElement> {
fn into_context_element(
&self,
renderer: &DustRenderer,
breadcrumbs: &Vec<&dyn IntoContextElement>,
) -> &dyn ContextElement {
// OwnedLiteral::LString(
// renderer
// .render_partial_name(self, breadcrumbs)
// .expect("TODO: Make into_context_element return a RenderError"),
// )
// TODO
&OwnedLiteral::LPositiveInteger(1)
}
}
impl Walkable for Vec<PartialNameElement> {
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
Err(WalkError::CantWalk)
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,94 @@
use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement;
use crate::renderer::context_element::IntoContextElement;
use crate::renderer::WalkError;
use std::borrow::Borrow;
enum WalkResult<'a> {
NoWalk,
PartialWalk,
FullyWalked(&'a dyn IntoContextElement),
}
fn walk_path_from_single_level<'a, P>(
context: &'a dyn IntoContextElement,
path: &[P],
) -> WalkResult<'a>
where
P: Borrow<str>,
{
if path.is_empty() {
return WalkResult::FullyWalked(context);
}
let mut walk_failure = WalkResult::NoWalk;
let mut output = context;
for elem in path.iter() {
match output.walk(elem.borrow()) {
Err(WalkError::CantWalk { .. }) => {
return walk_failure;
}
Ok(new_val) => {
walk_failure = WalkResult::PartialWalk;
output = new_val;
}
}
}
WalkResult::FullyWalked(output)
}
fn get_first_non_pseudo_element<'a>(
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
) -> Option<&'a BreadcrumbTreeElement<'a>> {
breadcrumbs
.iter()
.rev()
.filter(|b| {
!std::borrow::Borrow::<dyn IntoContextElement + 'a>::borrow(*b).is_pseudo_element()
})
.next()
}
pub fn walk_path<'a, P>(
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
path: &Vec<P>,
) -> Result<&'a dyn IntoContextElement, WalkError>
where
P: Borrow<str>,
{
match (breadcrumbs.last(), path.first()) {
(None, _) => return Err(WalkError::CantWalk),
(Some(last_elem), None) => return Ok(last_elem.borrow()),
(Some(_), Some(path_first)) if path_first.borrow() == "." => {
let first_non_pseudo_element = get_first_non_pseudo_element(breadcrumbs);
return match first_non_pseudo_element {
None => Err(WalkError::CantWalk),
Some(current_context) => {
match walk_path_from_single_level(current_context.borrow(), &path[1..]) {
// If no walking was done at all or we partially walked
// then stop trying to find anything because '.' restricts
// us to the current scope
WalkResult::NoWalk | WalkResult::PartialWalk => Err(WalkError::CantWalk),
WalkResult::FullyWalked(new_context) => Ok(new_context),
}
}
};
}
(Some(_), Some(path_first)) => {
for context in breadcrumbs.iter().rev() {
match walk_path_from_single_level(context.borrow(), path) {
// If no walking was done at all, keep looping
WalkResult::NoWalk => {}
// If we partially walked then stop trying to find
// anything
WalkResult::PartialWalk => {
return Err(WalkError::CantWalk);
}
WalkResult::FullyWalked(new_context) => return Ok(new_context),
}
}
}
}
Err(WalkError::CantWalk)
}

@ -1,18 +1,18 @@
use crate::renderer::context_element::ContextElement;
use crate::renderer::context_element::Walkable;
use crate::renderer::context_element::IntoContextElement;
use crate::renderer::WalkError;
use std::borrow::Borrow;
enum WalkResult<'a> {
NoWalk,
PartialWalk,
FullyWalked(&'a dyn ContextElement),
FullyWalked(&'a dyn IntoContextElement),
}
fn walk_path_from_single_level<'a, P, C>(context: &'a C, path: &[P]) -> WalkResult<'a>
where
P: Borrow<str>,
C: Borrow<dyn ContextElement + 'a>,
C: Borrow<dyn IntoContextElement + 'a>,
{
if path.is_empty() {
return WalkResult::FullyWalked(context.borrow());
@ -37,7 +37,7 @@ where
pub fn get_first_non_pseudo_element<'a, B>(breadcrumbs: &'a Vec<B>) -> Option<&B>
where
B: Borrow<dyn ContextElement + 'a>,
B: Borrow<dyn IntoContextElement + 'a>,
{
breadcrumbs
.iter()
@ -49,9 +49,9 @@ where
pub fn walk_path<'a, B, P>(
breadcrumbs: &'a Vec<B>,
path: &Vec<P>,
) -> Result<&'a dyn ContextElement, WalkError>
) -> Result<&'a dyn IntoContextElement, WalkError>
where
B: Borrow<dyn ContextElement + 'a>,
B: Borrow<dyn IntoContextElement + 'a>,
P: Borrow<str>,
{
if breadcrumbs.is_empty() {

Loading…
Cancel
Save