Files
duster/src/renderer/context_element.rs

178 lines
5.0 KiB
Rust
Raw Normal View History

use crate::parser::Filter;
use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement;
use crate::renderer::errors::RenderError;
use crate::renderer::errors::WalkError;
use crate::renderer::DustRenderer;
2020-05-10 17:12:15 -04:00
use std::any::Any;
use std::rc::Rc;
use std::{cmp::Ordering, fmt::Debug};
2020-04-11 20:34:16 -04:00
pub trait ContextElement:
Debug
+ Truthiness
+ Walkable
+ Renderable
+ Loopable
// + CloneIntoBoxedContextElement
+ CompareContextElement
+ FromContextElement
+ IntoRcIce
{
}
2020-04-11 20:34:16 -04:00
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
}
2020-04-11 20:34:16 -04:00
}
pub trait Renderable {
fn render(&self, filters: &Vec<Filter>) -> Result<String, RenderError>;
}
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.
2020-05-09 14:14:22 -04:00
fn get_loop_elements(&self) -> Vec<&dyn ContextElement>;
}
2020-05-10 14:53:12 -04:00
2020-05-10 22:04:41 -04:00
pub trait CastToAny {
2020-05-10 17:12:15 -04:00
fn to_any(&self) -> &dyn Any;
2020-05-10 22:04:41 -04:00
}
2020-05-10 19:16:55 -04:00
2020-05-10 22:04:41 -04:00
pub trait CompareContextElement: CastToAny {
2020-05-10 19:16:55 -04:00
fn equals(&self, other: &dyn ContextElement) -> bool;
fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering>;
2020-05-10 17:12:15 -04:00
}
// 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())
// }
// }
2020-05-10 22:05:48 -04:00
impl<C: 'static + ContextElement> 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 {
2020-05-10 21:00:06 -04:00
self.equals(*other)
}
}
impl<'a, 'b> PartialOrd<&'b dyn ContextElement> for &'a dyn ContextElement {
fn partial_cmp(&self, other: &&'b dyn ContextElement) -> Option<Ordering> {
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>>,
2020-05-31 23:47:20 -04:00
) -> Option<IceResult<'a>>;
}
impl<C: ContextElement> IntoContextElement for C {
fn into_context_element<'a>(
&'a self,
renderer: &DustRenderer,
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
2020-05-31 23:47:20 -04:00
) -> 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>>,
2020-05-31 23:47:20 -04:00
) -> 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)
}
}