Merge branch 'partial' into render
This commit is contained in:
commit
afda9ce22f
28
js/test_cases/partial_jump_around_one/input1.json
Normal file
28
js/test_cases/partial_jump_around_one/input1.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"v0": "0",
|
||||||
|
"level3": {
|
||||||
|
"v3": "3",
|
||||||
|
"v4": "3",
|
||||||
|
"v5": "3",
|
||||||
|
"level4": {
|
||||||
|
"v4": "4",
|
||||||
|
"v5": "4",
|
||||||
|
"level5": {
|
||||||
|
"v5": "5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"level1": {
|
||||||
|
"v1": "1",
|
||||||
|
"v2": "1",
|
||||||
|
"v3": "1",
|
||||||
|
"v4": "1",
|
||||||
|
"v5": "1",
|
||||||
|
"level2": {
|
||||||
|
"v2": "2",
|
||||||
|
"v3": "2",
|
||||||
|
"v4": "2",
|
||||||
|
"v5": "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
js/test_cases/partial_jump_around_one/main.dust
Normal file
3
js/test_cases/partial_jump_around_one/main.dust
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{#level1.level2}
|
||||||
|
{>partialone v0="a" v1="a" v2="a" v3="a" v4="a" v5="a"/}
|
||||||
|
{/level1.level2}
|
3
js/test_cases/partial_jump_around_one/partialone.dust
Normal file
3
js/test_cases/partial_jump_around_one/partialone.dust
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{#level3.level4}
|
||||||
|
{>partialtwo/}
|
||||||
|
{/level3.level4}
|
8
js/test_cases/partial_jump_around_one/partialtwo.dust
Normal file
8
js/test_cases/partial_jump_around_one/partialtwo.dust
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{#level5}
|
||||||
|
{v0}{~n}
|
||||||
|
{v1}{~n}
|
||||||
|
{v2}{~n}
|
||||||
|
{v3}{~n}
|
||||||
|
{v4}{~n}
|
||||||
|
{v5}{~n}
|
||||||
|
{/level5}
|
27
js/test_cases/partial_jump_around_two/input1.json
Normal file
27
js/test_cases/partial_jump_around_two/input1.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"level3": {
|
||||||
|
"v3": "3",
|
||||||
|
"v4": "3",
|
||||||
|
"v5": "3",
|
||||||
|
"level4": {
|
||||||
|
"v4": "4",
|
||||||
|
"v5": "4",
|
||||||
|
"level5": {
|
||||||
|
"v5": "5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"level1": {
|
||||||
|
"v1": "1",
|
||||||
|
"v2": "1",
|
||||||
|
"v3": "1",
|
||||||
|
"v4": "1",
|
||||||
|
"v5": "1",
|
||||||
|
"level2": {
|
||||||
|
"v2": "2",
|
||||||
|
"v3": "2",
|
||||||
|
"v4": "2",
|
||||||
|
"v5": "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
js/test_cases/partial_jump_around_two/main.dust
Normal file
3
js/test_cases/partial_jump_around_two/main.dust
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{#level1.level2}
|
||||||
|
{>partialone v1="a" v2="a" v3="a" v4="a" v5="a"/}
|
||||||
|
{/level1.level2}
|
3
js/test_cases/partial_jump_around_two/partialone.dust
Normal file
3
js/test_cases/partial_jump_around_two/partialone.dust
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{#level3.level4}
|
||||||
|
{>partialtwo v1="b" v2="b" v3="b" v4="b" v5="b" /}
|
||||||
|
{/level3.level4}
|
7
js/test_cases/partial_jump_around_two/partialtwo.dust
Normal file
7
js/test_cases/partial_jump_around_two/partialtwo.dust
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{#level5}
|
||||||
|
{v1}{~n}
|
||||||
|
{v2}{~n}
|
||||||
|
{v3}{~n}
|
||||||
|
{v4}{~n}
|
||||||
|
{v5}{~n}
|
||||||
|
{/level5}
|
1
js/test_cases/partial_path/greeting.dust
Normal file
1
js/test_cases/partial_path/greeting.dust
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello {name}{?item}, nice {item}{/item}!{~n}
|
14
js/test_cases/partial_path/input1.json
Normal file
14
js/test_cases/partial_path/input1.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"people": [
|
||||||
|
{
|
||||||
|
"name": "Alice",
|
||||||
|
"item": "cat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Bob"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"globals": {
|
||||||
|
"item": "couch"
|
||||||
|
}
|
||||||
|
}
|
3
js/test_cases/partial_path/main.dust
Normal file
3
js/test_cases/partial_path/main.dust
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{#people}
|
||||||
|
{>greeting item=globals.item/}
|
||||||
|
{/people}
|
1
js/test_cases/partial_simple/README.md
Normal file
1
js/test_cases/partial_simple/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
Partial parameters do not take highest priority but also do not seem to take lowest priority. Current theory is parameters are inserted 1 level above the current context (so parameters would be references before walking up).
|
1
js/test_cases/partial_simple/greeting.dust
Normal file
1
js/test_cases/partial_simple/greeting.dust
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello {name}{?item}, nice {item}{/item}!{~n}
|
4
js/test_cases/partial_simple/input1.json
Normal file
4
js/test_cases/partial_simple/input1.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{"people": [
|
||||||
|
{"name": "Alice", "item": "cat"},
|
||||||
|
{"name": "Bob"}
|
||||||
|
]}
|
5
js/test_cases/partial_simple/input2.json
Normal file
5
js/test_cases/partial_simple/input2.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{"people": [
|
||||||
|
{"name": "Alice"},
|
||||||
|
{"name": "Bob"}
|
||||||
|
],
|
||||||
|
"item": "cat"}
|
3
js/test_cases/partial_simple/main.dust
Normal file
3
js/test_cases/partial_simple/main.dust
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{#people}
|
||||||
|
{>greeting item="shoes"/}
|
||||||
|
{/people}
|
58
src/bin.rs
58
src/bin.rs
@ -8,6 +8,7 @@ use renderer::DustRenderer;
|
|||||||
use renderer::Loopable;
|
use renderer::Loopable;
|
||||||
use renderer::RenderError;
|
use renderer::RenderError;
|
||||||
use renderer::Renderable;
|
use renderer::Renderable;
|
||||||
|
use renderer::WalkError;
|
||||||
use renderer::Walkable;
|
use renderer::Walkable;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@ -91,67 +92,48 @@ impl Renderable for serde_json::Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Walkable for serde_json::Value {
|
impl Walkable for serde_json::Value {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> {
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
||||||
match self {
|
match self {
|
||||||
serde_json::Value::Null => Err(RenderError::CantWalk {
|
serde_json::Value::Null => Err(WalkError::CantWalk),
|
||||||
segment: segment.to_string(),
|
serde_json::Value::Bool(_boolean) => Err(WalkError::CantWalk),
|
||||||
elem: self,
|
serde_json::Value::Number(_num) => Err(WalkError::CantWalk),
|
||||||
}),
|
serde_json::Value::String(_string) => Err(WalkError::CantWalk),
|
||||||
serde_json::Value::Bool(_boolean) => Err(RenderError::CantWalk {
|
serde_json::Value::Array(_arr) => Err(WalkError::CantWalk),
|
||||||
segment: segment.to_string(),
|
serde_json::Value::Object(obj) => obj
|
||||||
elem: self,
|
.get(segment)
|
||||||
}),
|
|
||||||
serde_json::Value::Number(_num) => Err(RenderError::CantWalk {
|
|
||||||
segment: segment.to_string(),
|
|
||||||
elem: self,
|
|
||||||
}),
|
|
||||||
serde_json::Value::String(_string) => Err(RenderError::CantWalk {
|
|
||||||
segment: segment.to_string(),
|
|
||||||
elem: self,
|
|
||||||
}),
|
|
||||||
serde_json::Value::Array(_arr) => Err(RenderError::CantWalk {
|
|
||||||
segment: segment.to_string(),
|
|
||||||
elem: self,
|
|
||||||
}),
|
|
||||||
serde_json::Value::Object(obj) => {
|
|
||||||
obj.get(segment)
|
|
||||||
.map(|val| val as _)
|
.map(|val| val as _)
|
||||||
.ok_or(RenderError::WontWalk {
|
.ok_or(WalkError::CantWalk),
|
||||||
segment: segment.to_string(),
|
|
||||||
elem: self,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loopable for serde_json::Value {
|
impl Loopable for serde_json::Value {
|
||||||
fn get_loop_elements(&self) -> Result<Vec<&dyn ContextElement>, RenderError> {
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
||||||
match self {
|
match self {
|
||||||
serde_json::Value::Null => Ok(Vec::new()),
|
serde_json::Value::Null => Vec::new(),
|
||||||
serde_json::Value::Bool(boolean) => {
|
serde_json::Value::Bool(boolean) => {
|
||||||
if *boolean {
|
if *boolean {
|
||||||
Ok(vec![self])
|
vec![self]
|
||||||
} else {
|
} else {
|
||||||
Ok(Vec::new())
|
Vec::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
serde_json::Value::Number(_num) => Ok(vec![self]),
|
serde_json::Value::Number(_num) => vec![self],
|
||||||
serde_json::Value::String(string_value) => {
|
serde_json::Value::String(string_value) => {
|
||||||
if string_value.is_empty() {
|
if string_value.is_empty() {
|
||||||
Ok(Vec::new())
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
Ok(vec![self])
|
vec![self]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
serde_json::Value::Array(array_value) => {
|
serde_json::Value::Array(array_value) => {
|
||||||
if array_value.is_empty() {
|
if array_value.is_empty() {
|
||||||
Ok(Vec::new())
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
Ok(array_value.iter().map(|x| x as _).collect())
|
array_value.iter().map(|x| x as _).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
serde_json::Value::Object(_obj) => Ok(vec![self]),
|
serde_json::Value::Object(_obj) => vec![self],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ pub use parser::template;
|
|||||||
pub use parser::Body;
|
pub use parser::Body;
|
||||||
pub use parser::DustTag;
|
pub use parser::DustTag;
|
||||||
pub use parser::Filter;
|
pub use parser::Filter;
|
||||||
|
pub use parser::KVPair;
|
||||||
|
pub use parser::RValue;
|
||||||
pub use parser::Special;
|
pub use parser::Special;
|
||||||
pub use parser::Template;
|
pub use parser::Template;
|
||||||
pub use parser::TemplateElement;
|
pub use parser::TemplateElement;
|
||||||
|
@ -6,7 +6,7 @@ use nom::bytes::complete::{tag, take_until, take_until_parser_matches};
|
|||||||
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::space1;
|
use nom::character::complete::{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;
|
||||||
@ -114,20 +114,20 @@ pub struct ParameterizedBlock<'a> {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Partial<'a> {
|
pub struct Partial<'a> {
|
||||||
name: String,
|
pub name: String,
|
||||||
params: Vec<KVPair<'a>>,
|
pub params: Vec<KVPair<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
enum RValue<'a> {
|
pub enum RValue<'a> {
|
||||||
RVPath(Path<'a>),
|
RVPath(Path<'a>),
|
||||||
RVString(String),
|
RVString(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
struct KVPair<'a> {
|
pub struct KVPair<'a> {
|
||||||
key: &'a str,
|
pub key: &'a str,
|
||||||
value: RValue<'a>,
|
pub value: RValue<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@ -387,7 +387,11 @@ where
|
|||||||
let (i, (name, params, inner, maybe_else, _closing_name)) = tuple((
|
let (i, (name, params, inner, maybe_else, _closing_name)) = tuple((
|
||||||
preceded(tag(open_matcher), tag(tag_name)),
|
preceded(tag(open_matcher), tag(tag_name)),
|
||||||
terminated(
|
terminated(
|
||||||
opt(preceded(space1, separated_list1(space1, key_value_pair))),
|
opt(delimited(
|
||||||
|
space1,
|
||||||
|
separated_list1(space1, key_value_pair),
|
||||||
|
space0,
|
||||||
|
)),
|
||||||
tag("}"),
|
tag("}"),
|
||||||
),
|
),
|
||||||
opt(body),
|
opt(body),
|
||||||
@ -420,7 +424,11 @@ where
|
|||||||
tag(open_matcher),
|
tag(open_matcher),
|
||||||
tuple((
|
tuple((
|
||||||
tag(tag_name),
|
tag(tag_name),
|
||||||
opt(preceded(space1, separated_list1(space1, key_value_pair))),
|
opt(delimited(
|
||||||
|
space1,
|
||||||
|
separated_list1(space1, key_value_pair),
|
||||||
|
space0,
|
||||||
|
)),
|
||||||
)),
|
)),
|
||||||
tag("/}"),
|
tag("/}"),
|
||||||
)(i)?;
|
)(i)?;
|
||||||
@ -449,7 +457,11 @@ where
|
|||||||
tag(open_matcher),
|
tag(open_matcher),
|
||||||
tuple((
|
tuple((
|
||||||
alt((map(key, String::from), quoted_string)),
|
alt((map(key, String::from), quoted_string)),
|
||||||
opt(preceded(space1, separated_list1(space1, key_value_pair))),
|
opt(delimited(
|
||||||
|
space1,
|
||||||
|
separated_list1(space1, key_value_pair),
|
||||||
|
space0,
|
||||||
|
)),
|
||||||
)),
|
)),
|
||||||
tag("/}"),
|
tag("/}"),
|
||||||
)(i)?;
|
)(i)?;
|
||||||
@ -1052,4 +1064,55 @@ mod tests {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_full_document_parameterized_partial() {
|
||||||
|
assert_eq!(
|
||||||
|
super::template(
|
||||||
|
r#"{#level3.level4}{>partialtwo v1="b" v2="b" v3="b" v4="b" v5="b" /}{/level3.level4}"#
|
||||||
|
),
|
||||||
|
Ok::<_, nom::Err<(&str, ErrorKind)>>((
|
||||||
|
"",
|
||||||
|
Template {
|
||||||
|
contents: Body {
|
||||||
|
elements: vec![TemplateElement::TETag(DustTag::DTSection(Container {
|
||||||
|
path: Path {
|
||||||
|
keys: vec!["level3", "level4"]
|
||||||
|
},
|
||||||
|
contents: Some(Body {
|
||||||
|
elements: vec![TemplateElement::TETag(DustTag::DTPartial(
|
||||||
|
Partial {
|
||||||
|
name: "partialtwo".to_owned(),
|
||||||
|
params: vec![
|
||||||
|
KVPair {
|
||||||
|
key: "v1",
|
||||||
|
value: RValue::RVString("b".to_owned())
|
||||||
|
},
|
||||||
|
KVPair {
|
||||||
|
key: "v2",
|
||||||
|
value: RValue::RVString("b".to_owned())
|
||||||
|
},
|
||||||
|
KVPair {
|
||||||
|
key: "v3",
|
||||||
|
value: RValue::RVString("b".to_owned())
|
||||||
|
},
|
||||||
|
KVPair {
|
||||||
|
key: "v4",
|
||||||
|
value: RValue::RVString("b".to_owned())
|
||||||
|
},
|
||||||
|
KVPair {
|
||||||
|
key: "v5",
|
||||||
|
value: RValue::RVString("b".to_owned())
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
))]
|
||||||
|
}),
|
||||||
|
else_contents: None
|
||||||
|
}))]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
use crate::parser::Filter;
|
use crate::parser::Filter;
|
||||||
use crate::renderer::errors::RenderError;
|
use crate::renderer::errors::RenderError;
|
||||||
|
use crate::renderer::errors::WalkError;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
pub trait ContextElement: Debug + Walkable + Renderable + Loopable {}
|
pub trait ContextElement: Debug + Walkable + Renderable + Loopable {}
|
||||||
|
|
||||||
pub trait Walkable {
|
pub trait Walkable {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError>;
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Renderable {
|
pub trait Renderable {
|
||||||
@ -22,5 +23,5 @@ pub trait Loopable {
|
|||||||
/// once with the context being the element at that path. Finally,
|
/// once with the context being the element at that path. Finally,
|
||||||
/// if its an array-like value then it will render n-times, once
|
/// if its an array-like value then it will render n-times, once
|
||||||
/// for each element of the array.
|
/// for each element of the array.
|
||||||
fn get_loop_elements(&self) -> Result<Vec<&dyn ContextElement>, RenderError>;
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement>;
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,16 @@
|
|||||||
use crate::renderer::context_element::ContextElement;
|
|
||||||
use std::error;
|
use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub enum RenderError<'a> {
|
/// Fatal errors while rendering.
|
||||||
|
///
|
||||||
|
/// A RenderError will halt rendering.
|
||||||
|
pub enum RenderError {
|
||||||
Generic(String),
|
Generic(String),
|
||||||
/// For when walking is absolutely impossible
|
TemplateNotFound(String),
|
||||||
CantWalk {
|
}
|
||||||
segment: String,
|
|
||||||
elem: &'a dyn ContextElement,
|
pub enum WalkError {
|
||||||
},
|
CantWalk,
|
||||||
/// For when walking fails (example, a missing key on a map)
|
|
||||||
WontWalk {
|
|
||||||
segment: String,
|
|
||||||
elem: &'a dyn ContextElement,
|
|
||||||
},
|
|
||||||
NotFound {
|
|
||||||
path: &'a Vec<&'a str>,
|
|
||||||
breadcrumbs: Vec<&'a dyn ContextElement>,
|
|
||||||
},
|
|
||||||
/// Attempting to render and unrenderable type (for example, an object without any filters)
|
|
||||||
CantRender {
|
|
||||||
elem: &'a dyn ContextElement,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -29,43 +18,51 @@ pub struct CompileError {
|
|||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for RenderError<'_> {
|
impl fmt::Display for RenderError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
RenderError::Generic(msg) => write!(f, "{}", msg),
|
RenderError::Generic(msg) => write!(f, "{}", msg),
|
||||||
RenderError::CantWalk { segment, elem } => {
|
RenderError::TemplateNotFound(name) => {
|
||||||
write!(f, "Tried to walk to {} from {:?}", segment, elem)
|
write!(f, "No template named {} in context", name)
|
||||||
}
|
|
||||||
RenderError::WontWalk { segment, elem } => {
|
|
||||||
write!(f, "Failed to walk to {} from {:?}", segment, elem)
|
|
||||||
}
|
|
||||||
RenderError::CantRender { elem } => write!(f, "Cant render {:?}", elem),
|
|
||||||
RenderError::NotFound { path, breadcrumbs } => {
|
|
||||||
write!(f, "Could not find {:?} in {:?}", path, breadcrumbs)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for RenderError<'_> {
|
impl fmt::Debug for RenderError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
RenderError::Generic(msg) => write!(f, "{}", msg),
|
RenderError::Generic(msg) => write!(f, "{}", msg),
|
||||||
RenderError::CantWalk { segment, elem } => {
|
RenderError::TemplateNotFound(name) => {
|
||||||
write!(f, "Tried to walk to {} from {:?}", segment, elem)
|
write!(f, "No template named {} in context", name)
|
||||||
}
|
|
||||||
RenderError::WontWalk { segment, elem } => {
|
|
||||||
write!(f, "Failed to walk to {} from {:?}", segment, elem)
|
|
||||||
}
|
|
||||||
RenderError::CantRender { elem } => write!(f, "Cant render {:?}", elem),
|
|
||||||
RenderError::NotFound { path, breadcrumbs } => {
|
|
||||||
write!(f, "Could not find {:?} in {:?}", path, breadcrumbs)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for RenderError<'_> {
|
impl error::Error for RenderError {
|
||||||
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for WalkError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
WalkError::CantWalk => write!(f, "Failed to walk"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for WalkError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
WalkError::CantWalk => write!(f, "Failed to walk"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl error::Error for WalkError {
|
||||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
mod context_element;
|
mod context_element;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
mod parameters_context;
|
||||||
mod renderer;
|
mod renderer;
|
||||||
|
mod walking;
|
||||||
|
|
||||||
pub use context_element::ContextElement;
|
pub use context_element::ContextElement;
|
||||||
pub use context_element::Loopable;
|
pub use context_element::Loopable;
|
||||||
@ -10,6 +12,7 @@ pub use context_element::Renderable;
|
|||||||
pub use context_element::Walkable;
|
pub use context_element::Walkable;
|
||||||
pub use errors::CompileError;
|
pub use errors::CompileError;
|
||||||
pub use errors::RenderError;
|
pub use errors::RenderError;
|
||||||
|
pub use errors::WalkError;
|
||||||
pub use renderer::compile_template;
|
pub use renderer::compile_template;
|
||||||
pub use renderer::CompiledTemplate;
|
pub use renderer::CompiledTemplate;
|
||||||
pub use renderer::DustRenderer;
|
pub use renderer::DustRenderer;
|
||||||
|
86
src/renderer/parameters_context.rs
Normal file
86
src/renderer/parameters_context.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
use crate::parser::KVPair;
|
||||||
|
use crate::parser::{Filter, RValue};
|
||||||
|
use crate::renderer::context_element::ContextElement;
|
||||||
|
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;
|
||||||
|
|
||||||
|
#[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<KVPair<'a>>,
|
||||||
|
) -> 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<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<'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<Filter>) -> Result<String, RenderError> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,9 @@ use crate::parser::TemplateElement;
|
|||||||
use crate::renderer::context_element::ContextElement;
|
use crate::renderer::context_element::ContextElement;
|
||||||
use crate::renderer::errors::CompileError;
|
use crate::renderer::errors::CompileError;
|
||||||
use crate::renderer::errors::RenderError;
|
use crate::renderer::errors::RenderError;
|
||||||
|
use crate::renderer::errors::WalkError;
|
||||||
|
use crate::renderer::parameters_context::ParametersContext;
|
||||||
|
use crate::renderer::walking::walk_path;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -49,14 +52,11 @@ impl<'a> DustRenderer<'a> {
|
|||||||
&'a self,
|
&'a self,
|
||||||
name: &str,
|
name: &str,
|
||||||
breadcrumbs: &Vec<&'a dyn ContextElement>,
|
breadcrumbs: &Vec<&'a dyn ContextElement>,
|
||||||
) -> Result<String, RenderError<'a>> {
|
) -> Result<String, RenderError> {
|
||||||
let main_template = match self.templates.get(name) {
|
let main_template = match self.templates.get(name) {
|
||||||
Some(tmpl) => tmpl,
|
Some(tmpl) => tmpl,
|
||||||
None => {
|
None => {
|
||||||
return Err(RenderError::Generic(format!(
|
return Err(RenderError::TemplateNotFound(name.to_owned()));
|
||||||
"No template named {} in context",
|
|
||||||
name
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.render_body(&main_template.contents, breadcrumbs)
|
self.render_body(&main_template.contents, breadcrumbs)
|
||||||
@ -66,7 +66,7 @@ impl<'a> DustRenderer<'a> {
|
|||||||
&'a self,
|
&'a self,
|
||||||
body: &'a Body,
|
body: &'a Body,
|
||||||
breadcrumbs: &Vec<&'a dyn ContextElement>,
|
breadcrumbs: &Vec<&'a dyn ContextElement>,
|
||||||
) -> Result<String, RenderError<'a>> {
|
) -> Result<String, RenderError> {
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
for elem in &body.elements {
|
for elem in &body.elements {
|
||||||
match elem {
|
match elem {
|
||||||
@ -84,7 +84,7 @@ impl<'a> DustRenderer<'a> {
|
|||||||
&'a self,
|
&'a self,
|
||||||
tag: &'a DustTag,
|
tag: &'a DustTag,
|
||||||
breadcrumbs: &Vec<&'a dyn ContextElement>,
|
breadcrumbs: &Vec<&'a dyn ContextElement>,
|
||||||
) -> Result<String, RenderError<'a>> {
|
) -> Result<String, RenderError> {
|
||||||
match tag {
|
match tag {
|
||||||
DustTag::DTComment(_comment) => (),
|
DustTag::DTComment(_comment) => (),
|
||||||
DustTag::DTSpecial(special) => {
|
DustTag::DTSpecial(special) => {
|
||||||
@ -100,21 +100,20 @@ impl<'a> DustRenderer<'a> {
|
|||||||
DustTag::DTReference(reference) => {
|
DustTag::DTReference(reference) => {
|
||||||
let val = walk_path(breadcrumbs, &reference.path.keys);
|
let val = walk_path(breadcrumbs, &reference.path.keys);
|
||||||
match val {
|
match val {
|
||||||
Err(RenderError::NotFound { .. }) => return Ok("".to_owned()),
|
Err(WalkError::CantWalk) => return Ok("".to_owned()),
|
||||||
Ok(final_val) => {
|
Ok(final_val) => {
|
||||||
let loop_elements = final_val.get_loop_elements()?;
|
let loop_elements = final_val.get_loop_elements();
|
||||||
if loop_elements.is_empty() {
|
if loop_elements.is_empty() {
|
||||||
return Ok("".to_owned());
|
return Ok("".to_owned());
|
||||||
} else {
|
} else {
|
||||||
return final_val.render(&reference.filters);
|
return final_val.render(&reference.filters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(render_error) => return Err(render_error),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DustTag::DTSection(container) => {
|
DustTag::DTSection(container) => {
|
||||||
let val = walk_path(breadcrumbs, &container.path.keys);
|
let val = walk_path(breadcrumbs, &container.path.keys);
|
||||||
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val)?;
|
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val);
|
||||||
if loop_elements.is_empty() {
|
if loop_elements.is_empty() {
|
||||||
// Oddly enough if the value is falsey (like
|
// Oddly enough if the value is falsey (like
|
||||||
// an empty array or null), Dust uses the
|
// an empty array or null), Dust uses the
|
||||||
@ -144,7 +143,7 @@ impl<'a> DustRenderer<'a> {
|
|||||||
}
|
}
|
||||||
DustTag::DTExists(container) => {
|
DustTag::DTExists(container) => {
|
||||||
let val = walk_path(breadcrumbs, &container.path.keys);
|
let val = walk_path(breadcrumbs, &container.path.keys);
|
||||||
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val)?;
|
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val);
|
||||||
if loop_elements.is_empty() {
|
if loop_elements.is_empty() {
|
||||||
return match &container.else_contents {
|
return match &container.else_contents {
|
||||||
Some(body) => self.render_body(&body, breadcrumbs),
|
Some(body) => self.render_body(&body, breadcrumbs),
|
||||||
@ -159,7 +158,7 @@ impl<'a> DustRenderer<'a> {
|
|||||||
}
|
}
|
||||||
DustTag::DTNotExists(container) => {
|
DustTag::DTNotExists(container) => {
|
||||||
let val = walk_path(breadcrumbs, &container.path.keys);
|
let val = walk_path(breadcrumbs, &container.path.keys);
|
||||||
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val)?;
|
let loop_elements: Vec<&dyn ContextElement> = self.get_loop_elements(val);
|
||||||
if !loop_elements.is_empty() {
|
if !loop_elements.is_empty() {
|
||||||
return match &container.else_contents {
|
return match &container.else_contents {
|
||||||
Some(body) => self.render_body(&body, breadcrumbs),
|
Some(body) => self.render_body(&body, breadcrumbs),
|
||||||
@ -172,6 +171,18 @@ impl<'a> DustRenderer<'a> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DustTag::DTPartial(partial) => {
|
||||||
|
if partial.params.is_empty() {
|
||||||
|
let rendered_content = self.render(&partial.name, breadcrumbs)?;
|
||||||
|
return Ok(rendered_content);
|
||||||
|
} else {
|
||||||
|
let injected_context = ParametersContext::new(breadcrumbs, &partial.params);
|
||||||
|
let mut new_breadcrumbs = breadcrumbs.clone();
|
||||||
|
new_breadcrumbs.insert(new_breadcrumbs.len() - 1, &injected_context);
|
||||||
|
let rendered_content = self.render(&partial.name, &new_breadcrumbs)?;
|
||||||
|
return Ok(rendered_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => (), // TODO: Implement the rest
|
_ => (), // TODO: Implement the rest
|
||||||
}
|
}
|
||||||
Ok("".to_owned())
|
Ok("".to_owned())
|
||||||
@ -183,72 +194,15 @@ impl<'a> DustRenderer<'a> {
|
|||||||
/// block, this will return an empty vector.
|
/// block, this will return an empty vector.
|
||||||
fn get_loop_elements<'b>(
|
fn get_loop_elements<'b>(
|
||||||
&'a self,
|
&'a self,
|
||||||
walk_result: Result<&'b dyn ContextElement, RenderError<'b>>,
|
walk_result: Result<&'b dyn ContextElement, WalkError>,
|
||||||
) -> Result<Vec<&'b dyn ContextElement>, RenderError<'b>> {
|
) -> Vec<&'b dyn ContextElement> {
|
||||||
if let Err(RenderError::NotFound { .. }) = walk_result {
|
match walk_result {
|
||||||
// If reference does not exist in the context, render the else block
|
Err(WalkError::CantWalk) => Vec::new(),
|
||||||
Ok(vec![])
|
Ok(walk_target) => walk_target.get_loop_elements(),
|
||||||
} else {
|
|
||||||
Ok(walk_result?.get_loop_elements()?)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum WalkResult<'a> {
|
|
||||||
NoWalk,
|
|
||||||
PartialWalk,
|
|
||||||
FullyWalked(&'a dyn ContextElement),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn walk_path_from_single_level<'a>(
|
|
||||||
context: &'a dyn ContextElement,
|
|
||||||
path: &Vec<&str>,
|
|
||||||
) -> Result<WalkResult<'a>, RenderError<'a>> {
|
|
||||||
if path.is_empty() {
|
|
||||||
return Ok(WalkResult::FullyWalked(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut walk_failure = WalkResult::NoWalk;
|
|
||||||
let mut output = context;
|
|
||||||
for elem in path.iter() {
|
|
||||||
let new_val = output.walk(elem);
|
|
||||||
if let Err(RenderError::WontWalk { .. }) = new_val {
|
|
||||||
return Ok(walk_failure);
|
|
||||||
} else if let Err(RenderError::CantWalk { .. }) = new_val {
|
|
||||||
return Ok(walk_failure);
|
|
||||||
}
|
|
||||||
walk_failure = WalkResult::PartialWalk;
|
|
||||||
output = new_val?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(WalkResult::FullyWalked(output))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn walk_path<'a>(
|
|
||||||
breadcrumbs: &Vec<&'a dyn ContextElement>,
|
|
||||||
path: &'a Vec<&str>,
|
|
||||||
) -> Result<&'a dyn ContextElement, RenderError<'a>> {
|
|
||||||
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(RenderError::NotFound {
|
|
||||||
path: path,
|
|
||||||
breadcrumbs: breadcrumbs.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
WalkResult::FullyWalked(new_context) => return Ok(new_context),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(RenderError::NotFound {
|
|
||||||
path: path,
|
|
||||||
breadcrumbs: breadcrumbs.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -278,57 +232,48 @@ mod tests {
|
|||||||
impl<I: ContextElement> Renderable for HashMap<&str, I> {
|
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
|
||||||
Err(RenderError::CantRender { elem: self })
|
Ok("[object Object]".to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: ContextElement> Walkable for HashMap<&str, I> {
|
impl<I: ContextElement> Walkable for HashMap<&str, I> {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> {
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
||||||
let child = self.get(segment).ok_or(RenderError::WontWalk {
|
let child = self.get(segment).ok_or(WalkError::CantWalk)?;
|
||||||
segment: segment.to_string(),
|
|
||||||
elem: self,
|
|
||||||
})?;
|
|
||||||
Ok(child)
|
Ok(child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Walkable for &str {
|
impl Walkable for &str {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> {
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
||||||
Err(RenderError::CantWalk {
|
Err(WalkError::CantWalk)
|
||||||
segment: segment.to_string(),
|
|
||||||
elem: self,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Walkable for u32 {
|
impl Walkable for u32 {
|
||||||
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, RenderError> {
|
fn walk(&self, segment: &str) -> Result<&dyn ContextElement, WalkError> {
|
||||||
Err(RenderError::CantWalk {
|
Err(WalkError::CantWalk)
|
||||||
segment: segment.to_string(),
|
|
||||||
elem: self,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loopable for &str {
|
impl Loopable for &str {
|
||||||
fn get_loop_elements(&self) -> Result<Vec<&dyn ContextElement>, RenderError> {
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
Ok(Vec::new())
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
Ok(vec![self])
|
vec![self]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loopable for u32 {
|
impl Loopable for u32 {
|
||||||
fn get_loop_elements(&self) -> Result<Vec<&dyn ContextElement>, RenderError> {
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
||||||
Ok(vec![self])
|
vec![self]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: ContextElement> Loopable for HashMap<&str, I> {
|
impl<I: ContextElement> Loopable for HashMap<&str, I> {
|
||||||
fn get_loop_elements(&self) -> Result<Vec<&dyn ContextElement>, RenderError> {
|
fn get_loop_elements(&self) -> Vec<&dyn ContextElement> {
|
||||||
Ok(vec![self])
|
vec![self]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
53
src/renderer/walking.rs
Normal file
53
src/renderer/walking.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use crate::renderer::context_element::ContextElement;
|
||||||
|
use crate::renderer::WalkError;
|
||||||
|
|
||||||
|
enum WalkResult<'a> {
|
||||||
|
NoWalk,
|
||||||
|
PartialWalk,
|
||||||
|
FullyWalked(&'a dyn ContextElement),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn walk_path_from_single_level<'a>(
|
||||||
|
context: &'a dyn ContextElement,
|
||||||
|
path: &Vec<&str>,
|
||||||
|
) -> WalkResult<'a> {
|
||||||
|
if path.is_empty() {
|
||||||
|
return WalkResult::FullyWalked(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut walk_failure = WalkResult::NoWalk;
|
||||||
|
let mut output = context;
|
||||||
|
for elem in path.iter() {
|
||||||
|
let new_val = output.walk(elem);
|
||||||
|
match output.walk(elem) {
|
||||||
|
Err(WalkError::CantWalk { .. }) => {
|
||||||
|
return walk_failure;
|
||||||
|
}
|
||||||
|
Ok(new_val) => {
|
||||||
|
walk_failure = WalkResult::PartialWalk;
|
||||||
|
output = new_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WalkResult::FullyWalked(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn walk_path<'a>(
|
||||||
|
breadcrumbs: &Vec<&'a dyn ContextElement>,
|
||||||
|
path: &'a Vec<&str>,
|
||||||
|
) -> Result<&'a dyn ContextElement, WalkError> {
|
||||||
|
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)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user