|
|
|
use crate::parser::Filter;
|
|
|
|
use crate::parser::KVPair;
|
|
|
|
use crate::parser::OwnedLiteral;
|
|
|
|
use crate::parser::RValue;
|
|
|
|
use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement;
|
|
|
|
use crate::renderer::comparison_number::compare_json_numbers;
|
|
|
|
use crate::renderer::comparison_number::ComparisonNumber;
|
|
|
|
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<BreadcrumbTreeElement<'a>>)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> ParametersContext<'a> {
|
|
|
|
pub fn new(
|
|
|
|
renderer: &DustRenderer,
|
|
|
|
breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>,
|
|
|
|
params: &'a Vec<KVPair>,
|
|
|
|
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<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 {
|
|
|
|
parent: parent,
|
|
|
|
params: rendered_params,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_values(
|
|
|
|
parent: Option<&'a ParametersContext<'a>>,
|
|
|
|
params: HashMap<&'a str, (&'a RValue<'a>, Option<BreadcrumbTreeElement<'a>>)>,
|
|
|
|
) -> 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<BreadcrumbTreeElement<'b>>,
|
|
|
|
) -> Option<IceResult<'b>> {
|
|
|
|
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<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<'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::LBoolean(boolean) => *boolean,
|
|
|
|
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<Filter>) -> Result<String, RenderError> {
|
|
|
|
match self {
|
|
|
|
OwnedLiteral::LBoolean(boolean) => Ok(boolean.to_string()),
|
|
|
|
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 is_castable(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
OwnedLiteral::LBoolean(_) => false,
|
|
|
|
OwnedLiteral::LFloat(_) => true,
|
|
|
|
OwnedLiteral::LPositiveInteger(_) => true,
|
|
|
|
OwnedLiteral::LNegativeInteger(_) => true,
|
|
|
|
OwnedLiteral::LString(_) => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_size<'a>(&'a self) -> Option<IceResult<'a>> {
|
|
|
|
match self {
|
|
|
|
OwnedLiteral::LBoolean(_) => {
|
|
|
|
Some(IceResult::from_owned(OwnedLiteral::LPositiveInteger(0)))
|
|
|
|
}
|
|
|
|
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<IceResult<'a>> {
|
|
|
|
match (self, target) {
|
|
|
|
(OwnedLiteral::LString(text), "number") => text
|
|
|
|
.parse::<u64>()
|
|
|
|
.map(|num| IceResult::from_owned(OwnedLiteral::LPositiveInteger(num)))
|
|
|
|
.or_else(|_| {
|
|
|
|
text.parse::<i64>()
|
|
|
|
.map(|num| IceResult::from_owned(OwnedLiteral::LNegativeInteger(num)))
|
|
|
|
})
|
|
|
|
.or_else(|_| {
|
|
|
|
text.parse::<f64>()
|
|
|
|
.map(|num| IceResult::from_owned(OwnedLiteral::LFloat(num)))
|
|
|
|
})
|
|
|
|
.ok(),
|
|
|
|
(OwnedLiteral::LBoolean(boolean), "number") => {
|
|
|
|
if *boolean {
|
|
|
|
Some(IceResult::from_owned(OwnedLiteral::LPositiveInteger(1)))
|
|
|
|
} else {
|
|
|
|
Some(IceResult::from_owned(OwnedLiteral::LPositiveInteger(0)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(OwnedLiteral::LPositiveInteger(_), "number") => Some(IceResult::from_borrowed(self)),
|
|
|
|
(OwnedLiteral::LNegativeInteger(_), "number") => Some(IceResult::from_borrowed(self)),
|
|
|
|
(OwnedLiteral::LFloat(_), "number") => Some(IceResult::from_borrowed(self)),
|
|
|
|
|
|
|
|
(OwnedLiteral::LString(_), "string") => Some(IceResult::from_borrowed(self)),
|
|
|
|
(OwnedLiteral::LBoolean(boolean), "string") => Some(IceResult::from_owned(
|
|
|
|
OwnedLiteral::LString(boolean.to_string()),
|
|
|
|
)),
|
|
|
|
(OwnedLiteral::LPositiveInteger(num), "string") => Some(IceResult::from_owned(
|
|
|
|
OwnedLiteral::LString(num.to_string()),
|
|
|
|
)),
|
|
|
|
(OwnedLiteral::LNegativeInteger(num), "string") => Some(IceResult::from_owned(
|
|
|
|
OwnedLiteral::LString(num.to_string()),
|
|
|
|
)),
|
|
|
|
(OwnedLiteral::LFloat(num), "string") => Some(IceResult::from_owned(
|
|
|
|
OwnedLiteral::LString(num.to_string()),
|
|
|
|
)),
|
|
|
|
|
|
|
|
(OwnedLiteral::LString(text), "boolean") => {
|
|
|
|
if text.is_empty() {
|
|
|
|
Some(IceResult::from_owned(OwnedLiteral::LBoolean(false)))
|
|
|
|
} else {
|
|
|
|
Some(IceResult::from_owned(OwnedLiteral::LBoolean(true)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(OwnedLiteral::LBoolean(_), "boolean") => Some(IceResult::from_borrowed(self)),
|
|
|
|
(OwnedLiteral::LPositiveInteger(num), "boolean") => {
|
|
|
|
if *num == 0 {
|
|
|
|
Some(IceResult::from_owned(OwnedLiteral::LBoolean(false)))
|
|
|
|
} else {
|
|
|
|
Some(IceResult::from_owned(OwnedLiteral::LBoolean(true)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(OwnedLiteral::LNegativeInteger(num), "boolean") => {
|
|
|
|
if *num == 0 {
|
|
|
|
Some(IceResult::from_owned(OwnedLiteral::LBoolean(false)))
|
|
|
|
} else {
|
|
|
|
Some(IceResult::from_owned(OwnedLiteral::LBoolean(true)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(OwnedLiteral::LFloat(num), "boolean") => {
|
|
|
|
if *num == 0.0 || num.is_nan() {
|
|
|
|
Some(IceResult::from_owned(OwnedLiteral::LBoolean(false)))
|
|
|
|
} else {
|
|
|
|
Some(IceResult::from_owned(OwnedLiteral::LBoolean(true)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(_, _) => 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::<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::LPositiveInteger(other_num),
|
|
|
|
) => self_num == other_num,
|
|
|
|
(
|
|
|
|
OwnedLiteral::LNegativeInteger(self_num),
|
|
|
|
OwnedLiteral::LNegativeInteger(other_num),
|
|
|
|
) => self_num == other_num,
|
|
|
|
(OwnedLiteral::LBoolean(self_bool), OwnedLiteral::LBoolean(other_bool)) => {
|
|
|
|
self_bool == other_bool
|
|
|
|
}
|
|
|
|
(OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => false,
|
|
|
|
(OwnedLiteral::LBoolean(_), _) | (_, OwnedLiteral::LBoolean(_)) => 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<Ordering> {
|
|
|
|
// 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) {
|
|
|
|
// If they're both strings, compare them directly
|
|
|
|
(OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => {
|
|
|
|
self_text.partial_cmp(other_text)
|
|
|
|
}
|
|
|
|
// Otherwise, convert to numbers and compare them that way
|
|
|
|
(_, _) => return compare_json_numbers(self, other_literal),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn math_add<'a>(&self, other: &dyn ContextElement) -> Option<IceResult<'a>> {
|
|
|
|
// 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::<Self>() {
|
|
|
|
None => other.math_add(self),
|
|
|
|
Some(other_literal) => match (self, other_literal) {
|
|
|
|
(OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => None,
|
|
|
|
(_, _) => (std::convert::Into::<MathNumber>::into(self)
|
|
|
|
+ std::convert::Into::<MathNumber>::into(other_literal))
|
|
|
|
.map(IceResult::from_owned),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn math_subtract<'a>(&self, other: &dyn ContextElement) -> Option<IceResult<'a>> {
|
|
|
|
// 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::<Self>() {
|
|
|
|
None => other.math_subtract(self),
|
|
|
|
Some(other_literal) => match (self, other_literal) {
|
|
|
|
(OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => None,
|
|
|
|
(_, _) => (std::convert::Into::<MathNumber>::into(self)
|
|
|
|
- std::convert::Into::<MathNumber>::into(other_literal))
|
|
|
|
.map(IceResult::from_owned),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn math_multiply<'a>(&self, other: &dyn ContextElement) -> Option<IceResult<'a>> {
|
|
|
|
// 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::<Self>() {
|
|
|
|
None => other.math_multiply(self),
|
|
|
|
Some(other_literal) => match (self, other_literal) {
|
|
|
|
(OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => None,
|
|
|
|
(_, _) => (std::convert::Into::<MathNumber>::into(self)
|
|
|
|
* std::convert::Into::<MathNumber>::into(other_literal))
|
|
|
|
.map(IceResult::from_owned),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn math_divide<'a>(&self, other: &dyn ContextElement) -> Option<IceResult<'a>> {
|
|
|
|
// 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::<Self>() {
|
|
|
|
None => other.math_divide(self),
|
|
|
|
Some(other_literal) => match (self, other_literal) {
|
|
|
|
(OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => None,
|
|
|
|
(_, _) => (std::convert::Into::<MathNumber>::into(self)
|
|
|
|
/ std::convert::Into::<MathNumber>::into(other_literal))
|
|
|
|
.map(IceResult::from_owned),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn math_modulus<'a>(&self, other: &dyn ContextElement) -> Option<IceResult<'a>> {
|
|
|
|
// 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::<Self>() {
|
|
|
|
None => other.math_modulus(self),
|
|
|
|
Some(other_literal) => match (self, other_literal) {
|
|
|
|
(OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => None,
|
|
|
|
(_, _) => (std::convert::Into::<MathNumber>::into(self)
|
|
|
|
% std::convert::Into::<MathNumber>::into(other_literal))
|
|
|
|
.map(IceResult::from_owned),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn math_abs<'a>(&self) -> Option<IceResult<'a>> {
|
|
|
|
std::convert::Into::<MathNumber>::into(self)
|
|
|
|
.math_abs()
|
|
|
|
.map(IceResult::from_owned)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn math_floor<'a>(&self) -> Option<IceResult<'a>> {
|
|
|
|
std::convert::Into::<MathNumber>::into(self)
|
|
|
|
.math_floor()
|
|
|
|
.map(IceResult::from_owned)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn math_ceil<'a>(&self) -> Option<IceResult<'a>> {
|
|
|
|
std::convert::Into::<MathNumber>::into(self)
|
|
|
|
.math_ceil()
|
|
|
|
.map(IceResult::from_owned)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<&OwnedLiteral> for ComparisonNumber {
|
|
|
|
fn from(original: &OwnedLiteral) -> Self {
|
|
|
|
match original {
|
|
|
|
OwnedLiteral::LBoolean(boolean) => {
|
|
|
|
if *boolean {
|
|
|
|
ComparisonNumber::UnsignedInteger(1)
|
|
|
|
} else {
|
|
|
|
ComparisonNumber::UnsignedInteger(0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
OwnedLiteral::LPositiveInteger(num) => ComparisonNumber::UnsignedInteger(*num),
|
|
|
|
OwnedLiteral::LNegativeInteger(num) => ComparisonNumber::SignedInteger(*num),
|
|
|
|
OwnedLiteral::LString(text) => text.into(),
|
|
|
|
OwnedLiteral::LFloat(num) => {
|
|
|
|
if num.is_nan() {
|
|
|
|
ComparisonNumber::Failure
|
|
|
|
} else {
|
|
|
|
ComparisonNumber::Decimal(*num)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|