From e957caf38684cca4fd7413c755b7d03fa6a96b32 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sun, 3 May 2020 15:29:02 -0400 Subject: [PATCH] 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. --- .../render_unusual_types/input5.json | 1 + src/bin.rs | 11 ++- src/renderer/renderer.rs | 76 +++++++++++-------- 3 files changed, 52 insertions(+), 36 deletions(-) create mode 100644 js/test_cases/render_unusual_types/input5.json diff --git a/js/test_cases/render_unusual_types/input5.json b/js/test_cases/render_unusual_types/input5.json new file mode 100644 index 0000000..c1f2309 --- /dev/null +++ b/js/test_cases/render_unusual_types/input5.json @@ -0,0 +1 @@ +[true, false, null] diff --git a/src/bin.rs b/src/bin.rs index bcdf0eb..7535818 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -65,11 +65,7 @@ fn read_context_from_stdin() -> serde_json::Value { .read_to_string(&mut buffer) .expect("Failed to read stdin"); - let parsed: serde_json::Value = 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"), - } + serde_json::from_str(&buffer).expect("Failed to parse json") } impl ContextElement for serde_json::Value {} @@ -112,7 +108,10 @@ impl Walkable for serde_json::Value { segment: segment.to_string(), 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) => { obj.get(segment) .map(|val| val as _) diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 67bb142..d542a61 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -107,43 +107,40 @@ impl<'a> DustRenderer<'a> { if let Err(RenderError::WontWalk { .. }) = val { // If reference does not exist in the context, it becomes an empty string 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 { return val?.render(&reference.filters); } } DustTag::DTSection(container) => { let val = walk_path(context, &container.path.keys); - if let Err(RenderError::WontWalk { .. }) = val { - // If reference does not exist in the context, it becomes an empty string - return Ok("".to_owned()); + let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val)?; + if loop_elements.is_empty() { + // 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 { - let loop_elements: Vec<&dyn ContextElement> = val?.get_loop_elements()?; - if loop_elements.is_empty() { - // 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 { - match &container.contents { - None => return Ok("".to_owned()), - Some(body) => { - let rendered_results: Result, 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("")); - } - }; - } + match &container.contents { + None => return Ok("".to_owned()), + Some(body) => { + let rendered_results: Result, 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) => { @@ -160,6 +157,25 @@ impl<'a> DustRenderer<'a> { } 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, 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>(