duster/src/renderer/renderer.rs

171 lines
6.6 KiB
Rust
Raw Normal View History

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;
2020-04-11 18:25:48 -04:00
use crate::renderer::errors::CompileError;
use crate::renderer::tree_walking::walk_path;
use std::collections::HashMap;
#[derive(Clone, Debug)]
2020-04-10 20:58:55 -04:00
pub struct DustRenderer<'a> {
2020-04-11 18:25:48 -04:00
templates: HashMap<String, &'a Template<'a>>,
}
pub fn compile_template<'a>(source: &'a str) -> Result<Template<'a>, CompileError> {
let (_remaining, parsed_template) = template(source).map_err(|err| CompileError {
message: "Failed to compile template".to_owned(),
})?;
Ok(parsed_template)
}
2020-04-10 20:58:55 -04:00
impl<'a> DustRenderer<'a> {
pub fn new() -> DustRenderer<'a> {
DustRenderer {
2020-04-11 18:25:48 -04:00
templates: HashMap::new(),
}
}
pub fn load_source(&mut self, template: &'a Template, name: String) {
self.templates.insert(name, template);
}
2020-04-10 20:58:55 -04:00
/// 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<Path<'b>>,
new_context_element: Option<&'b dyn ContextElement>,
) -> Option<(Option<&'b BreadcrumbTree>, Vec<BreadcrumbTreeElement<'b>>)> {
// 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<BreadcrumbTreeElement> = 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))
2020-05-05 20:22:25 -04:00
}
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<Path<'b>>,
) -> Option<(Option<&'b BreadcrumbTree>, Vec<BreadcrumbTreeElement<'b>>)> {
// 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<BreadcrumbTreeElement> = 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);
}
}
}
2020-04-11 20:31:44 -04:00
}