Merge branch 'into_context_element_breadcrumb_tree' into into_context_element
This commit is contained in:
		
						commit
						9b69ba7b09
					
				| @ -21,3 +21,9 @@ | ||||
| {@eq key=array_of_some_obj value=array_of_some_obj}array_of_some_obj is equal to array_of_some_obj{:else}array_of_some_obj does not equal array_of_some_obj{/eq}{~n} | ||||
| {@eq key=array_of_some_obj value=copy_array_of_some_obj}array_of_some_obj is equal to copy_array_of_some_obj{:else}array_of_some_obj does not equal copy_array_of_some_obj{/eq}{~n} | ||||
| {@eq key=array_of_some_obj value=array_of_other_obj}array_of_some_obj is equal to array_of_other_obj{:else}array_of_some_obj does not equal array_of_other_obj{/eq}{~n} | ||||
| 
 | ||||
| Do objects with different paths referencing the same variable match?{~n} | ||||
| ===================================================================={~n} | ||||
| {#int renamed=some_obj} | ||||
|     {@eq key=some_obj value=renamed}some_obj equals renamed{:else}some_obj does not equal renamed{/eq}{~n} | ||||
| {/int} | ||||
|  | ||||
| @ -19,5 +19,14 @@ | ||||
|         "bar" | ||||
|       ] | ||||
|     } | ||||
|   ] | ||||
|   ], | ||||
|   "some_object": { | ||||
|     "foo": "bar" | ||||
|   }, | ||||
|   "some_same_object": { | ||||
|     "foo": "bar" | ||||
|   }, | ||||
|   "some_different_object": { | ||||
|     "foo": "baz" | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -53,3 +53,12 @@ Reference Parameters{~n} | ||||
| {#truthy name="chris" pet="{petname}" petname="{deeperpetname}" deeperpetname="fluffy"} | ||||
|     Hello {name}, nice {pet}{~n} | ||||
| {/truthy} | ||||
| 
 | ||||
| Equality{~n} | ||||
| ========{~n} | ||||
| {@eq key=some_object value=some_object}some_object equals some_object{:else}some_object does not equal some_object{/eq}{~n} | ||||
| {@eq key=some_object value=some_same_object}some_object equals some_same_object{:else}some_object does not equal some_same_object{/eq}{~n} | ||||
| {@eq key=some_object value="{some_object}"}some_object equals reference(some_object){:else}some_object does not equal reference(some_object){/eq}{~n} | ||||
| {@eq key="{some_object}" value="{some_object}"}reference(some_object) equals reference(some_object){:else}reference(some_object) does not equal reference(some_object){/eq}{~n} | ||||
| {@eq key="{some_object}" value="{some_same_object}"}reference(some_object) equals reference(some_same_object){:else}reference(some_object) does not equal reference(some_same_object){/eq}{~n} | ||||
| {@eq key="{some_object}" value="{some_different_object}"}reference(some_object) equals reference(some_different_object){:else}reference(some_object) does not equal reference(some_different_object){/eq}{~n} | ||||
|  | ||||
							
								
								
									
										151
									
								
								src/bin.rs
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								src/bin.rs
									
									
									
									
									
								
							| @ -3,8 +3,9 @@ extern crate nom; | ||||
| use crate::renderer::CompareContextElement; | ||||
| use parser::Filter; | ||||
| use parser::OwnedLiteral; | ||||
| use parser::Template; | ||||
| use renderer::compile_template; | ||||
| use renderer::CompiledTemplate; | ||||
| use renderer::CompileError; | ||||
| use renderer::ContextElement; | ||||
| use renderer::DustRenderer; | ||||
| use renderer::IntoContextElement; | ||||
| @ -38,32 +39,40 @@ fn main() { | ||||
|             (p.to_string(), template_content) | ||||
|         }) | ||||
|         .collect(); | ||||
|     let compiled_templates: Vec<CompiledTemplate> = template_contents | ||||
|         .iter() | ||||
|         .map(|(p, contents)| template_from_file(p, contents)) | ||||
|         .collect(); | ||||
|     let mut dust_renderer = DustRenderer::new(); | ||||
|     compiled_templates.iter().for_each(|template| { | ||||
|         dust_renderer.load_source(template); | ||||
|     }); | ||||
|     let compiled_templates_result: Result<Vec<(String, Template)>, CompileError> = | ||||
|         template_contents | ||||
|             .iter() | ||||
|             .map(|(p, contents)| template_from_file(p, contents)) | ||||
|             .collect(); | ||||
|     let compiled_templates = compiled_templates_result.unwrap(); | ||||
|     let main_template_name = &compiled_templates | ||||
|         .first() | ||||
|         .expect("There should be more than 1 template") | ||||
|         .name; | ||||
|     let breadcrumbs = vec![&context as &dyn IntoContextElement]; | ||||
|         .0; | ||||
|     let mut dust_renderer = DustRenderer::new(); | ||||
|     compiled_templates.iter().for_each(|(name, template)| { | ||||
|         dust_renderer.load_source(template, name.to_owned()); | ||||
|     }); | ||||
|     println!( | ||||
|         "{}", | ||||
|         dust_renderer | ||||
|             .render(main_template_name, &breadcrumbs) | ||||
|             .render(main_template_name, Some(&context)) | ||||
|             .expect("Failed to render") | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| fn template_from_file<'a>(file_path: &str, file_contents: &'a str) -> CompiledTemplate<'a> { | ||||
| fn template_from_file<'a>( | ||||
|     file_path: &str, | ||||
|     file_contents: &'a str, | ||||
| ) -> Result<(String, Template<'a>), CompileError> { | ||||
|     let path: &Path = Path::new(file_path); | ||||
|     let name = path.file_stem().unwrap(); | ||||
|     compile_template(file_contents, name.to_string_lossy().to_string()) | ||||
|         .expect("Failed to compile template") | ||||
|     let name = path.file_stem().ok_or(CompileError { | ||||
|         message: format!("Failed to get file stem on {}", file_path), | ||||
|     })?; | ||||
|     Ok(( | ||||
|         name.to_string_lossy().to_string(), | ||||
|         compile_template(file_contents)?, | ||||
|     )) | ||||
| } | ||||
| 
 | ||||
