You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
7.2 KiB
Rust
243 lines
7.2 KiB
Rust
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::ops::Add;
|
|
use std::ops::Div;
|
|
use std::ops::Mul;
|
|
use std::ops::Rem;
|
|
use std::ops::Sub;
|
|
use std::rc::Rc;
|
|
use std::{cmp::Ordering, fmt::Debug};
|
|
|
|
pub trait ContextElement:
|
|
Debug
|
|
+ Truthiness
|
|
+ Walkable
|
|
+ Renderable
|
|
+ Loopable
|
|
+ CompareContextElement
|
|
+ FromContextElement
|
|
+ IntoRcIce
|
|
+ Castable
|
|
+ Sizable
|
|
{
|
|
}
|
|
|
|
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<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.
|
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement>;
|
|
}
|
|
|
|
pub trait Castable {
|
|
fn cast_to_type<'a>(&'a self, target: &str) -> Option<IceResult<'a>>;
|
|
}
|
|
|
|
pub trait Sizable {
|
|
/// Special case: In DustJS the @size helper usually attempts to
|
|
/// cast to a number before calculating the size. The exception to
|
|
/// this is booleans. `Number(true) == 1` but `@size` on any
|
|
/// boolean is always 0. Make this function return false for any
|
|
/// type that casting to a number shouldn't be attempted.
|
|
///
|
|
/// Note: Its fine for objects that cannot be cast to a number to
|
|
/// return true here. False is only needed for cases where casting
|
|
/// to a number would cause a deviation in the final value for
|
|
/// `@size`.
|
|
fn is_castable(&self) -> bool;
|
|
|
|
fn get_size<'a>(&'a self) -> Option<IceResult<'a>>;
|
|
}
|
|
|
|
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<Ordering>;
|
|
|
|
fn math_add<'a>(&self, other: &dyn ContextElement) -> Option<IceResult<'a>>;
|
|
fn math_subtract<'a>(&self, other: &dyn ContextElement) -> Option<IceResult<'a>>;
|
|
fn math_multiply<'a>(&self, other: &dyn ContextElement) -> Option<IceResult<'a>>;
|
|
fn math_divide<'a>(&self, other: &dyn ContextElement) -> Option<IceResult<'a>>;
|
|
fn math_modulus<'a>(&self, other: &dyn ContextElement) -> Option<IceResult<'a>>;
|
|
fn math_abs<'a>(&self) -> Option<IceResult<'a>>;
|
|
fn math_floor<'a>(&self) -> Option<IceResult<'a>>;
|
|
fn math_ceil<'a>(&self) -> Option<IceResult<'a>>;
|
|
}
|
|
|
|
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 {
|
|
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)
|
|
}
|
|
}
|
|
|
|
impl<'a> Add<&'a dyn ContextElement> for &'a dyn ContextElement {
|
|
type Output = Option<IceResult<'a>>;
|
|
|
|
fn add(self, other: &'a dyn ContextElement) -> Self::Output {
|
|
self.math_add(other)
|
|
}
|
|
}
|
|
|
|
impl<'a> Sub<&'a dyn ContextElement> for &'a dyn ContextElement {
|
|
type Output = Option<IceResult<'a>>;
|
|
|
|
fn sub(self, other: &'a dyn ContextElement) -> Self::Output {
|
|
self.math_subtract(other)
|
|
}
|
|
}
|
|
|
|
impl<'a> Mul<&'a dyn ContextElement> for &'a dyn ContextElement {
|
|
type Output = Option<IceResult<'a>>;
|
|
|
|
fn mul(self, other: &'a dyn ContextElement) -> Self::Output {
|
|
self.math_multiply(other)
|
|
}
|
|
}
|
|
|
|
impl<'a> Div<&'a dyn ContextElement> for &'a dyn ContextElement {
|
|
type Output = Option<IceResult<'a>>;
|
|
|
|
fn div(self, other: &'a dyn ContextElement) -> Self::Output {
|
|
self.math_divide(other)
|
|
}
|
|
}
|
|
|
|
impl<'a> Rem<&'a dyn ContextElement> for &'a dyn ContextElement {
|
|
type Output = Option<IceResult<'a>>;
|
|
|
|
fn rem(self, other: &'a dyn ContextElement) -> Self::Output {
|
|
self.math_modulus(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 {
|
|
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)
|
|
}
|
|
}
|