Starting the new parameters context object.

This commit is contained in:
Tom Alexander 2020-05-31 21:46:00 -04:00
parent 7bf8012d6e
commit ff74d78fdb
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
3 changed files with 274 additions and 254 deletions

View File

@ -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;

View File

@ -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<PartialNameElement>),
RVLiteral(OwnedLiteral),
}
#[derive(Clone, Debug, PartialEq)]
pub struct OwnedPath {
pub keys: Vec<String>,
}
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<String, OwnedRValue>,
breadcrumbs: Vec<Box<dyn IntoContextElement>>,
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<KVPair>,
) -> ParametersContext {
let owned_params: HashMap<String, OwnedRValue> = params
.iter()
.map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value)))
.collect();
let owned_breadcrumbs: Vec<Box<dyn IntoContextElement>> =
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<Filter>) -> Result<String, RenderError> {
// 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<String, OwnedRValue> = self
.params
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
let new_breadcrumbs: Vec<Box<dyn IntoContextElement>> = 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<Ordering> {
// 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<Filter>) -> Result<String, RenderError> {
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::<Self>() {
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<Ordering> {
// 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::<Self>() {
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<PartialNameElement> {
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<PartialNameElement> {
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
Err(WalkError::CantWalk)
breadcrumbs: Option<&BreadcrumbTree>,
params: &Vec<KVPair>,
) -> Self {
todo!()
}
}

View File

@ -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<PartialNameElement>),
RVLiteral(OwnedLiteral),
}
#[derive(Clone, Debug, PartialEq)]
pub struct OwnedPath {
pub keys: Vec<String>,
}
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<String, OwnedRValue>,
breadcrumbs: Vec<Box<dyn IntoContextElement>>,
}
impl ParametersContext {
pub fn new(
breadcrumbs: &Vec<&dyn IntoContextElement>,
params: &Vec<KVPair>,
) -> ParametersContext {
let owned_params: HashMap<String, OwnedRValue> = params
.iter()
.map(|kvpair| (kvpair.key.to_string(), OwnedRValue::from(&kvpair.value)))
.collect();
let owned_breadcrumbs: Vec<Box<dyn IntoContextElement>> =
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<Filter>) -> Result<String, RenderError> {
// 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<String, OwnedRValue> = self
.params
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
let new_breadcrumbs: Vec<Box<dyn IntoContextElement>> = 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<Ordering> {
// 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<Filter>) -> Result<String, RenderError> {
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::<Self>() {
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<Ordering> {
// 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::<Self>() {
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<PartialNameElement> {
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<PartialNameElement> {
fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> {
Err(WalkError::CantWalk)
}
}