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: Debug + Truthiness + Walkable + Renderable + Loopable // + CloneIntoBoxedContextElement + CompareContextElement + FromContextElement + IntoRcIce { } pub trait Truthiness { fn is_truthy(&self) -> bool; } pub trait Walkable { 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 /// return true. /// /// For example, the iteration context contains $idx and $len but /// it should not be the result of a dot-reference like `{.}`. fn is_pseudo_element(&self) -> bool { false } } pub trait Renderable { fn render(&self, filters: &Vec) -> Result; } pub trait Loopable { /// Return the elements for a Dust section /// /// Sections in dust are accomplished with the {#path} syntax. If /// its an array-like value then it will render n-times, once for /// each element of the array. If this is a scalar value, then /// return an empty array. Sections with scalar values will still /// be rendered (only once) if their truthiness check comes back /// true. fn get_loop_elements(&self) -> Vec<&dyn ContextElement>; } pub trait CastToAny { fn to_any(&self) -> &dyn Any; } pub trait CompareContextElement: CastToAny { fn equals(&self, other: &dyn ContextElement) -> bool; fn partial_compare(&self, other: &dyn ContextElement) -> Option; } // pub trait CloneIntoBoxedContextElement { // fn clone_to_box(&self) -> Box; // } // impl CloneIntoBoxedContextElement for C { // fn clone_to_box(&self) -> Box { // Box::new(self.clone()) // } // } impl CastToAny for C { fn to_any(&self) -> &dyn Any { self } } impl<'a, 'b> PartialEq<&'b dyn ContextElement> for &'a dyn ContextElement { fn eq(&self, other: &&'b dyn ContextElement) -> bool { self.equals(*other) } } impl<'a, 'b> PartialOrd<&'b dyn ContextElement> for &'a dyn ContextElement { fn partial_cmp(&self, other: &&'b dyn ContextElement) -> Option { self.partial_compare(*other) } } pub trait FromContextElement { fn from_context_element(&self) -> &dyn IntoContextElement; } impl 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>, ) -> Option>; } impl IntoContextElement for C { fn into_context_element<'a>( &'a self, renderer: &DustRenderer, breadcrumbs: &'a Vec>, ) -> Option> { Some(IceResult::from_borrowed(self)) } } pub trait IntoRcIce { fn into_rc_ice(self: Rc) -> Rc; } impl IntoRcIce for C { fn into_rc_ice(self: Rc) -> Rc { Rc::clone(&self) as Rc } } #[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), Borrowed(&'a dyn ContextElement), } impl<'a> IceResult<'a> { pub fn from_owned(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>, ) -> Option> { 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) } }