From 669a698575b4fcc89a3126e7c150adc1364eb9ab Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 7 Jun 2020 00:17:55 -0400 Subject: [PATCH] Re-enable the ContextElement implementation for serde_json --- src/bin.rs | 547 ++++++++++++++++++++++++++--------------------------- 1 file changed, 273 insertions(+), 274 deletions(-) diff --git a/src/bin.rs b/src/bin.rs index 84fe456..b58c8ef 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -53,13 +53,12 @@ fn main() { compiled_templates.iter().for_each(|(name, template)| { dust_renderer.load_source(template, name.to_owned()); }); - // let breadcrumbs = vec![&context as &dyn IntoContextElement]; - // println!( - // "{}", - // dust_renderer - // .render(main_template_name, &breadcrumbs) - // .expect("Failed to render") - // ); + println!( + "{}", + dust_renderer + .render(main_template_name, Some(&context)) + .expect("Failed to render") + ); } fn template_from_file<'a>( @@ -171,288 +170,288 @@ fn encode_uri_component(inp: &str) -> String { output } -// fn apply_filter( -// json_value: &serde_json::Value, -// filter: &Filter, -// ) -> Result { -// match (json_value, filter) { -// // Html escape filter -// (serde_json::Value::String(string), Filter::HtmlEncode) => { -// Ok(serde_json::Value::String(html_escape(string))) -// } -// (_, Filter::HtmlEncode) => Ok(serde_json::Value::String(html_escape( -// &json_value.render(&Vec::new())?, -// ))), -// // Disable html escape filter -// (_, Filter::DisableHtmlEncode) => panic!("The |s filter is automatically removed by the renderer since it is a no-op during rendering."), -// // Parse JSON filter -// (serde_json::Value::String(string), Filter::JsonParse) => { -// serde_json::from_str(&string).or(Err(RenderError::InvalidJson(string.to_owned()))) -// } -// (_, Filter::JsonParse) => { -// let rendered_value = json_value.render(&Vec::new())?; -// serde_json::from_str(&rendered_value).or(Err(RenderError::InvalidJson(rendered_value))) -// } -// // Json Stringify filter -// (_, Filter::JsonStringify) => { -// Ok(serde_json::Value::String(json_value.to_string())) -// } -// // Javascript escape filter -// (serde_json::Value::String(string), Filter::JavascriptStringEncode) => { -// Ok(serde_json::Value::String(javascript_escape(string))) -// } -// (serde_json::Value::Bool(boolean), Filter::JavascriptStringEncode) => { -// Ok(serde_json::Value::Bool(*boolean)) -// } -// (serde_json::Value::Number(number), Filter::JavascriptStringEncode) => { -// Ok(serde_json::Value::Number(number.clone())) -// } -// (serde_json::Value::Array(arr), Filter::JavascriptStringEncode) => { -// Ok(serde_json::Value::Array(arr.clone())) -// } -// (serde_json::Value::Object(obj), Filter::JavascriptStringEncode) => { -// Ok(serde_json::Value::Object(obj.clone())) -// } -// (_, Filter::JavascriptStringEncode) => Ok(serde_json::Value::String(javascript_escape( -// &json_value.render(&Vec::new())?, -// ))), -// // EncodeURI filter -// (serde_json::Value::String(string), Filter::EncodeUri) => { -// Ok(serde_json::Value::String(encode_uri(string))) -// } -// (_, Filter::EncodeUri) => Ok(serde_json::Value::String(encode_uri( -// &json_value.render(&Vec::new())?, -// ))), -// // EncodeURIComponent filter -// (serde_json::Value::String(string), Filter::EncodeUriComponent) => { -// Ok(serde_json::Value::String(encode_uri_component(string))) -// } -// (_, Filter::EncodeUriComponent) => Ok(serde_json::Value::String(encode_uri_component( -// &json_value.render(&Vec::new())?, -// ))), -// } -// } +fn apply_filter( + json_value: &serde_json::Value, + filter: &Filter, +) -> Result { + match (json_value, filter) { + // Html escape filter + (serde_json::Value::String(string), Filter::HtmlEncode) => { + Ok(serde_json::Value::String(html_escape(string))) + } + (_, Filter::HtmlEncode) => Ok(serde_json::Value::String(html_escape( + &json_value.render(&Vec::new())?, + ))), + // Disable html escape filter + (_, Filter::DisableHtmlEncode) => panic!("The |s filter is automatically removed by the renderer since it is a no-op during rendering."), + // Parse JSON filter + (serde_json::Value::String(string), Filter::JsonParse) => { + serde_json::from_str(&string).or(Err(RenderError::InvalidJson(string.to_owned()))) + } + (_, Filter::JsonParse) => { + let rendered_value = json_value.render(&Vec::new())?; + serde_json::from_str(&rendered_value).or(Err(RenderError::InvalidJson(rendered_value))) + } + // Json Stringify filter + (_, Filter::JsonStringify) => { + Ok(serde_json::Value::String(json_value.to_string())) + } + // Javascript escape filter + (serde_json::Value::String(string), Filter::JavascriptStringEncode) => { + Ok(serde_json::Value::String(javascript_escape(string))) + } + (serde_json::Value::Bool(boolean), Filter::JavascriptStringEncode) => { + Ok(serde_json::Value::Bool(*boolean)) + } + (serde_json::Value::Number(number), Filter::JavascriptStringEncode) => { + Ok(serde_json::Value::Number(number.clone())) + } + (serde_json::Value::Array(arr), Filter::JavascriptStringEncode) => { + Ok(serde_json::Value::Array(arr.clone())) + } + (serde_json::Value::Object(obj), Filter::JavascriptStringEncode) => { + Ok(serde_json::Value::Object(obj.clone())) + } + (_, Filter::JavascriptStringEncode) => Ok(serde_json::Value::String(javascript_escape( + &json_value.render(&Vec::new())?, + ))), + // EncodeURI filter + (serde_json::Value::String(string), Filter::EncodeUri) => { + Ok(serde_json::Value::String(encode_uri(string))) + } + (_, Filter::EncodeUri) => Ok(serde_json::Value::String(encode_uri( + &json_value.render(&Vec::new())?, + ))), + // EncodeURIComponent filter + (serde_json::Value::String(string), Filter::EncodeUriComponent) => { + Ok(serde_json::Value::String(encode_uri_component(string))) + } + (_, Filter::EncodeUriComponent) => Ok(serde_json::Value::String(encode_uri_component( + &json_value.render(&Vec::new())?, + ))), + } +} -// fn apply_filters( -// json_value: &serde_json::Value, -// filters: &[Filter], -// ) -> Result { -// let mut final_value: serde_json::Value = apply_filter(json_value, &filters[0])?; +fn apply_filters( + json_value: &serde_json::Value, + filters: &[Filter], +) -> Result { + let mut final_value: serde_json::Value = apply_filter(json_value, &filters[0])?; -// for filter in &filters[1..] { -// final_value = apply_filter(&final_value, filter)?; -// } + for filter in &filters[1..] { + final_value = apply_filter(&final_value, filter)?; + } -// Ok(final_value) -// } + Ok(final_value) +} -// impl ContextElement for serde_json::Value {} +impl ContextElement for serde_json::Value {} -// impl Truthiness for serde_json::Value { -// fn is_truthy(&self) -> bool { -// match self { -// serde_json::Value::Null => false, -// serde_json::Value::Bool(boolean) => *boolean, -// serde_json::Value::Number(_num) => true, -// serde_json::Value::String(string_value) => !string_value.is_empty(), -// serde_json::Value::Array(array_value) => !array_value.is_empty(), -// serde_json::Value::Object(_obj) => true, -// } -// } -// } +impl Truthiness for serde_json::Value { + fn is_truthy(&self) -> bool { + match self { + serde_json::Value::Null => false, + serde_json::Value::Bool(boolean) => *boolean, + serde_json::Value::Number(_num) => true, + serde_json::Value::String(string_value) => !string_value.is_empty(), + serde_json::Value::Array(array_value) => !array_value.is_empty(), + serde_json::Value::Object(_obj) => true, + } + } +} -// impl Renderable for serde_json::Value { -// fn render(&self, _filters: &Vec) -> Result { -// let after_apply = if _filters.is_empty() { -// None -// } else { -// Some(apply_filters(self, _filters)?) -// }; +impl Renderable for serde_json::Value { + fn render(&self, _filters: &Vec) -> Result { + let after_apply = if _filters.is_empty() { + None + } else { + Some(apply_filters(self, _filters)?) + }; -// match after_apply.as_ref().unwrap_or(self) { -// serde_json::Value::Null => Ok("".to_owned()), -// serde_json::Value::Bool(boolean) => Ok(boolean.to_string()), -// serde_json::Value::Number(num) => Ok(num.to_string()), -// serde_json::Value::String(string) => Ok(string.to_string()), -// serde_json::Value::Array(arr) => { -// // TODO: Handle the filters instead of passing a Vec::new() -// let rendered: Result, RenderError> = -// arr.iter().map(|val| val.render(&Vec::new())).collect(); -// let rendered_slice: &[String] = &rendered?; -// Ok(rendered_slice.join(",")) -// } -// serde_json::Value::Object(_obj) => Ok("[object Object]".to_owned()), -// } -// } -// } + match after_apply.as_ref().unwrap_or(self) { + serde_json::Value::Null => Ok("".to_owned()), + serde_json::Value::Bool(boolean) => Ok(boolean.to_string()), + serde_json::Value::Number(num) => Ok(num.to_string()), + serde_json::Value::String(string) => Ok(string.to_string()), + serde_json::Value::Array(arr) => { + // TODO: Handle the filters instead of passing a Vec::new() + let rendered: Result, RenderError> = + arr.iter().map(|val| val.render(&Vec::new())).collect(); + let rendered_slice: &[String] = &rendered?; + Ok(rendered_slice.join(",")) + } + serde_json::Value::Object(_obj) => Ok("[object Object]".to_owned()), + } + } +} -// impl Walkable for serde_json::Value { -// fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { -// match self { -// serde_json::Value::Null => Err(WalkError::CantWalk), -// serde_json::Value::Bool(_boolean) => Err(WalkError::CantWalk), -// serde_json::Value::Number(_num) => Err(WalkError::CantWalk), -// serde_json::Value::String(_string) => Err(WalkError::CantWalk), -// serde_json::Value::Array(_arr) => Err(WalkError::CantWalk), -// serde_json::Value::Object(obj) => obj -// .get(segment) -// .map(|val| val as _) -// .ok_or(WalkError::CantWalk), -// } -// } -// } +impl Walkable for serde_json::Value { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + match self { + serde_json::Value::Null => Err(WalkError::CantWalk), + serde_json::Value::Bool(_boolean) => Err(WalkError::CantWalk), + serde_json::Value::Number(_num) => Err(WalkError::CantWalk), + serde_json::Value::String(_string) => Err(WalkError::CantWalk), + serde_json::Value::Array(_arr) => Err(WalkError::CantWalk), + serde_json::Value::Object(obj) => obj + .get(segment) + .map(|val| val as _) + .ok_or(WalkError::CantWalk), + } + } +} -// impl Loopable for serde_json::Value { -// fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { -// match self { -// serde_json::Value::Array(array_value) => array_value.iter().map(|x| x as _).collect(), -// _ => Vec::new(), -// } -// } -// } +impl Loopable for serde_json::Value { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + match self { + serde_json::Value::Array(array_value) => array_value.iter().map(|x| x as _).collect(), + _ => Vec::new(), + } + } +} -// 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 => (), -// Some(other_json_value) => match (self, other_json_value) { -// // Non-scalar values not caught in the renderer by the -// // identical-path shortcut are always not equal. -// (serde_json::Value::Array(_), _) -// | (_, serde_json::Value::Array(_)) -// | (serde_json::Value::Object(_), _) -// | (_, serde_json::Value::Object(_)) => return false, -// _ => return self == other_json_value, -// }, -// } -// // Handle literals -// match other.to_any().downcast_ref::() { -// None => (), -// 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 -// } +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 => (), + Some(other_json_value) => match (self, other_json_value) { + // Non-scalar values not caught in the renderer by the + // identical-path shortcut are always not equal. + (serde_json::Value::Array(_), _) + | (_, serde_json::Value::Array(_)) + | (serde_json::Value::Object(_), _) + | (_, serde_json::Value::Object(_)) => return false, + _ => return self == other_json_value, + }, + } + // Handle literals + match other.to_any().downcast_ref::() { + None => (), + 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 + 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(self.render(&Vec::new()).unwrap_or("".to_owned())) -// .partial_compare(other); -// } + // 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(self.render(&Vec::new()).unwrap_or("".to_owned())) + .partial_compare(other); + } -// // When doing a greater than or less than comparison -// // javascript turns arrays into strings. -// if let serde_json::Value::Array(_) = self { -// return OwnedLiteral::LString(self.render(&Vec::new()).unwrap_or("".to_owned())) -// .partial_compare(other); -// } + // When doing a greater than or less than comparison + // javascript turns arrays into strings. + if let serde_json::Value::Array(_) = self { + return OwnedLiteral::LString(self.render(&Vec::new()).unwrap_or("".to_owned())) + .partial_compare(other); + } -// // Handle other serde_json::Value -// match other.to_any().downcast_ref::() { -// None => (), -// Some(other_json_value) => { -// return match (self, other_json_value) { -// ( -// serde_json::Value::Bool(self_boolean), -// serde_json::Value::Bool(other_boolean), -// ) => self_boolean.partial_cmp(other_boolean), -// ( -// serde_json::Value::Number(self_number), -// serde_json::Value::Number(other_number), -// ) => 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), -// ) => { -// return self -// .render(&Vec::new()) -// .unwrap_or("".to_owned()) -// .partial_cmp( -// &other_json_value -// .render(&Vec::new()) -// .unwrap_or("".to_owned()), -// ) -// } -// _ => None, -// }; -// } -// } -// // Handle literals -// match other.to_any().downcast_ref::() { -// None => (), -// 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 -// } -// } + // Handle other serde_json::Value + match other.to_any().downcast_ref::() { + None => (), + Some(other_json_value) => { + return match (self, other_json_value) { + ( + serde_json::Value::Bool(self_boolean), + serde_json::Value::Bool(other_boolean), + ) => self_boolean.partial_cmp(other_boolean), + ( + serde_json::Value::Number(self_number), + serde_json::Value::Number(other_number), + ) => 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), + ) => { + return self + .render(&Vec::new()) + .unwrap_or("".to_owned()) + .partial_cmp( + &other_json_value + .render(&Vec::new()) + .unwrap_or("".to_owned()), + ) + } + _ => None, + }; + } + } + // Handle literals + match other.to_any().downcast_ref::() { + None => (), + 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 + } +} -// /// Create a new vec by of references to the serde_json::Values as -// /// ContextElement trait objects so we can use its implementation of -// /// PartialOrd. -// /// -// /// You cannot implement a trait you do not define for a type you do -// /// not define, so I cannot implement PartialOrd for -// /// serde_json::value. Instead, I just re-use the PartialOrd -// /// implementation for ContextElement which unfortunately has extra -// /// overhead of downcasting. This would be a good spot for -// /// optimization. -// fn convert_vec_to_context_element(array: &Vec) -> Vec<&dyn ContextElement> { -// array.iter().map(|v| v as _).collect() -// } +/// Create a new vec by of references to the serde_json::Values as +/// ContextElement trait objects so we can use its implementation of +/// PartialOrd. +/// +/// You cannot implement a trait you do not define for a type you do +/// not define, so I cannot implement PartialOrd for +/// serde_json::value. Instead, I just re-use the PartialOrd +/// implementation for ContextElement which unfortunately has extra +/// overhead of downcasting. This would be a good spot for +/// optimization. +fn convert_vec_to_context_element(array: &Vec) -> Vec<&dyn ContextElement> { + array.iter().map(|v| v as _).collect() +} #[derive(Debug)] enum JsonNumber {