diff --git a/js/test_cases/helpers_gt/main.dust b/js/test_cases/helpers_gt/main.dust index 0bd9fca..2bc9370 100644 --- a/js/test_cases/helpers_gt/main.dust +++ b/js/test_cases/helpers_gt/main.dust @@ -21,9 +21,9 @@ beta is {beta}{~n} {@gt key="a" value="A"}"a" is greater than "A"{:else}"a" is less than or equal to "A"{/gt}{~n} {@gt key="a" value="}"}"a" is greater than "}"{:else}"a" is less than or equal to "}"{/gt}{~n} {! -Commented out because unicode breaks nom -{@gt key="☃" value="☄"}"☃" is greater than "☄"{:else}"☃" is less than or equal to "☄"{/gt}{~n} -!} + Commented out because unicode breaks nom + {@gt key="☃" value="☄"}"☃" is greater than "☄"{:else}"☃" is less than or equal to "☄"{/gt}{~n} + !} {@gt key=true_value value=false_value}true is greater than false{:else}true is less than or equal to false{/gt}{~n} {@gt key=array_lower value=array_higher}[3,5,7] is greater than [8,9]{:else}[3,5,7] is less than or equal to [8,9]{/gt}{~n} {@gt key=array_higher value=array_lower}[8,9] is greater than [3,5,7]{:else}[8,9] is less than or equal to [3,5,7]{/gt}{~n} @@ -40,3 +40,5 @@ Commented out because unicode breaks nom {@gt key=array_higher value=array_higher_longer}array_higher is greater than array_higher_longer{:else}array_higher is less than or equal to array_higher_longer{/gt}{~n} {@gt key=array_lower value=array_lower_with_array}array_lower is greater than array_lower_with_array{:else}array_lower is less than or equal to array_lower_with_array{/gt}{~n} {@gt key=array_lower_with_array value=array_lower}array_lower_with_array is greater than array_lower{:else}array_lower_with_array is less than or equal to array_lower{/gt}{~n} +{@gt key=some_obj value=int}some_obj is greater than int{:else}some_obj is less than or equal to int{/gt}{~n} +{@gt key=some_obj value="[object Object]"}some_obj is greater than "[object Object]"{:else}some_obj is less than or equal to "[object Object]"{/gt}{~n} diff --git a/js/test_cases/helpers_gte/main.dust b/js/test_cases/helpers_gte/main.dust index b0ee031..750f75b 100644 --- a/js/test_cases/helpers_gte/main.dust +++ b/js/test_cases/helpers_gte/main.dust @@ -21,9 +21,9 @@ beta is {beta}{~n} {@gte key="a" value="A"}"a" is greater than or equal to "A"{:else}"a" is less than "A"{/gte}{~n} {@gte key="a" value="}"}"a" is greater than or equal to "}"{:else}"a" is less than "}"{/gte}{~n} {! -Commented out because unicode breaks nom -{@gte key="☃" value="☄"}"☃" is greater than or equal to "☄"{:else}"☃" is less than "☄"{/gte}{~n} -!} + Commented out because unicode breaks nom + {@gte key="☃" value="☄"}"☃" is greater than or equal to "☄"{:else}"☃" is less than "☄"{/gte}{~n} + !} {@gte key=true_value value=false_value}true is greater than or equal to false{:else}true is less than false{/gte}{~n} {@gte key=array_lower value=array_higher}[3,5,7] is greater than or equal to [8,9]{:else}[3,5,7] is less than [8,9]{/gte}{~n} {@gte key=array_higher value=array_lower}[8,9] is greater than or equal to [3,5,7]{:else}[8,9] is less than [3,5,7]{/gte}{~n} @@ -39,3 +39,5 @@ Commented out because unicode breaks nom {@gte key=array_higher value=array_higher_longer}array_higher is greater than or equal to array_higher_longer{:else}array_higher is less than array_higher_longer{/gte}{~n} {@gte key=array_lower value=array_lower_with_array}array_lower is greater than or equal to array_lower_with_array{:else}array_lower is less than array_lower_with_array{/gte}{~n} {@gte key=array_lower_with_array value=array_lower}array_lower_with_array is greater than or equal to array_lower{:else}array_lower_with_array is less than array_lower{/gte}{~n} +{@gte key=some_obj value=int}some_obj is greater than or equal to int{:else}some_obj is less than int{/gte}{~n} +{@gte key=some_obj value="[object Object]"}some_obj is greater than or equal to "[object Object]"{:else}some_obj is less than "[object Object]"{/gte}{~n} diff --git a/js/test_cases/helpers_lt/main.dust b/js/test_cases/helpers_lt/main.dust index f2b81d1..cbebe3f 100644 --- a/js/test_cases/helpers_lt/main.dust +++ b/js/test_cases/helpers_lt/main.dust @@ -21,9 +21,9 @@ beta is {beta}{~n} {@lt key="a" value="A"}"a" is less than "A"{:else}"a" is greater than or equal to "A"{/lt}{~n} {@lt key="a" value="}"}"a" is less than "}"{:else}"a" is greater than or equal to "}"{/lt}{~n} {! -Commented out because unicode breaks nom -{@lt key="☃" value="☄"}"☃" is less than "☄"{:else}"☃" is greater than or equal to "☄"{/lt}{~n} -!} + Commented out because unicode breaks nom + {@lt key="☃" value="☄"}"☃" is less than "☄"{:else}"☃" is greater than or equal to "☄"{/lt}{~n} + !} {@lt key=true_value value=false_value}true is less than false{:else}true is greater than or equal to false{/lt}{~n} {@lt key=array_lower value=array_higher}[3,5,7] is less than [8,9]{:else}[3,5,7] is greater than or equal to [8,9]{/lt}{~n} {@lt key=array_higher value=array_lower}[8,9] is less than [3,5,7]{:else}[8,9] is greater than or equal to [3,5,7]{/lt}{~n} @@ -39,3 +39,5 @@ Commented out because unicode breaks nom {@lt key=array_higher value=array_higher_longer}array_higher is less than array_higher_longer{:else}array_higher is greater than or equal to array_higher_longer{/lt}{~n} {@lt key=array_lower value=array_lower_with_array}array_lower is less than array_lower_with_array{:else}array_lower is greater than or equal to array_lower_with_array{/lt}{~n} {@lt key=array_lower_with_array value=array_lower}array_lower_with_array is less than array_lower{:else}array_lower_with_array is greater than or equal to array_lower{/lt}{~n} +{@lt key=some_obj value=int}some_obj is less than int{:else}some_obj is greater than or equal to int{/lt}{~n} +{@lt key=some_obj value="[object Object]"}some_obj is less than "[object Object]"{:else}some_obj is greater than or equal to "[object Object]"{/lt}{~n} diff --git a/js/test_cases/helpers_lte/main.dust b/js/test_cases/helpers_lte/main.dust index 72ee337..98939e4 100644 --- a/js/test_cases/helpers_lte/main.dust +++ b/js/test_cases/helpers_lte/main.dust @@ -21,9 +21,9 @@ beta is {beta}{~n} {@lte key="a" value="A"}"a" is less than or equal to "A"{:else}"a" is greater than "A"{/lte}{~n} {@lte key="a" value="}"}"a" is less than or equal to "}"{:else}"a" is greater than "}"{/lte}{~n} {! -Commented out because unicode breaks nom -{@lte key="☃" value="☄"}"☃" is less than or equal to "☄"{:else}"☃" is greater than "☄"{/lte}{~n} -!} + Commented out because unicode breaks nom + {@lte key="☃" value="☄"}"☃" is less than or equal to "☄"{:else}"☃" is greater than "☄"{/lte}{~n} + !} {@lte key=true_value value=false_value}true is less than or equal to false{:else}true is greater than false{/lte}{~n} {@lte key=array_lower value=array_higher}[3,5,7] is less than or equal to [8,9]{:else}[3,5,7] is greater than [8,9]{/lte}{~n} {@lte key=array_higher value=array_lower}[8,9] is less than or equal to [3,5,7]{:else}[8,9] is greater than [3,5,7]{/lte}{~n} @@ -39,3 +39,5 @@ Commented out because unicode breaks nom {@lte key=array_higher value=array_higher_longer}array_higher is less than or equal to array_higher_longer{:else}array_higher is greater than array_higher_longer{/lte}{~n} {@lte key=array_lower value=array_lower_with_array}array_lower is less than or equal to array_lower_with_array{:else}array_lower is greater than array_lower_with_array{/lte}{~n} {@lte key=array_lower_with_array value=array_lower}array_lower_with_array is less than or equal to array_lower{:else}array_lower_with_array is greater than array_lower{/lte}{~n} +{@lte key=some_obj value=int}some_obj is less than or equal to int{:else}some_obj is greater than int{/lte}{~n} +{@lte key=some_obj value="[object Object]"}some_obj is less than or equal to "[object Object]"{:else}some_obj is greater than "[object Object]"{/lte}{~n} diff --git a/src/bin.rs b/src/bin.rs index 4274501..7e0f6ff 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -2,6 +2,7 @@ extern crate nom; use crate::renderer::CompareContextElement; use parser::Filter; +use parser::OwnedLiteral; use renderer::compile_template; use renderer::CompiledTemplate; use renderer::ContextElement; @@ -142,6 +143,7 @@ impl Loopable for serde_json::Value { impl CompareContextElement for serde_json::Value { fn equals(&self, other: &dyn ContextElement) -> bool { + // println!("equals json {:?} | {:?}", self, other); // Handle other serde_json::Value match other.to_any().downcast_ref::() { None => (), @@ -155,20 +157,29 @@ impl CompareContextElement for serde_json::Value { _ => return self == other_json_value, }, } - // Handle string literals - match other.to_any().downcast_ref::() { + // Handle literals + match other.to_any().downcast_ref::() { None => (), - Some(other_string) => return self.as_str().map_or(false, |s| s == other_string), - } - // Handle numeric literals - match other.to_any().downcast_ref::() { - None => (), - Some(other_num) => return self.as_u64().map_or(false, |n| n == *other_num), + Some(OwnedLiteral::LString(other_string)) => { + return self.as_str().map_or(false, |s| s == other_string) + } + Some(OwnedLiteral::LPositiveInteger(other_num)) => { + return self.as_u64().map_or(false, |n| n == *other_num) + } } false } fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // println!("partial_compare json {:?} | {:?}", self, other); + // Handle type coerced objects + + // When doing a greater than or less than comparison, + // javascript coerces objects into "[object Object]". + if let serde_json::Value::Object(_) = self { + return OwnedLiteral::LString("[object Object]".to_owned()).partial_compare(other); + } + // Handle other serde_json::Value match other.to_any().downcast_ref::() { None => (), @@ -181,63 +192,71 @@ impl CompareContextElement for serde_json::Value { ( serde_json::Value::Number(self_number), serde_json::Value::Number(other_number), - ) => match ( - self_number.as_f64(), - other_number.as_f64(), - self_number.as_u64(), - other_number.as_u64(), - self_number.as_i64(), - other_number.as_i64(), - ) { - (_, _, _, _, Some(self_int), Some(other_int)) => { - self_int.partial_cmp(&other_int) - } - (_, _, Some(self_uint), Some(other_uint), _, _) => { - self_uint.partial_cmp(&other_uint) - } - (_, _, Some(_self_uint), _, _, Some(_other_int)) => { - // If the previous matches did not catch - // it, then other must be negative and - // self must be larger than can be - // represented with an i64, therefore self - // is greater than other. - Some(Ordering::Greater) - } - (_, _, _, Some(_other_uint), Some(_self_int), _) => { - // If the previous matches did not catch - // it, then self must be negative and - // other must be larger than can be - // represented with an i64, therefore - // other is greater than self. - Some(Ordering::Less) - } - (Some(self_float), Some(other_float), _, _, _, _) => { - self_float.partial_cmp(&other_float) - } - _ => panic!("This should be impossible since u64 and i64 can both be converted to floats"), - }, + ) => return compare_json_numbers(self_number, other_number), + ( + serde_json::Value::String(self_string), + serde_json::Value::Number(other_number), + ) => return compare_json_numbers(self_string, other_number), + ( + serde_json::Value::Number(self_number), + serde_json::Value::String(other_string), + ) => return compare_json_numbers(self_number, other_string), ( serde_json::Value::String(self_string), serde_json::Value::String(other_string), ) => self_string.partial_cmp(other_string), - (serde_json::Value::Array(self_array), serde_json::Value::Array(other_array)) => convert_vec_to_context_element(self_array).partial_cmp(&convert_vec_to_context_element(other_array)), + ( + serde_json::Value::Array(self_array), + serde_json::Value::Array(other_array), + ) => { + return self + .render(&Vec::new()) + .unwrap_or("".to_owned()) + .partial_cmp( + &other_json_value + .render(&Vec::new()) + .unwrap_or("".to_owned()), + ) + } _ => None, }; } } - // Handle string literals - match other.to_any().downcast_ref::() { + // Handle literals + match other.to_any().downcast_ref::() { None => (), - Some(other_string) => { - return self - .as_str() - .map_or(None, |s| s.partial_cmp(other_string.as_str())) - } - } - // Handle numeric literals - match other.to_any().downcast_ref::() { - None => (), - Some(other_num) => return self.as_u64().map_or(None, |n| n.partial_cmp(other_num)), + Some(other_literal) => match (self, other_literal) { + (serde_json::Value::String(self_string), OwnedLiteral::LString(other_string)) => { + return self_string.partial_cmp(other_string) + } + ( + serde_json::Value::String(self_string), + OwnedLiteral::LPositiveInteger(other_num), + ) => return compare_json_numbers(self_string, other_literal), + (serde_json::Value::Number(self_num), OwnedLiteral::LString(other_string)) => { + return compare_json_numbers(self_num, other_string) + } + ( + serde_json::Value::Number(self_num), + OwnedLiteral::LPositiveInteger(other_num), + ) => return compare_json_numbers(self_num, other_literal), + (serde_json::Value::Array(_), _) => { + // TODO + todo!() + } + (serde_json::Value::Object(_), _) => { + // TODO + todo!() + } + (serde_json::Value::Bool(_), _) => { + // TODO + todo!() + } + (serde_json::Value::Null, _) => { + // TODO + todo!() + } + }, } None } @@ -256,3 +275,116 @@ impl CompareContextElement for serde_json::Value { fn convert_vec_to_context_element(array: &Vec) -> Vec<&dyn ContextElement> { array.iter().map(|v| v as _).collect() } + +#[derive(Debug)] +enum JsonNumber { + UnsignedInteger(u64), + SignedInteger(i64), + Decimal(f64), + Failure, +} + +impl From<&String> for JsonNumber { + fn from(original: &String) -> Self { + match original.parse::() { + Ok(num) => return JsonNumber::UnsignedInteger(num), + Err(_) => (), + }; + match original.parse::() { + Ok(num) => return JsonNumber::SignedInteger(num), + Err(_) => (), + }; + match original.parse::() { + Ok(num) => return JsonNumber::Decimal(num), + Err(_) => (), + }; + JsonNumber::Failure + } +} + +impl From<&serde_json::Number> for JsonNumber { + fn from(original: &serde_json::Number) -> Self { + match original.as_u64() { + Some(num) => return JsonNumber::UnsignedInteger(num), + None => (), + }; + match original.as_i64() { + Some(num) => return JsonNumber::SignedInteger(num), + None => (), + }; + match original.as_f64() { + Some(num) => return JsonNumber::Decimal(num), + None => (), + }; + JsonNumber::Failure + } +} + +impl From<&OwnedLiteral> for JsonNumber { + fn from(original: &OwnedLiteral) -> Self { + match original { + OwnedLiteral::LPositiveInteger(num) => JsonNumber::UnsignedInteger(*num), + OwnedLiteral::LString(text) => text.into(), + } + } +} + +/// 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(self_input: S, other_input: O) -> Option +where + S: Into, + O: Into, +{ + let self_number: JsonNumber = self_input.into(); + let other_number: JsonNumber = other_input.into(); + // println!( + // "compare_number_and_string {:?} | {:?}", + // self_number, other_number + // ); + // TODO: Figure out how javascript compares floats and ints + match (self_number, other_number) { + (JsonNumber::Failure, _) => return None, + (_, JsonNumber::Failure) => return None, + (JsonNumber::UnsignedInteger(self_num), JsonNumber::UnsignedInteger(other_num)) => { + return self_num.partial_cmp(&other_num) + } + (JsonNumber::UnsignedInteger(self_num), JsonNumber::SignedInteger(other_num)) => { + return Some(Ordering::Greater) + } + (JsonNumber::UnsignedInteger(self_num), JsonNumber::Decimal(other_num)) => return None, + + (JsonNumber::SignedInteger(self_num), JsonNumber::UnsignedInteger(other_num)) => { + return Some(Ordering::Less) + } + (JsonNumber::SignedInteger(self_num), JsonNumber::SignedInteger(other_num)) => { + return self_num.partial_cmp(&other_num) + } + (JsonNumber::SignedInteger(self_num), JsonNumber::Decimal(other_num)) => return None, + + (JsonNumber::Decimal(self_num), JsonNumber::UnsignedInteger(other_num)) => return None, + (JsonNumber::Decimal(self_num), JsonNumber::SignedInteger(other_num)) => return None, + (JsonNumber::Decimal(self_num), JsonNumber::Decimal(other_num)) => { + return self_num.partial_cmp(&other_num) + } + } + None +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_nested_array_render() { + let x: serde_json::Value = + serde_json::from_str(r#"[3,5,[7,9]]"#).expect("Failed to parse json"); + assert_eq!( + x.render(&Vec::new()), + Ok::<_, RenderError>("3,5,7,9".to_owned()) + ); + } +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 202309e..58e38e3 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7,6 +7,7 @@ pub use parser::Body; pub use parser::DustTag; pub use parser::Filter; pub use parser::KVPair; +pub use parser::OwnedLiteral; pub use parser::RValue; pub use parser::Special; pub use parser::Template; diff --git a/src/parser/parser.rs b/src/parser/parser.rs index a742fa3..a1ed027 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -118,11 +118,16 @@ pub struct Partial<'a> { pub params: Vec>, } +#[derive(Clone, Debug, PartialEq)] +pub enum OwnedLiteral { + LString(String), + LPositiveInteger(u64), +} + #[derive(Clone, Debug, PartialEq)] pub enum RValue<'a> { RVPath(Path<'a>), - RVString(String), - RVPositiveInteger(u64), + RVLiteral(OwnedLiteral), } #[derive(Clone, Debug, PartialEq)] @@ -226,8 +231,12 @@ fn postitive_integer_literal(i: &str) -> IResult<&str, u64> { fn rvalue(i: &str) -> IResult<&str, RValue> { alt(( map(path, RValue::RVPath), - map(quoted_string, RValue::RVString), - map(postitive_integer_literal, RValue::RVPositiveInteger), + map(quoted_string, |s| { + RValue::RVLiteral(OwnedLiteral::LString(s)) + }), + map(postitive_integer_literal, |num| { + RValue::RVLiteral(OwnedLiteral::LPositiveInteger(num)) + }), ))(i) } @@ -911,7 +920,7 @@ mod tests { }, KVPair { key: "animal", - value: RValue::RVString("cat".to_owned()) + value: RValue::RVLiteral(OwnedLiteral::LString("cat".to_owned())) } ] }) @@ -934,7 +943,7 @@ mod tests { }, KVPair { key: "animal", - value: RValue::RVString("cat".to_owned()) + value: RValue::RVLiteral(OwnedLiteral::LString("cat".to_owned())) } ] }) @@ -953,11 +962,11 @@ mod tests { params: vec![ KVPair { key: "a", - value: RValue::RVString("foo".to_owned()) + value: RValue::RVLiteral(OwnedLiteral::LString("foo".to_owned())) }, KVPair { key: "b", - value: RValue::RVPositiveInteger(179) + value: RValue::RVLiteral(OwnedLiteral::LPositiveInteger(179)) } ] }) @@ -980,7 +989,7 @@ mod tests { }, KVPair { key: "value", - value: RValue::RVString("cat".to_owned()) + value: RValue::RVLiteral(OwnedLiteral::LString("cat".to_owned())) } ], contents: Some(Body { @@ -1016,7 +1025,7 @@ mod tests { }, KVPair { key: "value", - value: RValue::RVString("cat".to_owned()) + value: RValue::RVLiteral(OwnedLiteral::LString("cat".to_owned())) } ], contents: None, @@ -1122,23 +1131,33 @@ mod tests { params: vec![ KVPair { key: "v1", - value: RValue::RVString("b".to_owned()) + value: RValue::RVLiteral(OwnedLiteral::LString( + "b".to_owned() + )) }, KVPair { key: "v2", - value: RValue::RVString("b".to_owned()) + value: RValue::RVLiteral(OwnedLiteral::LString( + "b".to_owned() + )) }, KVPair { key: "v3", - value: RValue::RVString("b".to_owned()) + value: RValue::RVLiteral(OwnedLiteral::LString( + "b".to_owned() + )) }, KVPair { key: "v4", - value: RValue::RVString("b".to_owned()) + value: RValue::RVLiteral(OwnedLiteral::LString( + "b".to_owned() + )) }, KVPair { key: "v5", - value: RValue::RVString("b".to_owned()) + value: RValue::RVLiteral(OwnedLiteral::LString( + "b".to_owned() + )) } ] } diff --git a/src/renderer/errors.rs b/src/renderer/errors.rs index 7dac064..a72ef61 100644 --- a/src/renderer/errors.rs +++ b/src/renderer/errors.rs @@ -4,6 +4,7 @@ use std::fmt; /// Fatal errors while rendering. /// /// A RenderError will halt rendering. +#[derive(PartialEq)] pub enum RenderError { Generic(String), TemplateNotFound(String), diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 5812ce3..bc89624 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -1,5 +1,5 @@ use crate::parser::KVPair; -use crate::parser::{Filter, RValue}; +use crate::parser::{Filter, OwnedLiteral, RValue}; use crate::renderer::context_element::CompareContextElement; use crate::renderer::context_element::ContextElement; use crate::renderer::walking::owned_walk_path; @@ -21,11 +21,10 @@ use std::{cmp::Ordering, collections::HashMap}; /// are imposing the cost of copying the data in the renderer because /// the parser has no reason to not be able to reference data from the /// input string. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub enum OwnedRValue { RVPath(OwnedPath), - RVString(String), - RVPositiveInteger(u64), + RVLiteral(OwnedLiteral), } #[derive(Clone, Debug, PartialEq)] @@ -36,11 +35,10 @@ pub struct OwnedPath { impl From<&RValue<'_>> for OwnedRValue { fn from(original: &RValue<'_>) -> Self { match original { - RValue::RVString(text) => OwnedRValue::RVString(text.to_owned()), + RValue::RVLiteral(literal) => OwnedRValue::RVLiteral(literal.clone()), RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath { keys: path.keys.iter().map(|k| k.to_string()).collect(), }), - RValue::RVPositiveInteger(num) => OwnedRValue::RVPositiveInteger(*num), } } } @@ -92,8 +90,7 @@ impl Walkable for ParametersContext { let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; match rval { OwnedRValue::RVPath(path) => owned_walk_path(&self.breadcrumbs, &path.keys), - OwnedRValue::RVString(text) => Ok(text), - OwnedRValue::RVPositiveInteger(num) => Ok(num), + OwnedRValue::RVLiteral(literal) => Ok(literal), } } } @@ -129,45 +126,71 @@ impl CompareContextElement for ParametersContext { } } -impl ContextElement for String {} +impl ContextElement for OwnedLiteral {} -impl Renderable for String { +impl Renderable for OwnedLiteral { fn render(&self, _filters: &Vec) -> Result { - Ok(self.clone()) - } -} - -impl Loopable for String { - fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { - if self.is_empty() { - Vec::new() - } else { - vec![self] + match self { + OwnedLiteral::LString(text) => Ok(text.clone()), + OwnedLiteral::LPositiveInteger(num) => Ok(num.to_string()), } } } -impl Walkable for String { +impl Loopable for OwnedLiteral { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + match self { + OwnedLiteral::LString(text) => { + if text.is_empty() { + Vec::new() + } else { + vec![self] + } + } + OwnedLiteral::LPositiveInteger(num) => vec![self], + } + } +} + +impl Walkable for OwnedLiteral { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { Err(WalkError::CantWalk) } } -impl CompareContextElement for String { +impl CompareContextElement for OwnedLiteral { fn equals(&self, other: &dyn ContextElement) -> bool { - // If its a String then compare them directly, otherwise defer - // to the other type's implementation of CompareContextElement - // since the end user could add any type. + // println!("equals literal {:?} | {:?}", 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_string) => self == other_string, + Some(other_literal) => match (self, other_literal) { + (OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => { + self_text == other_text + } + (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => { + &self_num.to_string() == other_text + } + (OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => { + self_text == &other_num.to_string() + } + ( + OwnedLiteral::LPositiveInteger(self_num), + OwnedLiteral::LPositiveInteger(other_num), + ) => self_num == other_num, + }, } } fn partial_compare(&self, other: &dyn ContextElement) -> Option { - // If its a string then compare them directly, otherwise defer - // to the other type's implementation of CompareContextElement - // since the end user could add any type. + // println!("partial_compare literal {:?} | {:?}", 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 => match other.partial_compare(self) { None => None, @@ -177,56 +200,21 @@ impl CompareContextElement for String { Ordering::Less => Some(Ordering::Greater), }, }, - Some(other_string) => self.partial_cmp(other_string), - } - } -} - -impl ContextElement for u64 {} - -impl Renderable for u64 { - fn render(&self, _filters: &Vec) -> Result { - Ok(self.to_string()) - } -} - -impl Loopable for u64 { - fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { - vec![self] - } -} - -impl Walkable for u64 { - fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { - Err(WalkError::CantWalk) - } -} - -impl CompareContextElement for u64 { - fn equals(&self, other: &dyn ContextElement) -> bool { - // If its a u64 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_num) => self == other_num, - } - } - - fn partial_compare(&self, other: &dyn ContextElement) -> Option { - // If its a u64 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::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::LPositiveInteger(self_num), + OwnedLiteral::LPositiveInteger(other_num), + ) => self_num.partial_cmp(other_num), }, - Some(other_num) => self.partial_cmp(other_num), } } } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index f44022c..bf0b083 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -343,19 +343,19 @@ impl<'a> DustRenderer<'a> { ) } (Ok(left_side_unwrapped), Ok(right_side_unwrapped)) => { - return match left_side_unwrapped.partial_cmp(&right_side_unwrapped) { - Some(Ordering::Greater) | Some(Ordering::Equal) | None => self - .render_maybe_body( - ¶meterized_block.contents, - breadcrumbs, - blocks, - ), - Some(Ordering::Less) => self.render_maybe_body( + if left_side_unwrapped >= right_side_unwrapped { + return self.render_maybe_body( + ¶meterized_block.contents, + breadcrumbs, + blocks, + ); + } else { + return self.render_maybe_body( ¶meterized_block.else_contents, breadcrumbs, blocks, - ), - }; + ); + } } } } @@ -415,19 +415,19 @@ impl<'a> DustRenderer<'a> { ) } (Ok(left_side_unwrapped), Ok(right_side_unwrapped)) => { - return match left_side_unwrapped.partial_cmp(&right_side_unwrapped) { - Some(Ordering::Less) | Some(Ordering::Equal) | None => self - .render_maybe_body( - ¶meterized_block.contents, - breadcrumbs, - blocks, - ), - Some(Ordering::Greater) => self.render_maybe_body( + if left_side_unwrapped <= right_side_unwrapped { + return self.render_maybe_body( + ¶meterized_block.contents, + breadcrumbs, + blocks, + ); + } else { + return self.render_maybe_body( ¶meterized_block.else_contents, breadcrumbs, blocks, - ), - }; + ); + } } } } @@ -477,9 +477,8 @@ impl<'a> DustRenderer<'a> { match param_map.get(key) { None => None, Some(rval) => match rval { - RValue::RVString(text) => Some(Ok(text)), + RValue::RVLiteral(literal) => Some(Ok(literal)), RValue::RVPath(path) => Some(walk_path(breadcrumbs, &path.keys)), - RValue::RVPositiveInteger(num) => Some(Ok(num)), }, } } @@ -494,6 +493,81 @@ mod tests { use crate::renderer::context_element::Walkable; use crate::renderer::CompareContextElement; + impl ContextElement for String {} + + impl Renderable for String { + fn render(&self, _filters: &Vec) -> Result { + Ok(self.clone()) + } + } + + impl Loopable for String { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + if self.is_empty() { + Vec::new() + } else { + vec![self] + } + } + } + + impl Walkable for String { + fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { + Err(WalkError::CantWalk) + } + } + + impl CompareContextElement for String { + fn equals(&self, other: &dyn ContextElement) -> bool { + match other.to_any().downcast_ref::() { + None => false, + Some(other_string) => self == other_string, + } + } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + match other.to_any().downcast_ref::() { + None => None, + Some(other_string) => self.partial_cmp(other_string), + } + } + } + impl ContextElement for u64 {} + + impl Renderable for u64 { + fn render(&self, _filters: &Vec) -> Result { + Ok(self.to_string()) + } + } + + impl Loopable for u64 { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + vec![self] + } + } + + impl Walkable for u64 { + fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { + Err(WalkError::CantWalk) + } + } + + impl CompareContextElement for u64 { + fn equals(&self, other: &dyn ContextElement) -> bool { + match other.to_any().downcast_ref::() { + None => false, + Some(other_num) => self == other_num, + } + } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + match other.to_any().downcast_ref::() { + None => None, + Some(other_num) => self.partial_cmp(other_num), + } + } + } + impl ContextElement for HashMap {} impl Renderable for HashMap { @@ -520,6 +594,11 @@ mod tests { fn equals(&self, other: &dyn ContextElement) -> bool { false } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // TODO: Implement + None + } } #[test]