use crate::parser::KVPair; use crate::parser::{Filter, RValue}; use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoBoxedContextElement; use crate::renderer::walking::walk_path; use crate::renderer::Loopable; use crate::renderer::RenderError; use crate::renderer::Renderable; use crate::renderer::WalkError; use crate::renderer::Walkable; use std::collections::HashMap; /// Copy the data from an RValue to an Owned struct /// /// In order to get comparisons to work for our `ContextElement` trait /// objects, we need to be able to use `std::any::Any`. Unfortunately, /// `Any` requires that the structs do not have a lifetime (so they /// will have a `'static` lifetime. This means that we cannot have a /// `<'a>` appended to the struct type, so the struct cannot contain /// any borrows. Rather than impose the copy cost in the parser, we /// are imposing the cost of copying the data in the renderer because /// the parser has no reason to not be able to reference data from the /// input string. pub enum OwnedRValue { RVPath(OwnedPath), RVString(String), } pub struct OwnedPath { pub keys: Vec, } impl From<&RValue<'_>> for OwnedRValue { fn from(original: &RValue<'_>) -> Self { match original { RValue::RVString(text) => OwnedRValue::RVString(text.to_owned()), RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath { keys: path.keys.iter().map(|k| k.to_string()).collect(), }), } } } pub struct NewParametersContext { params: HashMap, breadcrumbs: Vec>, } impl NewParametersContext { pub fn new( breadcrumbs: &Vec<&dyn ContextElement>, params: &Vec, ) -> NewParametersContext { let owned_params: HashMap = params .iter() .map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value))) .collect(); let owned_breadcrumbs: Vec> = breadcrumbs.iter().map(|ce| ce.clone_to_box()).collect(); NewParametersContext { params: owned_params, breadcrumbs: owned_breadcrumbs, } } } // #[derive(Clone, Debug)] // pub struct ParametersContext<'a> { // params: HashMap<&'a str, &'a RValue<'a>>, // breadcrumbs: &'a Vec<&'a dyn ContextElement>, // } // impl<'a> ParametersContext<'a> { // pub fn new( // breadcrumbs: &'a Vec<&'a dyn ContextElement>, // params: &'a Vec>, // ) -> ParametersContext<'a> { // let param_map = params // .iter() // .map(|pair: &KVPair<'a>| (pair.key, &pair.value)) // .collect(); // ParametersContext { // params: param_map, // breadcrumbs: breadcrumbs, // } // } // } // impl<'a> ContextElement for ParametersContext<'a> {} // impl<'a> Renderable for ParametersContext<'a> { // fn render(&self, _filters: &Vec) -> Result { // // TODO: Would this even ever be called? Won't matter, but I'd // // like to know. Since it is injected 1 above the current // // context, we wouldn't be able to access it with `{.}`. // Ok("[object Object]".to_owned()) // } // } // impl<'a> Loopable for ParametersContext<'a> { // fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { // // TODO: Would this even ever be called? Won't matter, but I'd // // like to know. Since it is injected 1 above the current // // context, we wouldn't be able to access it with `{.}`. // vec![self] // } // } // impl<'a> Walkable for ParametersContext<'a> { // fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { // let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; // match rval { // RValue::RVPath(path) => walk_path(self.breadcrumbs, &path.keys), // RValue::RVString(text) => Ok(text), // } // } // } impl ContextElement for String {} impl Renderable for String { fn render(&self, _filters: &Vec) -> Result { Ok(self.clone()) } } impl Loopable for String { fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { if self.is_empty() { Vec::new() } else { vec![self] } } } impl Walkable for String { fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> { Err(WalkError::CantWalk) } } impl IntoBoxedContextElement for String { fn clone_to_box(&self) -> Box { Box::new(self.clone()) } }