use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoContextElement; use crate::renderer::WalkError; use std::borrow::Borrow; enum WalkResult<'a> { NoWalk, PartialWalk, FullyWalked(&'a dyn IntoContextElement), } fn walk_path_from_single_level<'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 get_first_non_pseudo_element<'a, B>(breadcrumbs: &'a Vec) -> Option<&B> where B: Borrow, { breadcrumbs .iter() .rev() .filter(|b| !(*b).borrow().is_pseudo_element()) .next() } pub fn walk_path<'a, B, P>( breadcrumbs: &'a Vec, path: &Vec

, ) -> Result<&'a dyn IntoContextElement, WalkError> where B: Borrow, P: Borrow, { if breadcrumbs.is_empty() { // This happens when you use a section with an explicit // context path, where both the path and the explicit context // path fail, leaving you with absolutely no context. return Err(WalkError::CantWalk); } 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() == "." { let first_non_pseudo_element = get_first_non_pseudo_element(breadcrumbs); return match first_non_pseudo_element { None => Err(WalkError::CantWalk), Some(current_context) => { match walk_path_from_single_level(current_context, &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(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) }