Merge branch 'helper_type_cast' into render
This commit is contained in:
commit
bd8b74984e
@ -30,4 +30,13 @@ Do objects with different paths referencing the same variable match?{~n}
|
|||||||
|
|
||||||
Floating point equality{~n}
|
Floating point equality{~n}
|
||||||
======================={~n}
|
======================={~n}
|
||||||
{@eq key=int value=7.0}int is equal to 7.0{:else}int is not equal to 7.0{/eq}
|
{@eq key=int value=7.0}int is equal to 7.0{~n}{:else}int is not equal to 7.0{~n}{/eq}
|
||||||
|
|
||||||
|
Type cast{~n}
|
||||||
|
========={~n}
|
||||||
|
{@eq key=int value="7"}int is equal to "7"{~n}{:else}int is not equal to "7"{~n}{/eq}
|
||||||
|
{@eq key=int value="7" type="number"}int is equal to "7"::number{~n}{:else}int is not equal to "7"::number{~n}{/eq}
|
||||||
|
{@eq key=beta value=21 type="string"}beta is equal to 21::string{~n}{:else}beta is not equal to 21::string{~n}{/eq}
|
||||||
|
{@eq key=beta value="21" type="string"}beta is equal to "21"::string{~n}{:else}beta is not equal to "21"::string{~n}{/eq}
|
||||||
|
{@eq key=1 value=true_value type="number"}1 is equal to true_value::number{~n}{:else}1 is not equal to true_value::number{~n}{/eq}
|
||||||
|
{@eq key=0 value=false_value type="number"}0 is equal to false_value::number{~n}{:else}0 is not equal to false_value::number{~n}{/eq}
|
||||||
|
181
src/bin.rs
181
src/bin.rs
@ -4,8 +4,10 @@ use crate::renderer::CompareContextElement;
|
|||||||
use parser::Filter;
|
use parser::Filter;
|
||||||
use parser::OwnedLiteral;
|
use parser::OwnedLiteral;
|
||||||
use parser::Template;
|
use parser::Template;
|
||||||
|
use renderer::compare_json_numbers;
|
||||||
use renderer::compile_template;
|
use renderer::compile_template;
|
||||||
use renderer::Castable;
|
use renderer::Castable;
|
||||||
|
use renderer::ComparisonNumber;
|
||||||
use renderer::CompileError;
|
use renderer::CompileError;
|
||||||
use renderer::ContextElement;
|
use renderer::ContextElement;
|
||||||
use renderer::DustRenderer;
|
use renderer::DustRenderer;
|
||||||
@ -315,6 +317,17 @@ impl Loopable for serde_json::Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Sizable for serde_json::Value {
|
impl Sizable for serde_json::Value {
|
||||||
|
fn is_castable(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
serde_json::Value::Null => true,
|
||||||
|
serde_json::Value::Bool(_) => false,
|
||||||
|
serde_json::Value::Number(_) => true,
|
||||||
|
serde_json::Value::String(_) => true,
|
||||||
|
serde_json::Value::Array(_) => true,
|
||||||
|
serde_json::Value::Object(_) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_size<'a>(&'a self) -> Option<IceResult<'a>> {
|
fn get_size<'a>(&'a self) -> Option<IceResult<'a>> {
|
||||||
match self {
|
match self {
|
||||||
serde_json::Value::Null => {
|
serde_json::Value::Null => {
|
||||||
@ -353,10 +366,64 @@ impl Castable for serde_json::Value {
|
|||||||
})
|
})
|
||||||
.ok(),
|
.ok(),
|
||||||
(serde_json::Value::Number(_), "number") => Some(IceResult::from_borrowed(self)),
|
(serde_json::Value::Number(_), "number") => Some(IceResult::from_borrowed(self)),
|
||||||
(serde_json::Value::Null, "number") => None,
|
(serde_json::Value::Null, "number") => {
|
||||||
(serde_json::Value::Bool(_), "number") => None,
|
Some(IceResult::from_owned(serde_json::Value::Number(0.into())))
|
||||||
|
}
|
||||||
|
(serde_json::Value::Bool(boolean), "number") => {
|
||||||
|
if *boolean {
|
||||||
|
Some(IceResult::from_owned(serde_json::Value::Number(1.into())))
|
||||||
|
} else {
|
||||||
|
Some(IceResult::from_owned(serde_json::Value::Number(0.into())))
|
||||||
|
}
|
||||||
|
}
|
||||||
(serde_json::Value::Array(_), "number") => None,
|
(serde_json::Value::Array(_), "number") => None,
|
||||||
(serde_json::Value::Object(_), "number") => None,
|
(serde_json::Value::Object(_), "number") => None,
|
||||||
|
|
||||||
|
(serde_json::Value::String(_), "string") => Some(IceResult::from_borrowed(self)),
|
||||||
|
(serde_json::Value::Number(num), "string") => Some(IceResult::from_owned(
|
||||||
|
serde_json::Value::String(num.to_string()),
|
||||||
|
)),
|
||||||
|
(serde_json::Value::Null, "string") => Some(IceResult::from_owned(
|
||||||
|
serde_json::Value::String("null".to_owned()),
|
||||||
|
)),
|
||||||
|
(serde_json::Value::Bool(boolean), "string") => Some(IceResult::from_owned(
|
||||||
|
serde_json::Value::String(boolean.to_string()),
|
||||||
|
)),
|
||||||
|
(serde_json::Value::Array(_), "string") => Some(IceResult::from_owned(
|
||||||
|
serde_json::Value::String(self.render(&Vec::new()).unwrap_or("".to_owned())),
|
||||||
|
)),
|
||||||
|
(serde_json::Value::Object(_), "string") => Some(IceResult::from_owned(
|
||||||
|
serde_json::Value::String(self.render(&Vec::new()).unwrap_or("".to_owned())),
|
||||||
|
)),
|
||||||
|
|
||||||
|
(serde_json::Value::String(text), "boolean") => {
|
||||||
|
if text.is_empty() {
|
||||||
|
Some(IceResult::from_owned(serde_json::Value::Bool(false)))
|
||||||
|
} else {
|
||||||
|
Some(IceResult::from_owned(serde_json::Value::Bool(true)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(serde_json::Value::Number(json_num), "boolean") => {
|
||||||
|
Some(IceResult::from_owned(serde_json::Value::Bool(
|
||||||
|
match (json_num.as_u64(), json_num.as_i64(), json_num.as_f64()) {
|
||||||
|
(Some(num), _, _) => num != 0,
|
||||||
|
(_, Some(num), _) => num != 0,
|
||||||
|
(_, _, Some(num)) => num != 0.0 && !num.is_nan(),
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
(serde_json::Value::Null, "boolean") => {
|
||||||
|
Some(IceResult::from_owned(serde_json::Value::Bool(false)))
|
||||||
|
}
|
||||||
|
(serde_json::Value::Bool(_), "boolean") => Some(IceResult::from_borrowed(self)),
|
||||||
|
(serde_json::Value::Array(_), "boolean") => {
|
||||||
|
Some(IceResult::from_owned(serde_json::Value::Bool(true)))
|
||||||
|
}
|
||||||
|
(serde_json::Value::Object(_), "boolean") => {
|
||||||
|
Some(IceResult::from_owned(serde_json::Value::Bool(true)))
|
||||||
|
}
|
||||||
|
|
||||||
(_, _) => panic!("Unimplemented cast"),
|
(_, _) => panic!("Unimplemented cast"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,6 +451,9 @@ impl CompareContextElement for serde_json::Value {
|
|||||||
Some(OwnedLiteral::LString(other_string)) => {
|
Some(OwnedLiteral::LString(other_string)) => {
|
||||||
return self.as_str().map_or(false, |s| s == other_string)
|
return self.as_str().map_or(false, |s| s == other_string)
|
||||||
}
|
}
|
||||||
|
Some(OwnedLiteral::LBoolean(boolean)) => {
|
||||||
|
return self.equals(&serde_json::Value::Bool(*boolean) as &dyn ContextElement);
|
||||||
|
}
|
||||||
Some(OwnedLiteral::LPositiveInteger(other_num)) => {
|
Some(OwnedLiteral::LPositiveInteger(other_num)) => {
|
||||||
let other_json_num: serde_json::Number = std::convert::From::from(*other_num);
|
let other_json_num: serde_json::Number = std::convert::From::from(*other_num);
|
||||||
return self
|
return self
|
||||||
@ -435,7 +505,7 @@ impl CompareContextElement for serde_json::Value {
|
|||||||
_,
|
_,
|
||||||
Some(OwnedLiteral::LString(other_string)),
|
Some(OwnedLiteral::LString(other_string)),
|
||||||
) => return self_string.partial_cmp(&other_string),
|
) => return self_string.partial_cmp(&other_string),
|
||||||
// Otherwise, convert to numbers are compare them that way
|
// Otherwise, convert to numbers and compare them that way
|
||||||
(_, Some(json_other), _) => return compare_json_numbers(self, json_other),
|
(_, Some(json_other), _) => return compare_json_numbers(self, json_other),
|
||||||
(_, _, Some(literal_other)) => return compare_json_numbers(self, literal_other),
|
(_, _, Some(literal_other)) => return compare_json_numbers(self, literal_other),
|
||||||
_ => panic!("Unimplemented comparison type."),
|
_ => panic!("Unimplemented comparison type."),
|
||||||
@ -591,14 +661,6 @@ impl CompareContextElement for serde_json::Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum ComparisonNumber {
|
|
||||||
UnsignedInteger(u64),
|
|
||||||
SignedInteger(i64),
|
|
||||||
Decimal(f64),
|
|
||||||
Failure,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&serde_json::Value> for ComparisonNumber {
|
impl From<&serde_json::Value> for ComparisonNumber {
|
||||||
/// Convert from a JSON value to a number for comparison based on
|
/// Convert from a JSON value to a number for comparison based on
|
||||||
/// the logic described at
|
/// the logic described at
|
||||||
@ -625,24 +687,6 @@ impl From<&serde_json::Value> for ComparisonNumber {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&String> for ComparisonNumber {
|
|
||||||
fn from(original: &String) -> Self {
|
|
||||||
match original.parse::<u64>() {
|
|
||||||
Ok(num) => return ComparisonNumber::UnsignedInteger(num),
|
|
||||||
Err(_) => (),
|
|
||||||
};
|
|
||||||
match original.parse::<i64>() {
|
|
||||||
Ok(num) => return ComparisonNumber::SignedInteger(num),
|
|
||||||
Err(_) => (),
|
|
||||||
};
|
|
||||||
match original.parse::<f64>() {
|
|
||||||
Ok(num) => return ComparisonNumber::Decimal(num),
|
|
||||||
Err(_) => (),
|
|
||||||
};
|
|
||||||
ComparisonNumber::Failure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&serde_json::Number> for ComparisonNumber {
|
impl From<&serde_json::Number> for ComparisonNumber {
|
||||||
fn from(original: &serde_json::Number) -> Self {
|
fn from(original: &serde_json::Number) -> Self {
|
||||||
match original.as_u64() {
|
match original.as_u64() {
|
||||||
@ -661,85 +705,6 @@ impl From<&serde_json::Number> for ComparisonNumber {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&OwnedLiteral> for ComparisonNumber {
|
|
||||||
fn from(original: &OwnedLiteral) -> Self {
|
|
||||||
match original {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compare json numbers
|
|
||||||
///
|
|
||||||
/// While this function can be called with two strings, it would not
|
|
||||||
/// make sense because javascript does not use numeric comparisons for
|
|
||||||
/// strings
|
|
||||||
fn compare_json_numbers<S, O>(self_input: S, other_input: O) -> Option<Ordering>
|
|
||||||
where
|
|
||||||
S: Into<ComparisonNumber>,
|
|
||||||
O: Into<ComparisonNumber>,
|
|
||||||
{
|
|
||||||
let self_number: ComparisonNumber = self_input.into();
|
|
||||||
let other_number: ComparisonNumber = other_input.into();
|
|
||||||
match (self_number, other_number) {
|
|
||||||
(ComparisonNumber::Failure, _) => return None,
|
|
||||||
(_, ComparisonNumber::Failure) => return None,
|
|
||||||
(
|
|
||||||
ComparisonNumber::UnsignedInteger(self_num),
|
|
||||||
ComparisonNumber::UnsignedInteger(other_num),
|
|
||||||
) => return self_num.partial_cmp(&other_num),
|
|
||||||
(
|
|
||||||
ComparisonNumber::UnsignedInteger(self_num),
|
|
||||||
ComparisonNumber::SignedInteger(other_num),
|
|
||||||
) => {
|
|
||||||
if self_num < std::i64::MAX as u64 {
|
|
||||||
return (self_num as i64).partial_cmp(&other_num);
|
|
||||||
} else {
|
|
||||||
return Some(Ordering::Greater);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(ComparisonNumber::UnsignedInteger(self_num), ComparisonNumber::Decimal(other_num)) => {
|
|
||||||
return (self_num as f64).partial_cmp(&other_num)
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
|
||||||
ComparisonNumber::SignedInteger(self_num),
|
|
||||||
ComparisonNumber::UnsignedInteger(other_num),
|
|
||||||
) => {
|
|
||||||
if other_num < std::i64::MAX as u64 {
|
|
||||||
return self_num.partial_cmp(&(other_num as i64));
|
|
||||||
} else {
|
|
||||||
return Some(Ordering::Less);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(ComparisonNumber::SignedInteger(self_num), ComparisonNumber::SignedInteger(other_num)) => {
|
|
||||||
return self_num.partial_cmp(&other_num)
|
|
||||||
}
|
|
||||||
(ComparisonNumber::SignedInteger(self_num), ComparisonNumber::Decimal(other_num)) => {
|
|
||||||
return (self_num as f64).partial_cmp(&other_num)
|
|
||||||
}
|
|
||||||
|
|
||||||
(ComparisonNumber::Decimal(self_num), ComparisonNumber::UnsignedInteger(other_num)) => {
|
|
||||||
return self_num.partial_cmp(&(other_num as f64))
|
|
||||||
}
|
|
||||||
(ComparisonNumber::Decimal(self_num), ComparisonNumber::SignedInteger(other_num)) => {
|
|
||||||
return self_num.partial_cmp(&(other_num as f64))
|
|
||||||
}
|
|
||||||
(ComparisonNumber::Decimal(self_num), ComparisonNumber::Decimal(other_num)) => {
|
|
||||||
return self_num.partial_cmp(&other_num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&serde_json::Value> for MathNumber {
|
impl From<&serde_json::Value> for MathNumber {
|
||||||
fn from(original: &serde_json::Value) -> Self {
|
fn from(original: &serde_json::Value) -> Self {
|
||||||
match original {
|
match original {
|
||||||
|
@ -123,6 +123,11 @@ pub enum OwnedLiteral {
|
|||||||
LPositiveInteger(u64),
|
LPositiveInteger(u64),
|
||||||
LNegativeInteger(i64),
|
LNegativeInteger(i64),
|
||||||
LFloat(f64),
|
LFloat(f64),
|
||||||
|
// Unlike the other OwnedLiterals, booleans cannot occur in DustJS
|
||||||
|
// templates because true/false are not reserved
|
||||||
|
// names. Regardless, they are needed here for type casting in the
|
||||||
|
// renderer.
|
||||||
|
LBoolean(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
89
src/renderer/comparison_number.rs
Normal file
89
src/renderer/comparison_number.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ComparisonNumber {
|
||||||
|
UnsignedInteger(u64),
|
||||||
|
SignedInteger(i64),
|
||||||
|
Decimal(f64),
|
||||||
|
Failure,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare json numbers
|
||||||
|
///
|
||||||
|
/// While this function can be called with two strings, it would not
|
||||||
|
/// make sense because javascript does not use numeric comparisons for
|
||||||
|
/// strings
|
||||||
|
pub fn compare_json_numbers<S, O>(self_input: S, other_input: O) -> Option<Ordering>
|
||||||
|
where
|
||||||
|
S: Into<ComparisonNumber>,
|
||||||
|
O: Into<ComparisonNumber>,
|
||||||
|
{
|
||||||
|
let self_number: ComparisonNumber = self_input.into();
|
||||||
|
let other_number: ComparisonNumber = other_input.into();
|
||||||
|
match (self_number, other_number) {
|
||||||
|
(ComparisonNumber::Failure, _) => return None,
|
||||||
|
(_, ComparisonNumber::Failure) => return None,
|
||||||
|
(
|
||||||
|
ComparisonNumber::UnsignedInteger(self_num),
|
||||||
|
ComparisonNumber::UnsignedInteger(other_num),
|
||||||
|
) => return self_num.partial_cmp(&other_num),
|
||||||
|
(
|
||||||
|
ComparisonNumber::UnsignedInteger(self_num),
|
||||||
|
ComparisonNumber::SignedInteger(other_num),
|
||||||
|
) => {
|
||||||
|
if self_num < std::i64::MAX as u64 {
|
||||||
|
return (self_num as i64).partial_cmp(&other_num);
|
||||||
|
} else {
|
||||||
|
return Some(Ordering::Greater);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(ComparisonNumber::UnsignedInteger(self_num), ComparisonNumber::Decimal(other_num)) => {
|
||||||
|
return (self_num as f64).partial_cmp(&other_num)
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
ComparisonNumber::SignedInteger(self_num),
|
||||||
|
ComparisonNumber::UnsignedInteger(other_num),
|
||||||
|
) => {
|
||||||
|
if other_num < std::i64::MAX as u64 {
|
||||||
|
return self_num.partial_cmp(&(other_num as i64));
|
||||||
|
} else {
|
||||||
|
return Some(Ordering::Less);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(ComparisonNumber::SignedInteger(self_num), ComparisonNumber::SignedInteger(other_num)) => {
|
||||||
|
return self_num.partial_cmp(&other_num)
|
||||||
|
}
|
||||||
|
(ComparisonNumber::SignedInteger(self_num), ComparisonNumber::Decimal(other_num)) => {
|
||||||
|
return (self_num as f64).partial_cmp(&other_num)
|
||||||
|
}
|
||||||
|
|
||||||
|
(ComparisonNumber::Decimal(self_num), ComparisonNumber::UnsignedInteger(other_num)) => {
|
||||||
|
return self_num.partial_cmp(&(other_num as f64))
|
||||||
|
}
|
||||||
|
(ComparisonNumber::Decimal(self_num), ComparisonNumber::SignedInteger(other_num)) => {
|
||||||
|
return self_num.partial_cmp(&(other_num as f64))
|
||||||
|
}
|
||||||
|
(ComparisonNumber::Decimal(self_num), ComparisonNumber::Decimal(other_num)) => {
|
||||||
|
return self_num.partial_cmp(&other_num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&String> for ComparisonNumber {
|
||||||
|
fn from(original: &String) -> Self {
|
||||||
|
match original.parse::<u64>() {
|
||||||
|
Ok(num) => return ComparisonNumber::UnsignedInteger(num),
|
||||||
|
Err(_) => (),
|
||||||
|
};
|
||||||
|
match original.parse::<i64>() {
|
||||||
|
Ok(num) => return ComparisonNumber::SignedInteger(num),
|
||||||
|
Err(_) => (),
|
||||||
|
};
|
||||||
|
match original.parse::<f64>() {
|
||||||
|
Ok(num) => return ComparisonNumber::Decimal(num),
|
||||||
|
Err(_) => (),
|
||||||
|
};
|
||||||
|
ComparisonNumber::Failure
|
||||||
|
}
|
||||||
|
}
|
@ -65,6 +65,18 @@ pub trait Castable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Sizable {
|
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>>;
|
fn get_size<'a>(&'a self) -> Option<IceResult<'a>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,13 @@ impl From<&OwnedLiteral> for MathNumber {
|
|||||||
fn from(original: &OwnedLiteral) -> Self {
|
fn from(original: &OwnedLiteral) -> Self {
|
||||||
match original {
|
match original {
|
||||||
OwnedLiteral::LString(_) => panic!("Strings should not be cast to numbers for math"),
|
OwnedLiteral::LString(_) => panic!("Strings should not be cast to numbers for math"),
|
||||||
|
OwnedLiteral::LBoolean(boolean) => {
|
||||||
|
if *boolean {
|
||||||
|
MathNumber::Integer(1)
|
||||||
|
} else {
|
||||||
|
MathNumber::Integer(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
OwnedLiteral::LPositiveInteger(num) => {
|
OwnedLiteral::LPositiveInteger(num) => {
|
||||||
return MathNumber::Integer((*num).try_into().unwrap())
|
return MathNumber::Integer((*num).try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! This module contains a renderer for a rust implementation of LinkedIn Dust
|
//! This module contains a renderer for a rust implementation of LinkedIn Dust
|
||||||
|
|
||||||
mod breadcrumb_tree;
|
mod breadcrumb_tree;
|
||||||
|
mod comparison_number;
|
||||||
mod context_element;
|
mod context_element;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod inline_partial_tree;
|
mod inline_partial_tree;
|
||||||
@ -11,6 +12,8 @@ mod renderer;
|
|||||||
mod select_context;
|
mod select_context;
|
||||||
mod walking;
|
mod walking;
|
||||||
|
|
||||||
|
pub use comparison_number::compare_json_numbers;
|
||||||
|
pub use comparison_number::ComparisonNumber;
|
||||||
pub use context_element::Castable;
|
pub use context_element::Castable;
|
||||||
pub use context_element::CompareContextElement;
|
pub use context_element::CompareContextElement;
|
||||||
pub use context_element::ContextElement;
|
pub use context_element::ContextElement;
|
||||||
|
@ -3,6 +3,8 @@ use crate::parser::KVPair;
|
|||||||
use crate::parser::OwnedLiteral;
|
use crate::parser::OwnedLiteral;
|
||||||
use crate::parser::RValue;
|
use crate::parser::RValue;
|
||||||
use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement;
|
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::CompareContextElement;
|
||||||
use crate::renderer::context_element::ContextElement;
|
use crate::renderer::context_element::ContextElement;
|
||||||
use crate::renderer::context_element::IceResult;
|
use crate::renderer::context_element::IceResult;
|
||||||
@ -160,6 +162,7 @@ impl ContextElement for OwnedLiteral {}
|
|||||||
impl Truthiness for OwnedLiteral {
|
impl Truthiness for OwnedLiteral {
|
||||||
fn is_truthy(&self) -> bool {
|
fn is_truthy(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
OwnedLiteral::LBoolean(boolean) => *boolean,
|
||||||
OwnedLiteral::LString(text) => !text.is_empty(),
|
OwnedLiteral::LString(text) => !text.is_empty(),
|
||||||
OwnedLiteral::LPositiveInteger(_num) => true,
|
OwnedLiteral::LPositiveInteger(_num) => true,
|
||||||
OwnedLiteral::LNegativeInteger(_num) => true,
|
OwnedLiteral::LNegativeInteger(_num) => true,
|
||||||
@ -171,6 +174,7 @@ impl Truthiness for OwnedLiteral {
|
|||||||
impl Renderable for OwnedLiteral {
|
impl Renderable for OwnedLiteral {
|
||||||
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
||||||
match self {
|
match self {
|
||||||
|
OwnedLiteral::LBoolean(boolean) => Ok(boolean.to_string()),
|
||||||
OwnedLiteral::LString(text) => Ok(text.clone()),
|
OwnedLiteral::LString(text) => Ok(text.clone()),
|
||||||
OwnedLiteral::LPositiveInteger(num) => Ok(num.to_string()),
|
OwnedLiteral::LPositiveInteger(num) => Ok(num.to_string()),
|
||||||
OwnedLiteral::LNegativeInteger(num) => Ok(num.to_string()),
|
OwnedLiteral::LNegativeInteger(num) => Ok(num.to_string()),
|
||||||
@ -192,8 +196,21 @@ impl Walkable for OwnedLiteral {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Sizable for OwnedLiteral {
|
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>> {
|
fn get_size<'a>(&'a self) -> Option<IceResult<'a>> {
|
||||||
match self {
|
match self {
|
||||||
|
OwnedLiteral::LBoolean(_) => {
|
||||||
|
Some(IceResult::from_owned(OwnedLiteral::LPositiveInteger(0)))
|
||||||
|
}
|
||||||
OwnedLiteral::LFloat(_) => Some(IceResult::from_borrowed(self)),
|
OwnedLiteral::LFloat(_) => Some(IceResult::from_borrowed(self)),
|
||||||
OwnedLiteral::LPositiveInteger(_) => Some(IceResult::from_borrowed(self)),
|
OwnedLiteral::LPositiveInteger(_) => Some(IceResult::from_borrowed(self)),
|
||||||
OwnedLiteral::LNegativeInteger(_) => Some(IceResult::from_borrowed(self)),
|
OwnedLiteral::LNegativeInteger(_) => Some(IceResult::from_borrowed(self)),
|
||||||
@ -219,9 +236,60 @@ impl Castable for OwnedLiteral {
|
|||||||
.map(|num| IceResult::from_owned(OwnedLiteral::LFloat(num)))
|
.map(|num| IceResult::from_owned(OwnedLiteral::LFloat(num)))
|
||||||
})
|
})
|
||||||
.ok(),
|
.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::LPositiveInteger(_), "number") => Some(IceResult::from_borrowed(self)),
|
||||||
(OwnedLiteral::LNegativeInteger(_), "number") => Some(IceResult::from_borrowed(self)),
|
(OwnedLiteral::LNegativeInteger(_), "number") => Some(IceResult::from_borrowed(self)),
|
||||||
(OwnedLiteral::LFloat(_), "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"),
|
(_, _) => panic!("Unimplemented cast"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,9 +316,11 @@ impl CompareContextElement for OwnedLiteral {
|
|||||||
OwnedLiteral::LNegativeInteger(self_num),
|
OwnedLiteral::LNegativeInteger(self_num),
|
||||||
OwnedLiteral::LNegativeInteger(other_num),
|
OwnedLiteral::LNegativeInteger(other_num),
|
||||||
) => self_num == other_num,
|
) => self_num == other_num,
|
||||||
(OwnedLiteral::LString(_self_text), _) | (_, OwnedLiteral::LString(_self_text)) => {
|
(OwnedLiteral::LBoolean(self_bool), OwnedLiteral::LBoolean(other_bool)) => {
|
||||||
false
|
self_bool == other_bool
|
||||||
}
|
}
|
||||||
|
(OwnedLiteral::LString(_), _) | (_, OwnedLiteral::LString(_)) => false,
|
||||||
|
(OwnedLiteral::LBoolean(_), _) | (_, OwnedLiteral::LBoolean(_)) => false,
|
||||||
(OwnedLiteral::LFloat(self_num), OwnedLiteral::LFloat(other_num)) => {
|
(OwnedLiteral::LFloat(self_num), OwnedLiteral::LFloat(other_num)) => {
|
||||||
self_num == other_num
|
self_num == other_num
|
||||||
}
|
}
|
||||||
@ -305,70 +375,12 @@ impl CompareContextElement for OwnedLiteral {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Some(other_literal) => match (self, other_literal) {
|
Some(other_literal) => match (self, other_literal) {
|
||||||
|
// If they're both strings, compare them directly
|
||||||
(OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => {
|
(OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => {
|
||||||
self_text.partial_cmp(other_text)
|
self_text.partial_cmp(other_text)
|
||||||
}
|
}
|
||||||
(
|
// Otherwise, convert to numbers and compare them that way
|
||||||
OwnedLiteral::LPositiveInteger(self_num),
|
(_, _) => return compare_json_numbers(self, other_literal),
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -471,3 +483,27 @@ impl CompareContextElement for OwnedLiteral {
|
|||||||
.map(IceResult::from_owned)
|
.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -730,7 +730,6 @@ impl<'a> DustRenderer<'a> {
|
|||||||
maybe_ice
|
maybe_ice
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|ice| ice.get_context_element_reference())
|
.map(|ice| ice.get_context_element_reference())
|
||||||
// .map(|ce| ce.get_size())
|
|
||||||
});
|
});
|
||||||
match value_ce {
|
match value_ce {
|
||||||
// If "key" is not on the @size tag at all, render 0.
|
// If "key" is not on the @size tag at all, render 0.
|
||||||
@ -742,8 +741,8 @@ impl<'a> DustRenderer<'a> {
|
|||||||
// numbers, and if that succeeds it uses the
|
// numbers, and if that succeeds it uses the
|
||||||
// number, otherwise we'll get the size of the
|
// number, otherwise we'll get the size of the
|
||||||
// original type.
|
// original type.
|
||||||
match ce.cast_to_type("number") {
|
match (ce.cast_to_type("number"), ce.is_castable()) {
|
||||||
Some(ice) => {
|
(Some(ice), true) => {
|
||||||
return ice
|
return ice
|
||||||
.get_context_element_reference()
|
.get_context_element_reference()
|
||||||
.get_size()
|
.get_size()
|
||||||
@ -752,7 +751,15 @@ impl<'a> DustRenderer<'a> {
|
|||||||
})
|
})
|
||||||
.unwrap_or(Ok("".to_owned()))
|
.unwrap_or(Ok("".to_owned()))
|
||||||
}
|
}
|
||||||
None => {
|
(Some(_), false) => {
|
||||||
|
return ce
|
||||||
|
.get_size()
|
||||||
|
.map(|ce_size| {
|
||||||
|
ce_size.get_context_element_reference().render(&Vec::new())
|
||||||
|
})
|
||||||
|
.unwrap_or(Ok("".to_owned()))
|
||||||
|
}
|
||||||
|
(None, _) => {
|
||||||
return ce
|
return ce
|
||||||
.get_size()
|
.get_size()
|
||||||
.map(|ce_size| {
|
.map(|ce_size| {
|
||||||
@ -1012,21 +1019,41 @@ impl<'a> DustRenderer<'a> {
|
|||||||
|
|
||||||
let left_side = self.tap(breadcrumbs, ¶m_map, "key");
|
let left_side = self.tap(breadcrumbs, ¶m_map, "key");
|
||||||
let right_side = self.tap(breadcrumbs, ¶m_map, "value");
|
let right_side = self.tap(breadcrumbs, ¶m_map, "value");
|
||||||
|
let type_cast = self.tap(breadcrumbs, ¶m_map, "type");
|
||||||
|
|
||||||
let left_side_ce = left_side.as_ref().map(|maybe_ice| {
|
let left_side_ce = left_side.as_ref().map(|maybe_ice| {
|
||||||
maybe_ice
|
maybe_ice
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|ice| ice.get_context_element_reference())
|
.map(|ice| ice.get_context_element_reference())
|
||||||
});
|
});
|
||||||
let right_side_ce = right_side.as_ref().map(|maybe_ice| {
|
let mut right_side_ce = right_side.as_ref().map(|maybe_ice| {
|
||||||
maybe_ice
|
maybe_ice
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|ice| ice.get_context_element_reference())
|
.map(|ice| ice.get_context_element_reference())
|
||||||
});
|
});
|
||||||
|
let type_rendered = match type_cast.as_ref().map(|maybe_ice| {
|
||||||
|
maybe_ice
|
||||||
|
.as_ref()
|
||||||
|
.map(|ice| ice.get_context_element_reference())
|
||||||
|
.map(|ce| ce.render(&Vec::new()))
|
||||||
|
}) {
|
||||||
|
Some(Ok(Ok(val))) => Some(val),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
if left_side_ce.is_none() {
|
if left_side_ce.is_none() {
|
||||||
// If key did not exist at all, return None
|
// If key did not exist at all, return None
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let right_side_cast = match (right_side_ce, type_rendered) {
|
||||||
|
(Some(Ok(ce)), Some(target_type)) => ce.cast_to_type(&target_type),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
match right_side_cast.as_ref() {
|
||||||
|
Some(ice) => {
|
||||||
|
right_side_ce = Some(Ok(ice.get_context_element_reference()));
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
match tag {
|
match tag {
|
||||||
// Special case: when comparing two RVPaths, if the path
|
// Special case: when comparing two RVPaths, if the path
|
||||||
// points to the same value then they are equal. This is
|
// points to the same value then they are equal. This is
|
||||||
|
Loading…
x
Reference in New Issue
Block a user