diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 2ebbc51..6baa196 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -5,7 +5,7 @@ mod context_element; mod errors; mod inline_partial_tree; // mod iteration_context; -// mod parameters_context; +mod parameters_context; mod renderer; mod tree_walking; mod walking; diff --git a/src/renderer/parameters_context.rs b/src/renderer/parameters_context.rs index 0fb4abf..3261b6e 100644 --- a/src/renderer/parameters_context.rs +++ b/src/renderer/parameters_context.rs @@ -1,264 +1,20 @@ use crate::parser::KVPair; -use crate::parser::{Filter, OwnedLiteral, PartialNameElement, RValue}; -use crate::renderer::context_element::CompareContextElement; -use crate::renderer::context_element::ContextElement; +use crate::renderer::breadcrumb_tree::BreadcrumbTree; use crate::renderer::context_element::IntoContextElement; -use crate::renderer::walking::walk_path; use crate::renderer::DustRenderer; -use crate::renderer::Loopable; -use crate::renderer::RenderError; -use crate::renderer::Renderable; -use crate::renderer::Truthiness; -use crate::renderer::WalkError; -use crate::renderer::Walkable; -use std::{cmp::Ordering, 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. -#[derive(Clone, Debug)] -pub enum OwnedRValue { - RVPath(OwnedPath), - RVTemplate(Vec), - RVLiteral(OwnedLiteral), -} - -#[derive(Clone, Debug, PartialEq)] -pub struct OwnedPath { - pub keys: Vec, -} - -impl From<&RValue<'_>> for OwnedRValue { - fn from(original: &RValue<'_>) -> Self { - match original { - RValue::RVLiteral(literal) => OwnedRValue::RVLiteral(literal.clone()), - RValue::RVTemplate(template) => OwnedRValue::RVTemplate(template.clone()), - RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath { - keys: path.keys.iter().map(|k| k.to_string()).collect(), - }), - } - } -} +use std::collections::HashMap; #[derive(Debug)] -pub struct ParametersContext { - params: HashMap, - breadcrumbs: Vec>, +pub struct ParametersContext<'a> { + params: HashMap<&'a str, &'a dyn IntoContextElement>, } -impl ParametersContext { +impl<'a> ParametersContext<'a> { pub fn new( - breadcrumbs: &Vec<&dyn IntoContextElement>, - params: &Vec, - ) -> ParametersContext { - 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(); - - ParametersContext { - params: owned_params, - breadcrumbs: owned_breadcrumbs, - } - } -} - -impl ContextElement for ParametersContext {} - -impl Truthiness for ParametersContext { - fn is_truthy(&self) -> bool { - // 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 `{.}`. - true - } -} - -impl Renderable for ParametersContext { - 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 Loopable for ParametersContext { - 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::new() - } -} - -impl Walkable for ParametersContext { - fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { - let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; - match rval { - OwnedRValue::RVPath(path) => walk_path(&self.breadcrumbs, &path.keys), - OwnedRValue::RVTemplate(template) => Ok(template), - OwnedRValue::RVLiteral(literal) => Ok(literal), - } - } - - fn is_pseudo_element(&self) -> bool { - true - } -} - -impl Clone for ParametersContext { - fn clone(&self) -> Self { - let new_params: HashMap = self - .params - .iter() - .map(|(k, v)| (k.clone(), v.clone())) - .collect(); - let new_breadcrumbs: Vec> = self - .breadcrumbs - .iter() - .map(|bread| bread.clone_to_box()) - .collect(); - ParametersContext { - params: new_params, - breadcrumbs: new_breadcrumbs, - } - } -} - -impl CompareContextElement for ParametersContext { - fn equals(&self, other: &dyn ContextElement) -> bool { - // TODO: Does this ever happen? perhaps I should have a panic here. - false - } - - fn partial_compare(&self, other: &dyn ContextElement) -> Option { - // TODO: Does this ever happen? perhaps I should have a panic here. - None - } -} - -impl ContextElement for OwnedLiteral {} - -impl Truthiness for OwnedLiteral { - fn is_truthy(&self) -> bool { - match self { - OwnedLiteral::LString(text) => !text.is_empty(), - OwnedLiteral::LPositiveInteger(num) => true, - } - } -} - -impl Renderable for OwnedLiteral { - fn render(&self, _filters: &Vec) -> Result { - match self { - OwnedLiteral::LString(text) => Ok(text.clone()), - OwnedLiteral::LPositiveInteger(num) => Ok(num.to_string()), - } - } -} - -impl Loopable for OwnedLiteral { - fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { - Vec::new() - } -} - -impl Walkable for OwnedLiteral { - fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { - Err(WalkError::CantWalk) - } -} - -impl CompareContextElement for OwnedLiteral { - fn equals(&self, other: &dyn ContextElement) -> bool { - // println!("equals literal {:?} | {:?}", self, other); - // If its an OwnedLiteral then compare them directly, - // otherwise defer to the other type's implementation of - // CompareContextElement since the end user could add any - // type. - match other.to_any().downcast_ref::() { - None => other.equals(self), - Some(other_literal) => match (self, other_literal) { - (OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => { - self_text == other_text - } - (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => { - &self_num.to_string() == other_text - } - (OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => { - self_text == &other_num.to_string() - } - ( - OwnedLiteral::LPositiveInteger(self_num), - OwnedLiteral::LPositiveInteger(other_num), - ) => self_num == other_num, - }, - } - } - - fn partial_compare(&self, other: &dyn ContextElement) -> Option { - // println!("partial_compare literal {:?} | {:?}", self, other); - // If its an OwnedLiteral then compare them directly, - // otherwise defer to the other type's implementation of - // CompareContextElement since the end user could add any - // type. - match other.to_any().downcast_ref::() { - None => match other.partial_compare(self) { - None => None, - Some(ord) => match ord { - Ordering::Equal => Some(Ordering::Equal), - Ordering::Greater => Some(Ordering::Less), - Ordering::Less => Some(Ordering::Greater), - }, - }, - Some(other_literal) => match (self, other_literal) { - (OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => { - self_text.partial_cmp(other_text) - } - (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => { - self_num.to_string().partial_cmp(other_text) - } - (OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => { - self_text.partial_cmp(&other_num.to_string()) - } - ( - OwnedLiteral::LPositiveInteger(self_num), - OwnedLiteral::LPositiveInteger(other_num), - ) => self_num.partial_cmp(other_num), - }, - } - } -} - -impl IntoContextElement for Vec { - fn into_context_element( - &self, renderer: &DustRenderer, - breadcrumbs: &Vec<&dyn IntoContextElement>, - ) -> &dyn ContextElement { - // OwnedLiteral::LString( - // renderer - // .render_partial_name(self, breadcrumbs) - // .expect("TODO: Make into_context_element return a RenderError"), - // ) - // TODO - &OwnedLiteral::LPositiveInteger(1) - } -} - -impl Walkable for Vec { - fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { - Err(WalkError::CantWalk) + breadcrumbs: Option<&BreadcrumbTree>, + params: &Vec, + ) -> Self { + todo!() } } diff --git a/src/renderer/parameters_context_old.rs b/src/renderer/parameters_context_old.rs new file mode 100644 index 0000000..0fb4abf --- /dev/null +++ b/src/renderer/parameters_context_old.rs @@ -0,0 +1,264 @@ +use crate::parser::KVPair; +use crate::parser::{Filter, OwnedLiteral, PartialNameElement, RValue}; +use crate::renderer::context_element::CompareContextElement; +use crate::renderer::context_element::ContextElement; +use crate::renderer::context_element::IntoContextElement; +use crate::renderer::walking::walk_path; +use crate::renderer::DustRenderer; +use crate::renderer::Loopable; +use crate::renderer::RenderError; +use crate::renderer::Renderable; +use crate::renderer::Truthiness; +use crate::renderer::WalkError; +use crate::renderer::Walkable; +use std::{cmp::Ordering, 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. +#[derive(Clone, Debug)] +pub enum OwnedRValue { + RVPath(OwnedPath), + RVTemplate(Vec), + RVLiteral(OwnedLiteral), +} + +#[derive(Clone, Debug, PartialEq)] +pub struct OwnedPath { + pub keys: Vec, +} + +impl From<&RValue<'_>> for OwnedRValue { + fn from(original: &RValue<'_>) -> Self { + match original { + RValue::RVLiteral(literal) => OwnedRValue::RVLiteral(literal.clone()), + RValue::RVTemplate(template) => OwnedRValue::RVTemplate(template.clone()), + RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath { + keys: path.keys.iter().map(|k| k.to_string()).collect(), + }), + } + } +} + +#[derive(Debug)] +pub struct ParametersContext { + params: HashMap, + breadcrumbs: Vec>, +} + +impl ParametersContext { + pub fn new( + breadcrumbs: &Vec<&dyn IntoContextElement>, + params: &Vec, + ) -> ParametersContext { + 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(); + + ParametersContext { + params: owned_params, + breadcrumbs: owned_breadcrumbs, + } + } +} + +impl ContextElement for ParametersContext {} + +impl Truthiness for ParametersContext { + fn is_truthy(&self) -> bool { + // 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 `{.}`. + true + } +} + +impl Renderable for ParametersContext { + 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 Loopable for ParametersContext { + 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::new() + } +} + +impl Walkable for ParametersContext { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?; + match rval { + OwnedRValue::RVPath(path) => walk_path(&self.breadcrumbs, &path.keys), + OwnedRValue::RVTemplate(template) => Ok(template), + OwnedRValue::RVLiteral(literal) => Ok(literal), + } + } + + fn is_pseudo_element(&self) -> bool { + true + } +} + +impl Clone for ParametersContext { + fn clone(&self) -> Self { + let new_params: HashMap = self + .params + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect(); + let new_breadcrumbs: Vec> = self + .breadcrumbs + .iter() + .map(|bread| bread.clone_to_box()) + .collect(); + ParametersContext { + params: new_params, + breadcrumbs: new_breadcrumbs, + } + } +} + +impl CompareContextElement for ParametersContext { + fn equals(&self, other: &dyn ContextElement) -> bool { + // TODO: Does this ever happen? perhaps I should have a panic here. + false + } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // TODO: Does this ever happen? perhaps I should have a panic here. + None + } +} + +impl ContextElement for OwnedLiteral {} + +impl Truthiness for OwnedLiteral { + fn is_truthy(&self) -> bool { + match self { + OwnedLiteral::LString(text) => !text.is_empty(), + OwnedLiteral::LPositiveInteger(num) => true, + } + } +} + +impl Renderable for OwnedLiteral { + fn render(&self, _filters: &Vec) -> Result { + match self { + OwnedLiteral::LString(text) => Ok(text.clone()), + OwnedLiteral::LPositiveInteger(num) => Ok(num.to_string()), + } + } +} + +impl Loopable for OwnedLiteral { + fn get_loop_elements(&self) -> Vec<&dyn ContextElement> { + Vec::new() + } +} + +impl Walkable for OwnedLiteral { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + Err(WalkError::CantWalk) + } +} + +impl CompareContextElement for OwnedLiteral { + fn equals(&self, other: &dyn ContextElement) -> bool { + // println!("equals literal {:?} | {:?}", self, other); + // If its an OwnedLiteral then compare them directly, + // otherwise defer to the other type's implementation of + // CompareContextElement since the end user could add any + // type. + match other.to_any().downcast_ref::() { + None => other.equals(self), + Some(other_literal) => match (self, other_literal) { + (OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => { + self_text == other_text + } + (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => { + &self_num.to_string() == other_text + } + (OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => { + self_text == &other_num.to_string() + } + ( + OwnedLiteral::LPositiveInteger(self_num), + OwnedLiteral::LPositiveInteger(other_num), + ) => self_num == other_num, + }, + } + } + + fn partial_compare(&self, other: &dyn ContextElement) -> Option { + // println!("partial_compare literal {:?} | {:?}", self, other); + // If its an OwnedLiteral then compare them directly, + // otherwise defer to the other type's implementation of + // CompareContextElement since the end user could add any + // type. + match other.to_any().downcast_ref::() { + None => match other.partial_compare(self) { + None => None, + Some(ord) => match ord { + Ordering::Equal => Some(Ordering::Equal), + Ordering::Greater => Some(Ordering::Less), + Ordering::Less => Some(Ordering::Greater), + }, + }, + Some(other_literal) => match (self, other_literal) { + (OwnedLiteral::LString(self_text), OwnedLiteral::LString(other_text)) => { + self_text.partial_cmp(other_text) + } + (OwnedLiteral::LPositiveInteger(self_num), OwnedLiteral::LString(other_text)) => { + self_num.to_string().partial_cmp(other_text) + } + (OwnedLiteral::LString(self_text), OwnedLiteral::LPositiveInteger(other_num)) => { + self_text.partial_cmp(&other_num.to_string()) + } + ( + OwnedLiteral::LPositiveInteger(self_num), + OwnedLiteral::LPositiveInteger(other_num), + ) => self_num.partial_cmp(other_num), + }, + } + } +} + +impl IntoContextElement for Vec { + fn into_context_element( + &self, + renderer: &DustRenderer, + breadcrumbs: &Vec<&dyn IntoContextElement>, + ) -> &dyn ContextElement { + // OwnedLiteral::LString( + // renderer + // .render_partial_name(self, breadcrumbs) + // .expect("TODO: Make into_context_element return a RenderError"), + // ) + // TODO + &OwnedLiteral::LPositiveInteger(1) + } +} + +impl Walkable for Vec { + fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { + Err(WalkError::CantWalk) + } +}