Render the else block in sections if the path does not exist or if the path can't be walked.

This fixes all existing compliance tests.
This commit is contained in:
Tom Alexander 2020-05-03 15:29:02 -04:00
parent cc6dbefcdb
commit e957caf386
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
3 changed files with 52 additions and 36 deletions

View File

@ -0,0 +1 @@
[true, false, null]

View File

@ -65,11 +65,7 @@ fn read_context_from_stdin() -> serde_json::Value {
.read_to_string(&mut buffer) .read_to_string(&mut buffer)
.expect("Failed to read stdin"); .expect("Failed to read stdin");
let parsed: serde_json::Value = serde_json::from_str(&buffer).expect("Failed to parse json"); serde_json::from_str(&buffer).expect("Failed to parse json")
match parsed {
serde_json::Value::Object(obj) => serde_json::value::Value::Object(obj),
_ => panic!("Expected context to be an object"),
}
} }
impl ContextElement for serde_json::Value {} impl ContextElement for serde_json::Value {}
@ -112,7 +108,10 @@ impl Walkable for serde_json::Value {
segment: segment.to_string(), segment: segment.to_string(),
elem: self, elem: self,
}), }),
serde_json::Value::Array(_arr) => todo!("Arrays not supported yet"), serde_json::Value::Array(_arr) => Err(RenderError::CantWalk {
segment: segment.to_string(),
elem: self,
}),
serde_json::Value::Object(obj) => { serde_json::Value::Object(obj) => {
obj.get(segment) obj.get(segment)
.map(|val| val as _) .map(|val| val as _)

View File

@ -107,43 +107,40 @@ impl<'a> DustRenderer<'a> {
if let Err(RenderError::WontWalk { .. }) = val { if let Err(RenderError::WontWalk { .. }) = val {
// If reference does not exist in the context, it becomes an empty string // If reference does not exist in the context, it becomes an empty string
return Ok("".to_owned()); return Ok("".to_owned());
} else if let Err(RenderError::CantWalk { .. }) = val {
// If the context type does not support walking, it becomes an empty string
return Ok("".to_owned());
} else { } else {
return val?.render(&reference.filters); return val?.render(&reference.filters);
} }
} }
DustTag::DTSection(container) => { DustTag::DTSection(container) => {
let val = walk_path(context, &container.path.keys); let val = walk_path(context, &container.path.keys);
if let Err(RenderError::WontWalk { .. }) = val { let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val)?;
// If reference does not exist in the context, it becomes an empty string if loop_elements.is_empty() {
return Ok("".to_owned()); // Oddly enough if the value is falsey (like
// an empty array or null), Dust uses the
// original context before walking the path as
// the context for rendering the else block
//
// TODO: do filters apply? I don't think so
// but I should test
return match &container.else_contents {
Some(body) => self.render_body(&body, context),
None => Ok("".to_owned()),
};
} else { } else {
let loop_elements: Vec<&dyn ContextElement> = val?.get_loop_elements()?; match &container.contents {
if loop_elements.is_empty() { None => return Ok("".to_owned()),
// Oddly enough if the value is falsey (like Some(body) => {
// an empty array or null), Dust uses the let rendered_results: Result<Vec<String>, RenderError> = loop_elements
// original context before walking the path as .into_iter()
// the context for rendering the else block .map(|array_elem| array_elem.render_body(self, &body))
// .collect();
// TODO: do filters apply? I don't think so let rendered_slice: &[String] = &rendered_results?;
// but I should test return Ok(rendered_slice.join(""));
return match &container.else_contents { }
Some(body) => self.render_body(&body, context), };
None => Ok("".to_owned()),
};
} else {
match &container.contents {
None => return Ok("".to_owned()),
Some(body) => {
let rendered_results: Result<Vec<String>, RenderError> =
loop_elements
.into_iter()
.map(|array_elem| array_elem.render_body(self, &body))
.collect();
let rendered_slice: &[String] = &rendered_results?;
return Ok(rendered_slice.join(""));
}
};
}
} }
} }
DustTag::DTSpecial(special) => { DustTag::DTSpecial(special) => {
@ -160,6 +157,25 @@ impl<'a> DustRenderer<'a> {
} }
Ok("".to_owned()) Ok("".to_owned())
} }
/// Gets the elements to loop over for a section.
///
/// If the value is falsey, and therefore should render the else
/// block, this will return an empty vector.
fn get_loop_elements<'b>(
&'a self,
walk_result: Result<&'b dyn ContextElement, RenderError<'b>>,
) -> Result<Vec<&'b dyn ContextElement>, RenderError<'b>> {
if let Err(RenderError::WontWalk { .. }) = walk_result {
// If reference does not exist in the context, render the else block
Ok(vec![])
} else if let Err(RenderError::CantWalk { .. }) = walk_result {
// If the context type does not support walking, render the else block
Ok(vec![])
} else {
Ok(walk_result?.get_loop_elements()?)
}
}
} }
fn walk_path<'a>( fn walk_path<'a>(