Re-enable the ContextElement implementation for serde_json

master
Tom Alexander 4 years ago
parent 422479bcf2
commit 669a698575
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE

@ -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<serde_json::Value, RenderError> {
// 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<serde_json::Value, RenderError> {
// 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)?;
// }
// Ok(final_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 Renderable for serde_json::Value {
// fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
// 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<Vec<String>, 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 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::<Self>() {
// 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::<OwnedLiteral>() {
// 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<Ordering> {
// // 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 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::<Self>() {
// 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::<OwnedLiteral>() {
// 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<serde_json::Value>) -> Vec<&dyn ContextElement> {
// array.iter().map(|v| v as _).collect()
// }
fn apply_filter(
json_value: &serde_json::Value,
filter: &Filter,
) -> Result<serde_json::Value, RenderError> {
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<serde_json::Value, RenderError> {
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)?;
}
Ok(final_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 Renderable for serde_json::Value {
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
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<Vec<String>, 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 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::<Self>() {
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::<OwnedLiteral>() {
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<Ordering> {
// 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 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::<Self>() {
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::<OwnedLiteral>() {
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<serde_json::Value>) -> Vec<&dyn ContextElement> {
array.iter().map(|v| v as _).collect()
}
#[derive(Debug)]
enum JsonNumber {

Loading…
Cancel
Save