Add implementation based on MathNumber.

This commit is contained in:
Tom Alexander 2020-06-13 19:02:56 -04:00
parent df0ae05648
commit d9ce011113
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE

View File

@ -18,9 +18,11 @@ use renderer::Truthiness;
use renderer::WalkError;
use renderer::Walkable;
use std::cmp::Ordering;
use std::convert::TryInto;
use std::env;
use std::fs;
use std::io::{self, Read};
use std::ops::Add;
use std::path::Path;
mod parser;
@ -391,11 +393,9 @@ impl CompareContextElement for serde_json::Value {
}
fn math_add<'a>(&self, other: &dyn ContextElement) -> Option<IceResult<'a>> {
match (
self,
other.to_any().downcast_ref::<Self>(),
other.to_any().downcast_ref::<OwnedLiteral>(),
) {
let other_json = other.to_any().downcast_ref::<Self>();
let other_literal = other.to_any().downcast_ref::<OwnedLiteral>();
match (self, other_json, other_literal) {
// If its neither of those types, then it is unimplemented
(_, None, None) => panic!("Math operation on unimplemented type"),
// Since this is specifically for the math helper, non-primitives are not supported
@ -408,45 +408,13 @@ impl CompareContextElement for serde_json::Value {
| (_, Some(serde_json::Value::String(_)), _)
| (_, _, Some(OwnedLiteral::LString(_))) => None,
// Handle other serde_json::Value
(
serde_json::Value::Number(self_num),
Some(serde_json::Value::Number(other_num)),
_,
) => {
match (
self_num.as_u64(),
other_num.as_u64(),
self_num.as_i64(),
other_num.as_i64(),
self_num.as_f64(),
other_num.as_f64(),
) {
// If both numbers are integers, we can pass them into math_ints
(Some(self_num), Some(other_num), _, _, _, _) => {
math_ints(self_num, other_num, std::ops::Add::add)
.map(IceResult::from_owned)
}
(_, _, Some(self_num), Some(other_num), _, _) => {
math_ints(self_num, other_num, std::ops::Add::add)
.map(IceResult::from_owned)
}
(Some(self_num), _, _, Some(other_num), _, _) => {
math_ints(self_num, other_num, std::ops::Add::add)
.map(IceResult::from_owned)
}
(_, Some(other_num), Some(self_num), _, _, _) => {
math_ints(self_num, other_num, std::ops::Add::add)
.map(IceResult::from_owned)
}
// Otherwise they'll be floats and we can just do the math directly
(_, _, _, _, Some(self_num), Some(other_num)) => Some(IceResult::from_owned(
OwnedLiteral::LFloat(self_num + other_num),
)),
_ => panic!("Unhandled operation, some integer must not have cast to a float."),
}
}
(_, Some(other_json_value), _) => (std::convert::Into::<MathNumber>::into(self)
+ std::convert::Into::<MathNumber>::into(other_json_value))
.map(IceResult::from_owned),
// Handle literals
_ => todo!(),
(_, _, Some(other_literal)) => (std::convert::Into::<MathNumber>::into(self)
+ std::convert::Into::<MathNumber>::into(other_literal))
.map(IceResult::from_owned),
}
}
}
@ -593,6 +561,91 @@ where
}
}
#[derive(Debug)]
enum MathNumber {
Integer(i128),
Decimal(f64),
Failure,
}
impl From<&serde_json::Value> for MathNumber {
fn from(original: &serde_json::Value) -> Self {
match original {
serde_json::Value::Null => MathNumber::Integer(0),
serde_json::Value::Bool(boolean) => {
if *boolean {
MathNumber::Integer(1)
} else {
MathNumber::Integer(0)
}
}
serde_json::Value::Number(num) => num.into(),
serde_json::Value::String(text) => {
panic!("Strings should not be cast to numbers for math")
}
serde_json::Value::Array(_) => {
panic!("Only primitives should be cast to numbers for comparisons")
}
serde_json::Value::Object(_) => {
panic!("Only primitives should be cast to numbers for comparisons")
}
}
}
}
impl From<&serde_json::Number> for MathNumber {
fn from(original: &serde_json::Number) -> Self {
match original.as_u64() {
Some(num) => return MathNumber::Integer(num.try_into().unwrap()),
None => (),
};
match original.as_i64() {
Some(num) => return MathNumber::Integer(num.into()),
None => (),
};
match original.as_f64() {
Some(num) => return MathNumber::Decimal(num),
None => (),
};
MathNumber::Failure
}
}
impl From<&OwnedLiteral> for MathNumber {
fn from(original: &OwnedLiteral) -> Self {
match original {
OwnedLiteral::LString(_) => panic!("Strings should not be cast to numbers for math"),
OwnedLiteral::LPositiveInteger(num) => {
return MathNumber::Integer((*num).try_into().unwrap())
}
OwnedLiteral::LNegativeInteger(num) => return MathNumber::Integer((*num).into()),
OwnedLiteral::LFloat(num) => return MathNumber::Decimal(*num),
}
}
}
impl Add<MathNumber> for MathNumber {
type Output = Option<OwnedLiteral>;
fn add(self, other: MathNumber) -> Self::Output {
match (self, other) {
(MathNumber::Failure, _) | (_, MathNumber::Failure) => None,
(MathNumber::Integer(self_num), MathNumber::Integer(other_num)) => {
math_ints(self_num, other_num, std::ops::Add::add)
}
(MathNumber::Decimal(self_num), MathNumber::Decimal(other_num)) => {
Some(OwnedLiteral::LFloat(self_num + other_num))
}
(MathNumber::Integer(self_num), MathNumber::Decimal(other_num)) => {
Some(OwnedLiteral::LFloat((self_num as f64) + other_num))
}
(MathNumber::Decimal(self_num), MathNumber::Integer(other_num)) => {
Some(OwnedLiteral::LFloat(self_num + (other_num as f64)))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;