diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index bc89624..45d5151 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -2,7 +2,7 @@ use crate::parser::KVPair; 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; +use crate::renderer::walking::walk_path_combined; use crate::renderer::Loopable; use crate::renderer::RenderError; use crate::renderer::Renderable; @@ -89,7 +89,7 @@ impl Walkable for ParametersContext { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; match rval { - OwnedRValue::RVPath(path) => owned_walk_path(&self.breadcrumbs, &path.keys), + OwnedRValue::RVPath(path) => walk_path_combined(&self.breadcrumbs, &path.keys), OwnedRValue::RVLiteral(literal) => Ok(literal), } } diff --git a/src/renderer/renderer.rs b/src/renderer/renderer.rs index 145b3d4..d4b77e2 100644 --- a/src/renderer/renderer.rs +++ b/src/renderer/renderer.rs @@ -15,6 +15,7 @@ use crate::renderer::inline_partial_tree::extract_inline_partials; use crate::renderer::inline_partial_tree::InlinePartialTreeElement; use crate::renderer::parameters_context::ParametersContext; use crate::renderer::walking::walk_path; +use crate::renderer::walking::walk_path_combined; use std::collections::HashMap; #[derive(Clone, Debug)] @@ -146,7 +147,7 @@ impl<'a> DustRenderer<'a> { } DustTag::DTLiteralStringBlock(literal) => return Ok((*literal).to_owned()), DustTag::DTReference(reference) => { - let val = walk_path(breadcrumbs, &reference.path.keys); + let val = walk_path_combined(breadcrumbs, &reference.path.keys); match val { Err(WalkError::CantWalk) => return Ok("".to_owned()), Ok(final_val) => { @@ -160,7 +161,7 @@ impl<'a> DustRenderer<'a> { } } DustTag::DTSection(container) => { - let val = walk_path(breadcrumbs, &container.path.keys); + let val = walk_path_combined(breadcrumbs, &container.path.keys); let loop_elements: Vec<&dyn ContextElement> = Self::get_loop_elements(val); if loop_elements.is_empty() { // Oddly enough if the value is falsey (like @@ -187,7 +188,7 @@ impl<'a> DustRenderer<'a> { } } DustTag::DTExists(container) => { - let val = walk_path(breadcrumbs, &container.path.keys); + let val = walk_path_combined(breadcrumbs, &container.path.keys); let loop_elements: Vec<&dyn ContextElement> = Self::get_loop_elements(val); return if loop_elements.is_empty() { self.render_maybe_body(&container.else_contents, breadcrumbs, blocks) @@ -196,7 +197,7 @@ impl<'a> DustRenderer<'a> { }; } DustTag::DTNotExists(container) => { - let val = walk_path(breadcrumbs, &container.path.keys); + let val = walk_path_combined(breadcrumbs, &container.path.keys); let loop_elements: Vec<&dyn ContextElement> = Self::get_loop_elements(val); return if !loop_elements.is_empty() { self.render_maybe_body(&container.else_contents, breadcrumbs, blocks) @@ -682,14 +683,14 @@ mod tests { .collect(); assert_eq!( - walk_path(&vec![&context as &dyn ContextElement], &vec!["cat"]) + walk_path_combined(&vec![&context as &dyn ContextElement], &vec!["cat"]) .unwrap() .render(&Vec::new()) .unwrap(), "kitty".to_owned() ); assert_eq!( - walk_path( + walk_path_combined( &vec![&number_context as &dyn ContextElement], &vec!["tiger"] ) @@ -699,7 +700,7 @@ mod tests { "3".to_owned() ); assert_eq!( - walk_path( + walk_path_combined( &vec![&deep_context as &dyn ContextElement], &vec!["tiger", "food"] ) diff --git a/src/renderer/walking.rs b/src/renderer/walking.rs index 4bc289b..8d0b2f7 100644 --- a/src/renderer/walking.rs +++ b/src/renderer/walking.rs @@ -1,5 +1,6 @@ use crate::renderer::context_element::ContextElement; use crate::renderer::WalkError; +use std::borrow::Borrow; enum WalkResult<'a> { NoWalk, @@ -32,6 +33,80 @@ fn walk_path_from_single_level<'a>( WalkResult::FullyWalked(output) } +fn walk_path_from_single_level_borrow<'a, P, C>(context: &'a C, path: &[P]) -> WalkResult<'a> +where + P: Borrow, + C: Borrow, +{ + if path.is_empty() { + return WalkResult::FullyWalked(context.borrow()); + } + + let mut walk_failure = WalkResult::NoWalk; + let mut output = context.borrow(); + for elem in path.iter() { + match output.borrow().walk(elem.borrow()) { + Err(WalkError::CantWalk { .. }) => { + return walk_failure; + } + Ok(new_val) => { + walk_failure = WalkResult::PartialWalk; + output = new_val; + } + } + } + + WalkResult::FullyWalked(output) +} + +pub fn walk_path_combined<'a, B, P>( + breadcrumbs: &'a Vec, + path: &Vec

, +) -> Result<&'a dyn ContextElement, WalkError> +where + B: Borrow, + P: Borrow, +{ + if path.is_empty() { + return Ok(breadcrumbs + .last() + .expect("Breadcrumbs should never be empty.") + .borrow()); + } + if path + .first() + .expect("Already proved path is not empty") + .borrow() + == "." + { + return match walk_path_from_single_level_borrow( + breadcrumbs + .last() + .expect("Breadcrumbs should never be empty"), + &path[1..], + ) { + // If no walking was done at all or we partially walked + // then stop trying to find anything because '.' restricts + // us to the current scope + WalkResult::NoWalk | WalkResult::PartialWalk => Err(WalkError::CantWalk), + WalkResult::FullyWalked(new_context) => Ok(new_context), + }; + } + for context in breadcrumbs.iter().rev() { + match walk_path_from_single_level_borrow(context, path) { + // If no walking was done at all, keep looping + WalkResult::NoWalk => {} + // If we partially walked then stop trying to find + // anything + WalkResult::PartialWalk => { + return Err(WalkError::CantWalk); + } + WalkResult::FullyWalked(new_context) => return Ok(new_context), + } + } + Err(WalkError::CantWalk) +} + pub fn walk_path<'a>( breadcrumbs: &Vec<&'a dyn ContextElement>, path: &'a Vec<&str>,