use crate::parser::template; use crate::parser::Path; use crate::parser::Template; use crate::renderer::breadcrumb_tree::BreadcrumbTree; use crate::renderer::breadcrumb_tree::BreadcrumbTreeElement; use crate::renderer::context_element::ContextElement; use crate::renderer::context_element::IntoContextElement; use crate::renderer::errors::CompileError; use crate::renderer::tree_walking::walk_path; use std::collections::HashMap; #[derive(Clone, Debug)] pub struct DustRenderer<'a> { templates: HashMap>, } pub fn compile_template<'a>(source: &'a str) -> Result, CompileError> { let (_remaining, parsed_template) = template(source).map_err(|err| CompileError { message: "Failed to compile template".to_owned(), })?; Ok(parsed_template) } impl<'a> DustRenderer<'a> { pub fn new() -> DustRenderer<'a> { DustRenderer { templates: HashMap::new(), } } pub fn load_source(&mut self, template: &'a Template, name: String) { self.templates.insert(name, template); } /// Returns a option of a tuple of (parent, new_node_elements) /// which can then be formed into new BreadcrumbTreeNodes /// /// If None is returned, then it is a signal to simply re-use the /// existing breadcrumbs. /// /// Otherwise, the parent (which may be None, especially for /// explicit contexts) and the additional node elements (which may /// be empty) should be combined into a final BreadcrumbTreeNode fn new_breadcrumbs_section<'b>( &self, maybe_breadcrumbs: Option<&'a BreadcrumbTree>, index_context: Option<&'b dyn IntoContextElement>, injected_context: Option<&'b dyn IntoContextElement>, explicit_context: &Option>, new_context_element: Option<&'b dyn ContextElement>, ) -> Option<(Option<&'b BreadcrumbTree>, Vec>)> { // If none of the additional contexts are present, return None // to signal that the original breadcrumbs should be used // rather than incurring a copy here. match ( index_context, injected_context, explicit_context, new_context_element, ) { (None, None, None, None) => return None, _ => (), } // If there is an explicit context, then drop all the current // context let parent = match explicit_context { Some(_) => None, None => maybe_breadcrumbs, }; let mut new_nodes: Vec = Vec::new(); explicit_context.as_ref().map(|path| { let x = walk_path(maybe_breadcrumbs, &path.keys); x.map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) .map(|val| { if val.is_truthy() { new_nodes.push(BreadcrumbTreeElement::Borrowed(val.from_context_element())) } }); }); injected_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); new_context_element .map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx.from_context_element()))); index_context.map(|ctx| new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx))); Some((parent, new_nodes)) } fn new_breadcrumbs_partial<'b>( &self, maybe_breadcrumbs: Option<&'b BreadcrumbTree>, explicit_context_maybe_breadcrumbs: Option<&'b BreadcrumbTree>, injected_context: Option<&'b dyn IntoContextElement>, explicit_context: &Option>, ) -> Option<(Option<&'b BreadcrumbTree>, Vec>)> { // If none of the additional contexts are present, return None // to signal that the original breadcrumbs should be used // rather than incurring a copy here. match (injected_context, explicit_context) { (None, None) => return None, _ => (), }; // If there is an explicit context, then drop all the current // context let mut parent = match explicit_context { Some(_) => None, None => maybe_breadcrumbs, }; let mut new_nodes: Vec = Vec::new(); injected_context.map(|ctx| { // Special case: when there is no explicit context, the // injected context gets inserted 1 spot behind the // current context. Otherwise, the injected context gets // added after the current context but before the explicit // context. match explicit_context { None => { let (new_parent, passed_nodes) = Self::split_tree_at_predicate(parent, |b| b.get_ice().is_pseudo_element()); parent = new_parent; new_nodes.extend(passed_nodes.iter().map(|b| b.get_element().clone())); } _ => new_nodes.push(BreadcrumbTreeElement::Borrowed(ctx)), } }); explicit_context.as_ref().map(|path| { let x = walk_path(maybe_breadcrumbs, &path.keys); // TODO: should resolving the value here use // explicit_context_maybe_breadcrumbs or // maybe_breadcrumbs? x.map(|ice| ice.into_context_element(self, maybe_breadcrumbs)) .map(|val| { if val.is_truthy() { new_nodes.push(BreadcrumbTreeElement::Borrowed(val.from_context_element())) } }); }); None } /// Returns a Breadcrumb tree where all the bottom nodes that do /// not match the predicate and the first node that match the /// predicate are shaved off, and a list of those nodes that are /// shaved off. fn split_tree_at_predicate<'b, F>( maybe_breadcrumbs: Option<&'b BreadcrumbTree>, f: F, ) -> (Option<&'b BreadcrumbTree<'b>>, Vec<&'b BreadcrumbTree<'b>>) where F: Fn(&'b BreadcrumbTree) -> bool, { match maybe_breadcrumbs { None => return (None, Vec::new()), Some(breadcrumbs) => { let mut passed_nodes: Vec<&'b BreadcrumbTree<'b>> = Vec::new(); for tree_node in breadcrumbs { passed_nodes.push(tree_node); if f(tree_node) { return (tree_node.get_parent(), passed_nodes); } } return (None, passed_nodes); } } } }