| fn read_context_from_stdin() -> serde_json::Value { | ||||
| @ -166,61 +175,61 @@ fn apply_filter( | ||||
|     filter: &Filter, | ||||
| ) -> Result<serde_json::Value, RenderError> { | ||||
|     match (json_value, filter) { | ||||
|         // Html escape filter
 | ||||
|         (serde_json::Value::String(string), Filter::HtmlEncode) => { | ||||
|             Ok(serde_json::Value::String(html_escape(string))) | ||||
|         } | ||||
|         (_, Filter::HtmlEncode) => Ok(serde_json::Value::String(html_escape( | ||||
|             &json_value.render(&Vec::new())?, | ||||
|         ))), | ||||
|         // Disable html escape filter
 | ||||
|         (_, Filter::DisableHtmlEncode) => panic!("The |s filter is automatically removed by the renderer since it is a no-op during rendering."), | ||||
|         // Parse JSON filter
 | ||||
|         (serde_json::Value::String(string), Filter::JsonParse) => { | ||||
|             serde_json::from_str(&string).or(Err(RenderError::InvalidJson(string.to_owned()))) | ||||
|         } | ||||
|         (_, Filter::JsonParse) => { | ||||
|             let rendered_value = json_value.render(&Vec::new())?; | ||||
|             serde_json::from_str(&rendered_value).or(Err(RenderError::InvalidJson(rendered_value))) | ||||
|         } | ||||
|         // Json Stringify filter
 | ||||
|         (_, Filter::JsonStringify) => { | ||||
|             Ok(serde_json::Value::String(json_value.to_string())) | ||||
|         } | ||||
|         // Javascript escape filter
 | ||||
|         (serde_json::Value::String(string), Filter::JavascriptStringEncode) => { | ||||
|             Ok(serde_json::Value::String(javascript_escape(string))) | ||||
|         } | ||||
|         (serde_json::Value::Bool(boolean), Filter::JavascriptStringEncode) => { | ||||
|             Ok(serde_json::Value::Bool(*boolean)) | ||||
|         } | ||||
|         (serde_json::Value::Number(number), Filter::JavascriptStringEncode) => { | ||||
|             Ok(serde_json::Value::Number(number.clone())) | ||||
|         } | ||||
|         (serde_json::Value::Array(arr), Filter::JavascriptStringEncode) => { | ||||
|             Ok(serde_json::Value::Array(arr.clone())) | ||||
|         } | ||||
|         (serde_json::Value::Object(obj), Filter::JavascriptStringEncode) => { | ||||
|             Ok(serde_json::Value::Object(obj.clone())) | ||||
|         } | ||||
|         (_, Filter::JavascriptStringEncode) => Ok(serde_json::Value::String(javascript_escape( | ||||
|             &json_value.render(&Vec::new())?, | ||||
|         ))), | ||||
|         // EncodeURI filter
 | ||||
|         (serde_json::Value::String(string), Filter::EncodeUri) => { | ||||
|             Ok(serde_json::Value::String(encode_uri(string))) | ||||
|         } | ||||
|         (_, Filter::EncodeUri) => Ok(serde_json::Value::String(encode_uri( | ||||
|             &json_value.render(&Vec::new())?, | ||||
|         ))), | ||||
|         // EncodeURIComponent filter
 | ||||
|         (serde_json::Value::String(string), Filter::EncodeUriComponent) => { | ||||
|             Ok(serde_json::Value::String(encode_uri_component(string))) | ||||
|         } | ||||
|         (_, Filter::EncodeUriComponent) => Ok(serde_json::Value::String(encode_uri_component( | ||||
|             &json_value.render(&Vec::new())?, | ||||
|         ))), | ||||
|     } | ||||
|          // Html escape filter
 | ||||
|          (serde_json::Value::String(string), Filter::HtmlEncode) => { | ||||
|              Ok(serde_json::Value::String(html_escape(string))) | ||||
|          } | ||||
|          (_, Filter::HtmlEncode) => Ok(serde_json::Value::String(html_escape( | ||||
|              &json_value.render(&Vec::new())?, | ||||
|          ))), | ||||
|          // Disable html escape filter
 | ||||
|          (_, Filter::DisableHtmlEncode) => panic!("The |s filter is automatically removed by the renderer since it is a no-op during rendering."), | ||||
|          // Parse JSON filter
 | ||||
|          (serde_json::Value::String(string), Filter::JsonParse) => { | ||||
|              serde_json::from_str(&string).or(Err(RenderError::InvalidJson(string.to_owned()))) | ||||
|          } | ||||
|          (_, Filter::JsonParse) => { | ||||
|              let rendered_value = json_value.render(&Vec::new())?; | ||||
|              serde_json::from_str(&rendered_value).or(Err(RenderError::InvalidJson(rendered_value))) | ||||
|          } | ||||
|          // Json Stringify filter
 | ||||
|          (_, Filter::JsonStringify) => { | ||||
|              Ok(serde_json::Value::String(json_value.to_string())) | ||||
|          } | ||||
|          // Javascript escape filter
 | ||||
|          (serde_json::Value::String(string), Filter::JavascriptStringEncode) => { | ||||
|              Ok(serde_json::Value::String(javascript_escape(string))) | ||||
|          } | ||||
|          (serde_json::Value::Bool(boolean), Filter::JavascriptStringEncode) => { | ||||
|              Ok(serde_json::Value::Bool(*boolean)) | ||||
|          } | ||||
|          (serde_json::Value::Number(number), Filter::JavascriptStringEncode) => { | ||||
|              Ok(serde_json::Value::Number(number.clone())) | ||||
|          } | ||||
|          (serde_json::Value::Array(arr), Filter::JavascriptStringEncode) => { | ||||
|              Ok(serde_json::Value::Array(arr.clone())) | ||||
|          } | ||||
|          (serde_json::Value::Object(obj), Filter::JavascriptStringEncode) => { | ||||
|              Ok(serde_json::Value::Object(obj.clone())) | ||||
|          } | ||||
|          (_, Filter::JavascriptStringEncode) => Ok(serde_json::Value::String(javascript_escape( | ||||
|              &json_value.render(&Vec::new())?, | ||||
|          ))), | ||||
|          // EncodeURI filter
 | ||||
|          (serde_json::Value::String(string), Filter::EncodeUri) => { | ||||
|              Ok(serde_json::Value::String(encode_uri(string))) | ||||
|          } | ||||
|          (_, Filter::EncodeUri) => Ok(serde_json::Value::String(encode_uri( | ||||
|              &json_value.render(&Vec::new())?, | ||||
|          ))), | ||||
|          // EncodeURIComponent filter
 | ||||
|          (serde_json::Value::String(string), Filter::EncodeUriComponent) => { | ||||
|              Ok(serde_json::Value::String(encode_uri_component(string))) | ||||
|          } | ||||
|          (_, Filter::EncodeUriComponent) => Ok(serde_json::Value::String(encode_uri_component( | ||||
|              &json_value.render(&Vec::new())?, | ||||
|          ))), | ||||
|      } | ||||
| } | ||||
| 
 | ||||
