use crate::parser::Filter; use crate::parser::KVPair; use crate::parser::OwnedLiteral; use crate::parser::PartialNameElement; 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::context_element::IceResult; use crate::renderer::context_element::IntoContextElement; use crate::renderer::math::MathNumber; use crate::renderer::walking::walk_path; use crate::renderer::Castable; use crate::renderer::DustRenderer; use crate::renderer::Loopable; use crate::renderer::RenderError; use crate::renderer::Renderable; use crate::renderer::Sizable; use crate::renderer::Truthiness; use crate::renderer::WalkError; use crate::renderer::Walkable; use std::borrow::Borrow; use std::cmp::Ordering; use std::collections::HashMap; use std::convert::TryInto; #[derive(Debug)] pub struct ParametersContext<'a> { parent: Option<&'a ParametersContext<'a>>, params: HashMap<&'a str, (&'a RValue<'a>, Option>)>, } impl<'a> ParametersContext<'a> { pub fn new( renderer: &DustRenderer, breadcrumbs: &'a Vec>, params: &'a Vec, parent: Option<&'a ParametersContext<'a>>, ) -> 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>)> = params .iter() .map(|kvpair| { let k = kvpair.key; let v: Option> = 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 { parent: parent, params: rendered_params, } } pub fn from_values( parent: Option<&'a ParametersContext<'a>>, params: HashMap<&'a str, (&'a RValue<'a>, Option>)>, ) -> Self { ParametersContext { parent: parent, params: params, } } pub fn contains_key(&self, segment: &str) -> bool { self.params.contains_key(segment) || self .parent .map(|p| p.contains_key(segment)) .unwrap_or(false) } pub fn get_original_rvalue(&self, segment: &str) -> Option<&'a RValue<'a>> { self.params .get(segment) .map(|(rvalue, _bte)| *rvalue) .or_else(|| { self.parent .map(|p| p.get_original_rvalue(segment)) .flatten() }) } } impl<'a> IntoContextElement for ParametersContext<'a> { fn into_context_element<'b>( &'b self, _renderer: &DustRenderer, _breadcrumbs: &'b Vec>, ) -> Option> { panic!("into_context_element cannot be called on pseudo elements"); } } 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()), Some(None) => Err(WalkError::CantWalk), None => self .parent .map(|p| p.walk(segment)) .unwrap_or(Err(WalkError::CantWalk)), } } fn is_pseudo_element(&self) -> bool { true } } impl<'a> IntoContextElement for RValue<'a> { fn into_context_element<'b>( &'b self, renderer: &DustRenderer, breadcrumbs: &'b Vec>, ) -> Option> { 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<'a> Walkable for RValue<'a> { fn walk(&self, _segment: &str) -> Result<&dyn IntoContextElement, WalkError> { Err(WalkError::CantWalk) } } 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, OwnedLiteral::LNegativeInteger(_num) => true, OwnedLiteral::LFloat(_num) => true, } } } impl Renderable for OwnedLiteral { fn render(&self, _filters: &Vec) -> Result { match self { OwnedLiteral::LString(text) => Ok(text.clone()), OwnedLiteral::LPositiveInteger(num) => Ok(num.to_string()), OwnedLiteral::LNegativeInteger(num) => Ok(num.to_string()), OwnedLiteral::LFloat(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 Sizable for OwnedLiteral { fn get_size<'a>(&'a self) -> Option> { match self { OwnedLiteral::LFloat(_) => Some(IceResult::from_borrowed(self)), OwnedLiteral::LPositiveInteger(_) => Some(IceResult::from_borrowed(self)), OwnedLiteral::LNegativeInteger(_) => Some(IceResult::from_borrowed(self)), OwnedLiteral::LString(text) => Some(IceResult::from_owned( OwnedLiteral::LPositiveInteger(text.len().try_into().unwrap()), )), } } } impl Castable for OwnedLiteral { fn cast_to_type<'a>(&'a self, target: &str) -> Option> { match (self, target) { (OwnedLiteral::LString(text), "number") => text .parse::() .map(|num| IceResult::from_owned(OwnedLiteral::LPositiveInteger(num))) .or_else(|_| { text.parse::() .map(|num| IceResult::from_owned(OwnedLiteral::LNegativeInteger(num))) }) .or_else(|_| { text.parse::() .map(|num| IceResult::from_owned(OwnedLiteral::LFloat(num))) }) .ok(), (OwnedLiteral::LPositiveInteger(_), "number") => Some(IceResult::from_borrowed(self)), (OwnedLiteral::LNegativeInteger(_), "number") => Some(IceResult::from_borrowed(self)), (OwnedLiteral::LFloat(_), "number") => Some(IceResult::from_borrowed(self)), (_, _) => panic!("Unimplemented cast"), } } } impl CompareContextElement for OwnedLiteral { fn equals(&self, other: &dyn ContextElement) -> bool { // println!("Literal equality check {:?} == {:?}", 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::() { 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::LPositiveInteger(other_num), ) => self_num == other_num, ( OwnedLiteral::LNegativeInteger(self_num), OwnedLiteral::LNegativeInteger(other_num), ) => self_num == other_num, (OwnedLiteral::LString(_self_text), _) | (_, OwnedLiteral::LString(_self_text)) => { false } (OwnedLiteral::LFloat(self_num), OwnedLiteral::LFloat(other_num)) => { self_num == other_num } (OwnedLiteral::LFloat(self_num), OwnedLiteral::LPositiveInteger(other_num)) => { *self_num == (*other_num as f64) } (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LFloat(other_num)) => { (*self_num as f64) == *other_num } (OwnedLiteral::LFloat(self_num), OwnedLiteral::LNegativeInteger(other_num)) => { *self_num == (*other_num as f64) } (OwnedLiteral::LNegativeInteger(self_num), OwnedLiteral::LFloat(other_num)) => { (*self_num as f64) == *other_num } ( OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LNegativeInteger(other_num), ) => { if *self_num < std::i64::MAX as u64 { (*self_num as i64) == *other_num } else { false } } ( OwnedLiteral::LNegativeInteger(self_num), OwnedLiteral::LPositiveInteger(other_num), ) => { if *other_num < std::i64::MAX as u64 { *self_num == (*other_num as i64) } else { false } } }, } } fn partial_compare(&self, other: &dyn ContextElement) -> Option { // 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::() { 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::LPositiveInteger(other_num), ) => self_num.partial_cmp(other_num), ( OwnedLiteral::LNegativeInteger(self_num), OwnedLiteral::LNegativeInteger(other_num), ) => self_num.partial_cmp(other_num), (OwnedLiteral::LFloat(self_num), OwnedLiteral::LFloat(other_num)) => { self_num.partial_cmp(other_num) } (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::LNegativeInteger(self_num), OwnedLiteral::LString(other_text)) => { self_num.to_string().partial_cmp(other_text) } (OwnedLiteral::LString(self_text), OwnedLiteral::LNegativeInteger(other_num)) => { self_text.partial_cmp(&other_num.to_string()) } (OwnedLiteral::LFloat(self_num), OwnedLiteral::LString(other_text)) => { self_num.to_string().partial_cmp(other_text) } (OwnedLiteral::LString(self_text), OwnedLiteral::LFloat(other_num)) => { self_text.partial_cmp(&other_num.to_string()) } (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LFloat(other_num)) => { (*self_num as f64).partial_cmp(other_num) } (OwnedLiteral::LFloat(self_num), OwnedLiteral::LPositiveInteger(other_num)) => { self_num.partial_cmp(&(*other_num as f64)) } (OwnedLiteral::LNegativeInteger(self_num), OwnedLiteral::LFloat(other_num)) => { (*self_num as f64).partial_cmp(other_num) } (OwnedLiteral::LFloat(self_num), OwnedLiteral::LNegativeInteger(other_num)) => { self_num.partial_cmp(&(*other_num as f64)) } ( OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LNegativeInteger(other_num), ) => { if *self_num < std::i64::MAX as u64 { (*self_num as i64).partial_cmp(other_num) } else { Some(Ordering::Greater) } } ( OwnedLiteral::LNegativeInteger(self_num), OwnedLiteral::LPositiveInteger(other_num), ) => { if *other_num < std::i64::MAX as u64 { self_num.partial_cmp(&(*other_num as i64)) } else { Some(Ordering::Less) } } }, } } fn math_add<'a>(&self, other: &dyn ContextElement) -> Option> { // If its an OwnedLiteral then add 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::() { None => other.math_add(self), Some(other_literal) => match (self, other_literal) { (OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => None, (_, _) => (std::convert::Into::::into(self) + std::convert::Into::::into(other_literal)) .map(IceResult::from_owned), }, } } fn math_subtract<'a>(&self, other: &dyn ContextElement) -> Option> { // If its an OwnedLiteral then subtract 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::() { None => other.math_subtract(self), Some(other_literal) => match (self, other_literal) { (OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => None, (_, _) => (std::convert::Into::::into(self) - std::convert::Into::::into(other_literal)) .map(IceResult::from_owned), }, } } fn math_multiply<'a>(&self, other: &dyn ContextElement) -> Option> { // If its an OwnedLiteral then multiply 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::() { None => other.math_multiply(self), Some(other_literal) => match (self, other_literal) { (OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => None, (_, _) => (std::convert::Into::::into(self) * std::convert::Into::::into(other_literal)) .map(IceResult::from_owned), }, } } fn math_divide<'a>(&self, other: &dyn ContextElement) -> Option> { // If its an OwnedLiteral then divide 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::() { None => other.math_divide(self), Some(other_literal) => match (self, other_literal) { (OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => None, (_, _) => (std::convert::Into::::into(self) / std::convert::Into::::into(other_literal)) .map(IceResult::from_owned), }, } } fn math_modulus<'a>(&self, other: &dyn ContextElement) -> Option> { // If its an OwnedLiteral then modulus 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::() { None => other.math_modulus(self), Some(other_literal) => match (self, other_literal) { (OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => None, (_, _) => (std::convert::Into::::into(self) % std::convert::Into::::into(other_literal)) .map(IceResult::from_owned), }, } } fn math_abs<'a>(&self) -> Option> { std::convert::Into::::into(self) .math_abs() .map(IceResult::from_owned) } fn math_floor<'a>(&self) -> Option> { std::convert::Into::::into(self) .math_floor() .map(IceResult::from_owned) } fn math_ceil<'a>(&self) -> Option> { std::convert::Into::::into(self) .math_ceil() .map(IceResult::from_owned) } }