Merge branch 'remove_lifetime_from_context_elements' into dust_helpers
This commit is contained in:
commit
3c85717952
22
src/bin.rs
22
src/bin.rs
@ -1,5 +1,6 @@
|
|||||||
extern crate nom;
|
extern crate nom;
|
||||||
|
|
||||||
|
use crate::renderer::CompareContextElement;
|
||||||
use parser::Filter;
|
use parser::Filter;
|
||||||
use renderer::compile_template;
|
use renderer::compile_template;
|
||||||
use renderer::CompiledTemplate;
|
use renderer::CompiledTemplate;
|
||||||
@ -137,3 +138,24 @@ impl Loopable for serde_json::Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CompareContextElement for serde_json::Value {
|
||||||
|
fn equals(&self, other: &dyn ContextElement) -> bool {
|
||||||
|
// Handle other serde_json::Value
|
||||||
|
match other.to_any().downcast_ref::<Self>() {
|
||||||
|
None => (),
|
||||||
|
Some(other_json_value) => return self == other_json_value,
|
||||||
|
}
|
||||||
|
// Handle string literals
|
||||||
|
match other.to_any().downcast_ref::<String>() {
|
||||||
|
None => (),
|
||||||
|
Some(other_string) => return self.as_str().map_or(false, |s| s == other_string),
|
||||||
|
}
|
||||||
|
// Handle numeric literals
|
||||||
|
match other.to_any().downcast_ref::<u64>() {
|
||||||
|
None => (),
|
||||||
|
Some(other_num) => return self.as_u64().map_or(false, |n| n == *other_num),
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,11 +2,11 @@ use nom::branch::alt;
|
|||||||
use nom::bytes::complete::escaped_transform;
|
use nom::bytes::complete::escaped_transform;
|
||||||
use nom::bytes::complete::is_a;
|
use nom::bytes::complete::is_a;
|
||||||
use nom::bytes::complete::is_not;
|
use nom::bytes::complete::is_not;
|
||||||
use nom::bytes::complete::{tag, take_until, take_until_parser_matches};
|
use nom::bytes::complete::{tag, take_until, take_until_parser_matches, take_while};
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::multispace0;
|
use nom::character::complete::multispace0;
|
||||||
use nom::character::complete::one_of;
|
use nom::character::complete::one_of;
|
||||||
use nom::character::complete::{space0, space1};
|
use nom::character::complete::{digit1, space0, space1};
|
||||||
use nom::combinator::all_consuming;
|
use nom::combinator::all_consuming;
|
||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
@ -122,6 +122,7 @@ pub struct Partial<'a> {
|
|||||||
pub enum RValue<'a> {
|
pub enum RValue<'a> {
|
||||||
RVPath(Path<'a>),
|
RVPath(Path<'a>),
|
||||||
RVString(String),
|
RVString(String),
|
||||||
|
RVPositiveInteger(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@ -210,11 +211,23 @@ fn path(i: &str) -> IResult<&str, Path> {
|
|||||||
))(i)
|
))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Just digits, no signs or decimals
|
||||||
|
fn postitive_integer_literal(i: &str) -> IResult<&str, u64> {
|
||||||
|
map(
|
||||||
|
verify(
|
||||||
|
map(digit1, |number_string: &str| number_string.parse::<u64>()),
|
||||||
|
|parse_result| parse_result.is_ok(),
|
||||||
|
),
|
||||||
|
|parsed_number| parsed_number.unwrap(),
|
||||||
|
)(i)
|
||||||
|
}
|
||||||
|
|
||||||
/// Either a literal or a path to a value
|
/// Either a literal or a path to a value
|
||||||
fn rvalue(i: &str) -> IResult<&str, RValue> {
|
fn rvalue(i: &str) -> IResult<&str, RValue> {
|
||||||
alt((
|
alt((
|
||||||
map(path, RValue::RVPath),
|
map(path, RValue::RVPath),
|
||||||
map(quoted_string, RValue::RVString),
|
map(quoted_string, RValue::RVString),
|
||||||
|
map(postitive_integer_literal, RValue::RVPositiveInteger),
|
||||||
))(i)
|
))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -929,6 +942,29 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_literals() {
|
||||||
|
assert_eq!(
|
||||||
|
dust_tag(r#"{>foo a="foo" b=179/}"#),
|
||||||
|
Ok((
|
||||||
|
"",
|
||||||
|
DustTag::DTPartial(Partial {
|
||||||
|
name: "foo".to_owned(),
|
||||||
|
params: vec![
|
||||||
|
KVPair {
|
||||||
|
key: "a",
|
||||||
|
value: RValue::RVString("foo".to_owned())
|
||||||
|
},
|
||||||
|
KVPair {
|
||||||
|
key: "b",
|
||||||
|
value: RValue::RVPositiveInteger(179)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_helper() {
|
fn test_helper() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
use crate::parser::Filter;
|
use crate::parser::Filter;
|
||||||
use crate::renderer::errors::RenderError;
|
use crate::renderer::errors::RenderError;
|
||||||
use crate::renderer::errors::WalkError;
|
use crate::renderer::errors::WalkError;
|
||||||
|
use std::any::Any;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
pub trait ContextElement: Debug + Walkable + Renderable + Loopable {}
|
pub trait ContextElement:
|
||||||
|
Debug + Walkable + Renderable + Loopable + CloneIntoBoxedContextElement + CompareContextElement
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Walkable {
|
pub trait Walkable {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError>;
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError>;
|
||||||
@ -26,8 +30,32 @@ pub trait Loopable {
|
|||||||
fn get_loop_elements(&self) -> Vec<&dyn ContextElement>;
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl PartialEq<dyn ContextElement> for dyn ContextElement {
|
pub trait CastToAny {
|
||||||
// fn eq(&self, other: &dyn ContextElement) -> bool {
|
fn to_any(&self) -> &dyn Any;
|
||||||
// todo!()
|
}
|
||||||
// }
|
|
||||||
// }
|
pub trait CompareContextElement: CastToAny {
|
||||||
|
fn equals(&self, other: &dyn ContextElement) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CloneIntoBoxedContextElement {
|
||||||
|
fn clone_to_box(&self) -> Box<dyn ContextElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: 'static + ContextElement + Clone> CloneIntoBoxedContextElement for C {
|
||||||
|
fn clone_to_box(&self) -> Box<dyn ContextElement> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: 'static + ContextElement> CastToAny for C {
|
||||||
|
fn to_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> PartialEq<&'b dyn ContextElement> for &'a dyn ContextElement {
|
||||||
|
fn eq(&self, other: &&'b dyn ContextElement) -> bool {
|
||||||
|
self.equals(*other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,6 +7,8 @@ mod parameters_context;
|
|||||||
mod renderer;
|
mod renderer;
|
||||||
mod walking;
|
mod walking;
|
||||||
|
|
||||||
|
pub use context_element::CloneIntoBoxedContextElement;
|
||||||
|
pub use context_element::CompareContextElement;
|
||||||
pub use context_element::ContextElement;
|
pub use context_element::ContextElement;
|
||||||
pub use context_element::Loopable;
|
pub use context_element::Loopable;
|
||||||
pub use context_element::Renderable;
|
pub use context_element::Renderable;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::parser::KVPair;
|
use crate::parser::KVPair;
|
||||||
use crate::parser::{Filter, RValue};
|
use crate::parser::{Filter, RValue};
|
||||||
|
use crate::renderer::context_element::CompareContextElement;
|
||||||
use crate::renderer::context_element::ContextElement;
|
use crate::renderer::context_element::ContextElement;
|
||||||
use crate::renderer::walking::walk_path;
|
use crate::renderer::walking::owned_walk_path;
|
||||||
use crate::renderer::Loopable;
|
use crate::renderer::Loopable;
|
||||||
use crate::renderer::RenderError;
|
use crate::renderer::RenderError;
|
||||||
use crate::renderer::Renderable;
|
use crate::renderer::Renderable;
|
||||||
@ -9,31 +10,66 @@ use crate::renderer::WalkError;
|
|||||||
use crate::renderer::Walkable;
|
use crate::renderer::Walkable;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
/// Copy the data from an RValue to an Owned struct
|
||||||
pub struct ParametersContext<'a> {
|
///
|
||||||
params: HashMap<&'a str, &'a RValue<'a>>,
|
/// In order to get comparisons to work for our `ContextElement` trait
|
||||||
breadcrumbs: &'a Vec<&'a dyn ContextElement>,
|
/// 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, PartialEq)]
|
||||||
|
pub enum OwnedRValue {
|
||||||
|
RVPath(OwnedPath),
|
||||||
|
RVString(String),
|
||||||
|
RVPositiveInteger(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ParametersContext<'a> {
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub fn new(
|
pub struct OwnedPath {
|
||||||
breadcrumbs: &'a Vec<&'a dyn ContextElement>,
|
pub keys: Vec<String>,
|
||||||
params: &'a Vec<KVPair<'a>>,
|
}
|
||||||
) -> ParametersContext<'a> {
|
|
||||||
let param_map = params
|
impl From<&RValue<'_>> for OwnedRValue {
|
||||||
.iter()
|
fn from(original: &RValue<'_>) -> Self {
|
||||||
.map(|pair: &KVPair<'a>| (pair.key, &pair.value))
|
match original {
|
||||||
.collect();
|
RValue::RVString(text) => OwnedRValue::RVString(text.to_owned()),
|
||||||
ParametersContext {
|
RValue::RVPath(path) => OwnedRValue::RVPath(OwnedPath {
|
||||||
params: param_map,
|
keys: path.keys.iter().map(|k| k.to_string()).collect(),
|
||||||
breadcrumbs: breadcrumbs,
|
}),
|
||||||
|
RValue::RVPositiveInteger(num) => OwnedRValue::RVPositiveInteger(*num),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ContextElement for ParametersContext<'a> {}
|
#[derive(Debug)]
|
||||||
|
pub struct ParametersContext {
|
||||||
|
params: HashMap<String, OwnedRValue>,
|
||||||
|
breadcrumbs: Vec<Box<dyn ContextElement>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Renderable for ParametersContext<'a> {
|
impl ParametersContext {
|
||||||
|
pub fn new(breadcrumbs: &Vec<&dyn ContextElement>, 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 ContextElement>> =
|
||||||
|
breadcrumbs.iter().map(|ce| ce.clone_to_box()).collect();
|
||||||
|
|
||||||
|
ParametersContext {
|
||||||
|
params: owned_params,
|
||||||
|
breadcrumbs: owned_breadcrumbs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextElement for ParametersContext {}
|
||||||
|
|
||||||
|
impl Renderable for ParametersContext {
|
||||||
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
||||||
// TODO: Would this even ever be called? Won't matter, but I'd
|
// TODO: Would this even ever be called? Won't matter, but I'd
|
||||||
// like to know. Since it is injected 1 above the current
|
// like to know. Since it is injected 1 above the current
|
||||||
@ -42,7 +78,7 @@ impl<'a> Renderable for ParametersContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Loopable for ParametersContext<'a> {
|
impl Loopable for ParametersContext {
|
||||||
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
||||||
// TODO: Would this even ever be called? Won't matter, but I'd
|
// TODO: Would this even ever be called? Won't matter, but I'd
|
||||||
// like to know. Since it is injected 1 above the current
|
// like to know. Since it is injected 1 above the current
|
||||||
@ -51,16 +87,43 @@ impl<'a> Loopable for ParametersContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Walkable for ParametersContext<'a> {
|
impl Walkable for ParametersContext {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
||||||
let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?;
|
let rval = self.params.get(segment).ok_or(WalkError::CantWalk)?;
|
||||||
match rval {
|
match rval {
|
||||||
RValue::RVPath(path) => walk_path(self.breadcrumbs, &path.keys),
|
OwnedRValue::RVPath(path) => owned_walk_path(&self.breadcrumbs, &path.keys),
|
||||||
RValue::RVString(text) => Ok(text),
|
OwnedRValue::RVString(text) => Ok(text),
|
||||||
|
OwnedRValue::RVPositiveInteger(num) => Ok(num),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 ContextElement>> = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ContextElement for String {}
|
impl ContextElement for String {}
|
||||||
|
|
||||||
impl Renderable for String {
|
impl Renderable for String {
|
||||||
@ -84,3 +147,47 @@ impl Walkable for String {
|
|||||||
Err(WalkError::CantWalk)
|
Err(WalkError::CantWalk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CompareContextElement for String {
|
||||||
|
fn equals(&self, other: &dyn ContextElement) -> bool {
|
||||||
|
// If its a String 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_string) => self == other_string,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextElement for u64 {}
|
||||||
|
|
||||||
|
impl Renderable for u64 {
|
||||||
|
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
||||||
|
Ok(self.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Loopable for u64 {
|
||||||
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
||||||
|
vec![self]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Walkable for u64 {
|
||||||
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
||||||
|
Err(WalkError::CantWalk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CompareContextElement for u64 {
|
||||||
|
fn equals(&self, other: &dyn ContextElement) -> bool {
|
||||||
|
// If its a u64 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_num) => self == other_num,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -235,6 +235,7 @@ impl<'a> DustRenderer<'a> {
|
|||||||
Some(rval) => match rval {
|
Some(rval) => match rval {
|
||||||
RValue::RVString(text) => Ok(text),
|
RValue::RVString(text) => Ok(text),
|
||||||
RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys),
|
RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys),
|
||||||
|
RValue::RVPositiveInteger(num) => Ok(num),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let right_side: Result<&dyn ContextElement, WalkError> =
|
let right_side: Result<&dyn ContextElement, WalkError> =
|
||||||
@ -243,16 +244,26 @@ impl<'a> DustRenderer<'a> {
|
|||||||
Some(rval) => match rval {
|
Some(rval) => match rval {
|
||||||
RValue::RVString(text) => Ok(text),
|
RValue::RVString(text) => Ok(text),
|
||||||
RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys),
|
RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys),
|
||||||
|
RValue::RVPositiveInteger(num) => Ok(num),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// let x = WalkError::CantWalk;
|
if left_side == right_side {
|
||||||
// let y = WalkError::CantWalk;
|
match ¶meterized_block.contents {
|
||||||
// if x == y {
|
None => return Ok("".to_owned()),
|
||||||
// panic!("placeholder");
|
Some(body) => {
|
||||||
// }
|
let rendered_content = self.render_body(body, breadcrumbs, blocks)?;
|
||||||
// if left_side.unwrap() == right_side.unwrap() {
|
return Ok(rendered_content);
|
||||||
// panic!("placeholder");
|
}
|
||||||
// }
|
}
|
||||||
|
} else {
|
||||||
|
match ¶meterized_block.else_contents {
|
||||||
|
None => return Ok("".to_owned()),
|
||||||
|
Some(body) => {
|
||||||
|
let rendered_content = self.render_body(body, breadcrumbs, blocks)?;
|
||||||
|
return Ok(rendered_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => (), // TODO: Implement the rest
|
_ => (), // TODO: Implement the rest
|
||||||
}
|
}
|
||||||
@ -281,88 +292,76 @@ mod tests {
|
|||||||
use crate::renderer::context_element::Loopable;
|
use crate::renderer::context_element::Loopable;
|
||||||
use crate::renderer::context_element::Renderable;
|
use crate::renderer::context_element::Renderable;
|
||||||
use crate::renderer::context_element::Walkable;
|
use crate::renderer::context_element::Walkable;
|
||||||
|
use crate::renderer::CompareContextElement;
|
||||||
|
|
||||||
impl ContextElement for u32 {}
|
impl<I: 'static + ContextElement + Clone> ContextElement for HashMap<String, I> {}
|
||||||
impl ContextElement for &str {}
|
|
||||||
impl<I: ContextElement> ContextElement for HashMap<&str, I> {}
|
|
||||||
|
|
||||||
impl Renderable for u32 {
|
impl<I: ContextElement> Renderable for HashMap<String, I> {
|
||||||
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
|
||||||
// TODO: handle the filters
|
|
||||||
Ok(self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Renderable for &str {
|
|
||||||
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
|
||||||
// TODO: handle the filters
|
|
||||||
Ok(self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: ContextElement> Renderable for HashMap<&str, I> {
|
|
||||||
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
fn render(&self, _filters: &Vec<Filter>) -> Result<String, RenderError> {
|
||||||
// TODO: handle the filters
|
// TODO: handle the filters
|
||||||
Ok("[object Object]".to_owned())
|
Ok("[object Object]".to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: ContextElement> Walkable for HashMap<&str, I> {
|
impl<I: ContextElement> Walkable for HashMap<String, I> {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
||||||
let child = self.get(segment).ok_or(WalkError::CantWalk)?;
|
let child = self.get(segment).ok_or(WalkError::CantWalk)?;
|
||||||
Ok(child)
|
Ok(child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Walkable for &str {
|
impl<I: 'static + ContextElement + Clone> Loopable for HashMap<String, I> {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
|
||||||
Err(WalkError::CantWalk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Walkable for u32 {
|
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
|
||||||
Err(WalkError::CantWalk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Loopable for &str {
|
|
||||||
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
|
||||||
if self.is_empty() {
|
|
||||||
Vec::new()
|
|
||||||
} else {
|
|
||||||
vec![self]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Loopable for u32 {
|
|
||||||
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
||||||
vec![self]
|
vec![self]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: ContextElement> Loopable for HashMap<&str, I> {
|
impl<I: 'static + ContextElement + Clone> CompareContextElement for HashMap<String, I> {
|
||||||
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
fn equals(&self, other: &dyn ContextElement) -> bool {
|
||||||
vec![self]
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_walk_path() {
|
fn test_walk_path() {
|
||||||
let context: HashMap<&str, &str> =
|
let context: HashMap<String, String> = [
|
||||||
[("cat", "kitty"), ("dog", "doggy"), ("tiger", "murderkitty")]
|
("cat".to_string(), "kitty".to_string()),
|
||||||
.iter()
|
("dog".to_string(), "doggy".to_string()),
|
||||||
.cloned()
|
("tiger".to_string(), "murderkitty".to_string()),
|
||||||
.collect();
|
]
|
||||||
let number_context: HashMap<&str, u32> = [("cat", 1), ("dog", 2), ("tiger", 3)]
|
.iter()
|
||||||
.iter()
|
.cloned()
|
||||||
.cloned()
|
.collect();
|
||||||
.collect();
|
let number_context: HashMap<String, u64> = [
|
||||||
let deep_context: HashMap<&str, HashMap<&str, &str>> = [
|
("cat".to_string(), 1),
|
||||||
("cat", [("food", "meat")].iter().cloned().collect()),
|
("dog".to_string(), 2),
|
||||||
("dog", [("food", "meat")].iter().cloned().collect()),
|
("tiger".to_string(), 3),
|
||||||
("tiger", [("food", "people")].iter().cloned().collect()),
|
]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
let deep_context: HashMap<String, HashMap<String, String>> = [
|
||||||
|
(
|
||||||
|
"cat".to_string(),
|
||||||
|
[("food".to_string(), "meat".to_string())]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"dog".to_string(),
|
||||||
|
[("food".to_string(), "meat".to_string())]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"tiger".to_string(),
|
||||||
|
[("food".to_string(), "people".to_string())]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
|
@ -51,3 +51,23 @@ pub fn walk_path<'a>(
|
|||||||
}
|
}
|
||||||
Err(WalkError::CantWalk)
|
Err(WalkError::CantWalk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn owned_walk_path<'a>(
|
||||||
|
breadcrumbs: &'a Vec<Box<dyn ContextElement>>,
|
||||||
|
path: &Vec<String>,
|
||||||
|
) -> Result<&'a dyn ContextElement, WalkError> {
|
||||||
|
let path_reference: Vec<&str> = path.iter().map(|p| &p[..]).collect();
|
||||||
|
for context in breadcrumbs.iter().rev() {
|
||||||
|
match walk_path_from_single_level(context.as_ref(), &path_reference) {
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user