| fn apply_filters( | ||||
|  | ||||
| @ -23,7 +23,7 @@ use nom::sequence::terminated; | ||||
| use nom::sequence::tuple; | ||||
| use nom::IResult; | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum DustTag<'a> { | ||||
|     DTSpecial(Special), | ||||
|     DTComment(Comment<'a>), | ||||
| @ -52,12 +52,12 @@ pub enum Special { | ||||
|     RightCurlyBrace, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum IgnoredWhitespace<'a> { | ||||
|     StartOfLine(&'a str), | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct Comment<'a> { | ||||
|     value: &'a str, | ||||
| } | ||||
| @ -65,12 +65,12 @@ pub struct Comment<'a> { | ||||
| /// A series of keys separated by '.' to reference a variable in the context
 | ||||
| ///
 | ||||
| /// Special case: If the path is just "." then keys will be an empty vec
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct Path<'a> { | ||||
|     pub keys: Vec<&'a str>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct Reference<'a> { | ||||
|     pub path: Path<'a>, | ||||
|     pub filters: Vec<Filter>, | ||||
| @ -87,12 +87,12 @@ pub enum Filter { | ||||
|     JsonParse, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct Span<'a> { | ||||
|     pub contents: &'a str, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct ParameterizedBlock<'a> { | ||||
|     pub path: Path<'a>, | ||||
|     pub explicit_context: Option<Path<'a>>, | ||||
| @ -101,33 +101,33 @@ pub struct ParameterizedBlock<'a> { | ||||
|     pub else_contents: Option<Body<'a>>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct Partial<'a> { | ||||
|     pub name: Vec<PartialNameElement>, | ||||
|     pub explicit_context: Option<Path<'a>>, | ||||
|     pub params: Vec<KVPair<'a>>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum OwnedLiteral { | ||||
|     LString(String), | ||||
|     LPositiveInteger(u64), | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum RValue<'a> { | ||||
|     RVPath(Path<'a>), | ||||
|     RVTemplate(Vec<PartialNameElement>), | ||||
|     RVLiteral(OwnedLiteral), | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct KVPair<'a> { | ||||
|     pub key: &'a str, | ||||
|     pub value: RValue<'a>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum PartialNameElement { | ||||
|     PNSpan { | ||||
|         contents: String, | ||||
| @ -138,17 +138,17 @@ pub enum PartialNameElement { | ||||
|     }, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct Body<'a> { | ||||
|     pub elements: Vec<TemplateElement<'a>>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub struct Template<'a> { | ||||
|     pub contents: Body<'a>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Debug, PartialEq)] | ||||
| pub enum TemplateElement<'a> { | ||||
|     TESpan(Span<'a>), | ||||
|     TETag(DustTag<'a>), | ||||
|  | ||||
							
								
								
									
										59
									
								
								src/renderer/breadcrumb_tree.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/renderer/breadcrumb_tree.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| use crate::renderer::context_element::ContextElement; | ||||
| use crate::renderer::context_element::IceResult; | ||||
| use crate::renderer::context_element::IntoContextElement; | ||||
| use crate::renderer::context_element::IntoRcIce; | ||||
| use std::borrow::Borrow; | ||||
| use std::rc::Rc; | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub enum BreadcrumbTreeElement<'a> { | ||||
|     // Using Rc so that when we need to create BreadcrumbTrees with
 | ||||
|     // the same BreadcrumbTreeElement but a different parent (for
 | ||||
|     // example, when inserting behind the tail), we don't need to the
 | ||||
|     // copy the already owned/malloc'd data.
 | ||||
|     Owned(Rc<dyn IntoContextElement + 'a>), | ||||
|     Borrowed(&'a dyn IntoContextElement), | ||||
| } | ||||
| 
 | ||||
| impl<'a> BreadcrumbTreeElement<'a> { | ||||
|     pub fn from_owned<I: 'a + IntoContextElement>(val: I) -> BreadcrumbTreeElement<'a> { | ||||
|         BreadcrumbTreeElement::Owned(Rc::new(val)) | ||||
|     } | ||||
| 
 | ||||
|     pub fn from_borrowed(val: &'a dyn IntoContextElement) -> BreadcrumbTreeElement<'a> { | ||||
|         BreadcrumbTreeElement::Borrowed(val) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> From<&'a IceResult<'a>> for BreadcrumbTreeElement<'a> { | ||||
|     fn from(inp: &'a IceResult<'a>) -> Self { | ||||
|         match inp { | ||||
|             IceResult::Owned(rc_ce) => { | ||||
|                 BreadcrumbTreeElement::from_borrowed(rc_ce.from_context_element()) | ||||
|             } | ||||
|             IceResult::Borrowed(ce) => { | ||||
|                 BreadcrumbTreeElement::from_borrowed(ce.from_context_element()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> From<IceResult<'a>> for BreadcrumbTreeElement<'a> { | ||||
|     fn from(inp: IceResult<'a>) -> Self { | ||||
|         match inp { | ||||
|             IceResult::Owned(rc_ce) => BreadcrumbTreeElement::Owned(rc_ce.into_rc_ice()), | ||||
|             IceResult::Borrowed(ce) => { | ||||
|                 BreadcrumbTreeElement::from_borrowed(ce.from_context_element()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> Borrow<dyn IntoContextElement + 'a> for BreadcrumbTreeElement<'a> { | ||||
|     fn borrow(&self) -> &(dyn IntoContextElement + 'a) { | ||||
|         match self { | ||||
|             BreadcrumbTreeElement::Owned(ice) => ice.as_ref(), | ||||
|             BreadcrumbTreeElement::Borrowed(ice) => *ice, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,8 +1,10 @@ | ||||
| use crate::parser::Filter; | ||||
| use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; | ||||
| use crate::renderer::errors::RenderError; | ||||
| use crate::renderer::errors::WalkError; | ||||
| use crate::renderer::DustRenderer; | ||||
| use std::any::Any; | ||||
| use std::rc::Rc; | ||||
| use std::{cmp::Ordering, fmt::Debug}; | ||||
| 
 | ||||
| pub trait ContextElement: | ||||
| @ -11,9 +13,10 @@ pub trait ContextElement: | ||||
|     + Walkable | ||||
|     + Renderable | ||||
|     + Loopable | ||||
|     + CloneIntoBoxedContextElement | ||||
|     // + CloneIntoBoxedContextElement
 | ||||
|     + CompareContextElement | ||||
|     + FromContextElement | ||||
|     + IntoRcIce | ||||
| { | ||||
| } | ||||
| 
 | ||||
| @ -61,15 +64,15 @@ pub trait CompareContextElement: CastToAny { | ||||
|     fn partial_compare(&self, other: &dyn ContextElement) -> Option<Ordering>; | ||||
| } | ||||
| 
 | ||||
| pub trait CloneIntoBoxedContextElement { | ||||
|     fn clone_to_box(&self) -> Box<dyn IntoContextElement>; | ||||
| } | ||||
| // pub trait CloneIntoBoxedContextElement {
 | ||||
| //     fn clone_to_box(&self) -> Box<dyn IntoContextElement>;
 | ||||
| // }
 | ||||
| 
 | ||||
| impl<C: 'static + IntoContextElement + Clone> CloneIntoBoxedContextElement for C { | ||||
|     fn clone_to_box(&self) -> Box<dyn IntoContextElement> { | ||||
|         Box::new(self.clone()) | ||||
|     } | ||||
| } | ||||
| // impl<C: 'static + IntoContextElement + Clone> CloneIntoBoxedContextElement for C {
 | ||||
| //     fn clone_to_box(&self) -> Box<dyn IntoContextElement> {
 | ||||
| //         Box::new(self.clone())
 | ||||
| //     }
 | ||||
| // }
 | ||||
| 
 | ||||
| impl<C: 'static + ContextElement> CastToAny for C { | ||||
|     fn to_any(&self) -> &dyn Any { | ||||
| @ -99,20 +102,76 @@ impl<C: ContextElement> FromContextElement for C { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait IntoContextElement: Debug + Walkable + CloneIntoBoxedContextElement { | ||||
|     fn into_context_element( | ||||
|         &self, | ||||
| pub trait IntoContextElement: Debug + Walkable /* + CloneIntoBoxedContextElement*/ { | ||||
|     fn into_context_element<'a>( | ||||
|         &'a self, | ||||
|         renderer: &DustRenderer, | ||||
|         breadcrumbs: &Vec<&dyn IntoContextElement>, | ||||
|     ) -> &dyn ContextElement; | ||||
|         breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>, | ||||
|     ) -> Option<IceResult<'a>>; | ||||
| } | ||||
| 
 | ||||
| impl<C: ContextElement> IntoContextElement for C { | ||||
|     fn into_context_element( | ||||
|         &self, | ||||
|     fn into_context_element<'a>( | ||||
|         &'a self, | ||||
|         renderer: &DustRenderer, | ||||
|         breadcrumbs: &Vec<&dyn IntoContextElement>, | ||||
|     ) -> &dyn ContextElement { | ||||
|         self | ||||
|         breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>, | ||||
|     ) -> Option<IceResult<'a>> { | ||||
|         Some(IceResult::from_borrowed(self)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait IntoRcIce { | ||||
|     fn into_rc_ice(self: Rc<Self>) -> Rc<dyn IntoContextElement>; | ||||
| } | ||||
| 
 | ||||
| impl<C: 'static + ContextElement> IntoRcIce for C { | ||||
|     fn into_rc_ice(self: Rc<C>) -> Rc<dyn IntoContextElement> { | ||||
|         Rc::clone(&self) as Rc<dyn IntoContextElement> | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub enum IceResult<'a> { | ||||
|     // Using Rc so that when we need to create BreadcrumbTrees with
 | ||||
|     // the same BreadcrumbTreeElement but a different parent (for
 | ||||
|     // example, when inserting behind the tail), we don't need to the
 | ||||
|     // copy the already owned/malloc'd data.
 | ||||
|     Owned(Rc<dyn ContextElement>), | ||||
|     Borrowed(&'a dyn ContextElement), | ||||
| } | ||||
| 
 | ||||
| impl<'a> IceResult<'a> { | ||||
|     pub fn from_owned<C: 'static + ContextElement>(val: C) -> IceResult<'a> { | ||||
|         IceResult::Owned(Rc::new(val)) | ||||
|     } | ||||
| 
 | ||||
|     pub fn from_borrowed(val: &'a dyn ContextElement) -> IceResult<'a> { | ||||
|         IceResult::Borrowed(val) | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_context_element_reference(&self) -> &dyn ContextElement { | ||||
|         match self { | ||||
|             IceResult::Owned(rc_ce) => rc_ce.as_ref(), | ||||
|             IceResult::Borrowed(ce) => *ce, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> IntoContextElement for IceResult<'a> { | ||||
|     fn into_context_element<'b>( | ||||
|         &'b self, | ||||
|         renderer: &DustRenderer, | ||||
|         breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>, | ||||
|     ) -> Option<IceResult<'b>> { | ||||
|         match self { | ||||
|             IceResult::Owned(rc_ce) => Some(IceResult::from_borrowed(rc_ce.as_ref())), | ||||
|             IceResult::Borrowed(ce) => Some(IceResult::from_borrowed(*ce)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> Walkable for IceResult<'a> { | ||||
|     fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { | ||||
|         self.get_context_element_reference().walk(segment) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,9 @@ | ||||
| use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; | ||||
| use crate::renderer::context_element::CompareContextElement; | ||||
| use crate::renderer::context_element::ContextElement; | ||||
| use crate::renderer::context_element::IceResult; | ||||
| use crate::renderer::context_element::IntoContextElement; | ||||
| use crate::renderer::DustRenderer; | ||||
| use crate::renderer::Loopable; | ||||
| use crate::renderer::RenderError; | ||||
| use crate::renderer::Renderable; | ||||
| @ -16,7 +19,7 @@ use std::cmp::Ordering; | ||||
| /// Functions the same as the injected parameters contexts for
 | ||||
| /// helpers/partials with parameters but this has no need for storing
 | ||||
| /// breadcrumbs since its simply storing two integers.
 | ||||
| #[derive(Debug, Clone)] | ||||
| #[derive(Debug)] | ||||
| pub struct IterationContext { | ||||
|     idx: OwnedLiteral, | ||||
|     len: OwnedLiteral, | ||||
| @ -32,32 +35,13 @@ impl IterationContext { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ContextElement for IterationContext {} | ||||
| 
 | ||||
| impl Truthiness for IterationContext { | ||||
|     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 IterationContext { | ||||
|     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 IterationContext { | ||||
|     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 IntoContextElement for IterationContext { | ||||
|     fn into_context_element<'b>( | ||||
|         &'b self, | ||||
|         renderer: &DustRenderer, | ||||
|         breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>, | ||||
|     ) -> Option<IceResult<'b>> { | ||||
|         panic!("into_context_element cannot be called on pseudo elements"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -74,15 +58,3 @@ impl Walkable for IterationContext { | ||||
|         true | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl CompareContextElement for IterationContext { | ||||
|     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 | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,14 +1,16 @@ | ||||
| //! This module contains a renderer for a rust implementation of LinkedIn Dust
 | ||||
| 
 | ||||
| mod breadcrumb_tree; | ||||
| mod context_element; | ||||
| mod errors; | ||||
| mod inline_partial_tree; | ||||
| mod iteration_context; | ||||
| mod parameters_context; | ||||
| mod renderer; | ||||
| mod tree_walking; | ||||
| mod walking; | ||||
| 
 | ||||
| pub use context_element::CloneIntoBoxedContextElement; | ||||
| // pub use context_element::CloneIntoBoxedContextElement;
 | ||||
| pub use context_element::CompareContextElement; | ||||
| pub use context_element::ContextElement; | ||||
| pub use context_element::IntoContextElement; | ||||
| @ -20,5 +22,5 @@ pub use errors::CompileError; | ||||
| pub use errors::RenderError; | ||||
| pub use errors::WalkError; | ||||
| pub use renderer::compile_template; | ||||
| pub use renderer::CompiledTemplate; | ||||
| // pub use renderer::CompiledTemplate;
 | ||||
| pub use renderer::DustRenderer; | ||||
|  | ||||
| @ -1,9 +1,13 @@ | ||||
| use crate::parser::Filter; | ||||
| use crate::parser::KVPair; | ||||
| use crate::parser::{Filter, OwnedLiteral, PartialNameElement, RValue}; | ||||
| use crate::parser::OwnedLiteral; | ||||
| use crate::parser::RValue; | ||||
| use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; | ||||
| use crate::renderer::context_element::CompareContextElement; | ||||
| use crate::renderer::context_element::ContextElement; | ||||
| use crate::renderer::context_element::IceResult; | ||||
| use crate::renderer::context_element::IntoContextElement; | ||||
| use crate::renderer::walking::walk_path; | ||||
| use crate::renderer::tree_walking::walk_path; | ||||
| use crate::renderer::DustRenderer; | ||||
| use crate::renderer::Loopable; | ||||
| use crate::renderer::RenderError; | ||||
| @ -11,104 +15,80 @@ 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::borrow::Borrow; | ||||
| use std::cmp::Ordering; | ||||
| use std::collections::HashMap; | ||||
| use std::rc::Rc; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct ParametersContext { | ||||
|     params: HashMap<String, OwnedRValue>, | ||||
|     breadcrumbs: Vec<Box<dyn IntoContextElement>>, | ||||
| pub struct ParametersContext<'a> { | ||||
|     params: HashMap<&'a str, (&'a RValue<'a>, Option<BreadcrumbTreeElement<'a>>)>, | ||||
| } | ||||
| 
 | ||||
| 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(); | ||||
|         renderer: &DustRenderer, | ||||
|         breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>, | ||||
|         params: &'a Vec<KVPair>, | ||||
|     ) -> Self { | ||||
|         // If the parameter is a Path, then we resolve it immediately
 | ||||
|         // to a context element because those are resolved using the
 | ||||
|         // breadcrumbs at the time of assignment.
 | ||||
|         //
 | ||||
|         // If the parameter is a template (for example `foo="{bar}"`)
 | ||||
|         // then those are resolved at the time of access rather than
 | ||||
|         // the time of assignment, so we leave them into their
 | ||||
|         // original IntoContextElement state.
 | ||||
|         let rendered_params: HashMap<&'a str, (&'a RValue<'a>, Option<BreadcrumbTreeElement<'a>>)> = | ||||
|             params | ||||
|                 .iter() | ||||
|                 .map(|kvpair| { | ||||
|                     let k = kvpair.key; | ||||
|                     let v: Option<BreadcrumbTreeElement<'a>> = match &kvpair.value { | ||||
|                         RValue::RVLiteral(owned_literal) => { | ||||
|                             Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value)) | ||||
|                         } | ||||
|                         RValue::RVPath(path) => kvpair | ||||
|                             .value | ||||
|                             .into_context_element(renderer, breadcrumbs) | ||||
|                             .map(std::convert::From::from), | ||||
|                         RValue::RVTemplate(template) => { | ||||
|                             Some(BreadcrumbTreeElement::from_borrowed(&kvpair.value)) | ||||
|                         } | ||||
|                     }; | ||||
|                     (k, (&kvpair.value, v)) | ||||
|                 }) | ||||
|                 .collect(); | ||||
| 
 | ||||
|         ParametersContext { | ||||
|             params: owned_params, | ||||
|             breadcrumbs: owned_breadcrumbs, | ||||
|             params: rendered_params, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ContextElement for ParametersContext {} | ||||
|     pub fn get_original_rvalue(&self, segment: &str) -> Option<&'a RValue<'a>> { | ||||
|         self.params.get(segment).map(|(rvalue, _bte)| *rvalue) | ||||
|     } | ||||
| 
 | ||||
| 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 | ||||
|     pub fn contains_key(&self, segment: &str) -> bool { | ||||
|         self.params.contains_key(segment) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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<'a> IntoContextElement for ParametersContext<'a> { | ||||
|     fn into_context_element<'b>( | ||||
|         &'b self, | ||||
|         renderer: &DustRenderer, | ||||
|         breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>, | ||||
|     ) -> Option<IceResult<'b>> { | ||||
|         panic!("into_context_element cannot be called on pseudo elements"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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 { | ||||
| impl<'a> Walkable for ParametersContext<'a> { | ||||
|     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), | ||||
|         match self.params.get(segment).map(|(_rvalue, bte)| bte) { | ||||
|             Some(Some(bte)) => Ok(bte.borrow()), | ||||
|             _ => Err(WalkError::CantWalk), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -117,34 +97,30 @@ impl Walkable for ParametersContext { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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<'a> IntoContextElement for RValue<'a> { | ||||
|     fn into_context_element<'b>( | ||||
|         &'b self, | ||||
|         renderer: &DustRenderer, | ||||
|         breadcrumbs: &'b Vec<BreadcrumbTreeElement<'b>>, | ||||
|     ) -> Option<IceResult<'b>> { | ||||
|         match self { | ||||
|             RValue::RVLiteral(owned_literal) => Some(IceResult::from_borrowed(owned_literal)), | ||||
|             RValue::RVPath(path) => walk_path(breadcrumbs, &path.keys) | ||||
|                 .map(|ice| ice.into_context_element(renderer, breadcrumbs)) | ||||
|                 .ok() | ||||
|                 .flatten(), | ||||
|             RValue::RVTemplate(template) => renderer | ||||
|                 .render_partial_name(template, breadcrumbs) | ||||
|                 .map(|rendered| OwnedLiteral::LString(rendered)) | ||||
|                 .ok() | ||||
|                 .map(|owned_literal| IceResult::from_owned(owned_literal)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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<'a> Walkable for RValue<'a> { | ||||
|     fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { | ||||
|         Err(WalkError::CantWalk) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -240,24 +216,3 @@ impl CompareContextElement for OwnedLiteral { | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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"), | ||||
|         ) | ||||
|         // &OwnedLiteral::LPositiveInteger(1)
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Walkable for Vec<PartialNameElement> { | ||||
|     fn walk(&self, segment: &str) -> Result<&dyn IntoContextElement, WalkError> { | ||||
|         Err(WalkError::CantWalk) | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										264
									
								
								src/renderer/parameters_context_old.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								src/renderer/parameters_context_old.rs
									
									
									
									
									
										Normal 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) | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1021
									
								
								src/renderer/renderer_old.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1021
									
								
								src/renderer/renderer_old.rs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										94
									
								
								src/renderer/tree_walking.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/renderer/tree_walking.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | ||||
| use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; | ||||
| use crate::renderer::context_element::IntoContextElement; | ||||
| use crate::renderer::WalkError; | ||||
| use std::borrow::Borrow; | ||||
| 
 | ||||
| enum WalkResult<'a> { | ||||
|     NoWalk, | ||||
|     PartialWalk, | ||||
|     FullyWalked(&'a dyn IntoContextElement), | ||||
| } | ||||
| 
 | ||||
| fn walk_path_from_single_level<'a, P>( | ||||
|     context: &'a dyn IntoContextElement, | ||||
|     path: &[P], | ||||
| ) -> WalkResult<'a> | ||||
| where | ||||
|     P: Borrow<str>, | ||||
| { | ||||
|     if path.is_empty() { | ||||
|         return WalkResult::FullyWalked(context); | ||||
|     } | ||||
| 
 | ||||
|     let mut walk_failure = WalkResult::NoWalk; | ||||
|     let mut output = context; | ||||
|     for elem in path.iter() { | ||||
|         match output.walk(elem.borrow()) { | ||||
|             Err(WalkError::CantWalk { .. }) => { | ||||
|                 return walk_failure; | ||||
|             } | ||||
|             Ok(new_val) => { | ||||
|                 walk_failure = WalkResult::PartialWalk; | ||||
|                 output = new_val; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     WalkResult::FullyWalked(output) | ||||
| } | ||||
| 
 | ||||
| fn get_first_non_pseudo_element<'a>( | ||||
|     breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>, | ||||
| ) -> Option<&'a BreadcrumbTreeElement<'a>> { | ||||
|     breadcrumbs | ||||
|         .iter() | ||||
|         .rev() | ||||
|         .filter(|b| { | ||||
|             !std::borrow::Borrow::<dyn IntoContextElement + 'a>::borrow(*b).is_pseudo_element() | ||||
|         }) | ||||
|         .next() | ||||
| } | ||||
| 
 | ||||
| pub fn walk_path<'a, P>( | ||||
|     breadcrumbs: &'a Vec<BreadcrumbTreeElement<'a>>, | ||||
|     path: &Vec<P>, | ||||
| ) -> Result<&'a dyn IntoContextElement, WalkError> | ||||
| where | ||||
|     P: Borrow<str>, | ||||
| { | ||||
|     match (breadcrumbs.last(), path.first()) { | ||||
|         (None, _) => return Err(WalkError::CantWalk), | ||||
|         (Some(last_elem), None) => return Ok(last_elem.borrow()), | ||||
|         (Some(_), Some(path_first)) if path_first.borrow() == "." => { | ||||
|             let first_non_pseudo_element = get_first_non_pseudo_element(breadcrumbs); | ||||
|             return match first_non_pseudo_element { | ||||
|                 None => Err(WalkError::CantWalk), | ||||
|                 Some(current_context) => { | ||||
|                     match walk_path_from_single_level(current_context.borrow(), &path[1..]) { | ||||
|                         // If no walking was done at all or we partially walked
 | ||||
|                         // then stop trying to find anything because '.' restricts
 | ||||
|                         // us to the current scope
 | ||||
|                         WalkResult::NoWalk | WalkResult::PartialWalk => Err(WalkError::CantWalk), | ||||
|                         WalkResult::FullyWalked(new_context) => Ok(new_context), | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|         (Some(_), Some(path_first)) => { | ||||
|             for context in breadcrumbs.iter().rev() { | ||||
|                 match walk_path_from_single_level(context.borrow(), 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
	 Tom Alexander
						Tom Alexander