2020-04-11 00:27:27 +00:00
|
|
|
use crate::parser::template;
|
2020-04-11 22:40:36 +00:00
|
|
|
use crate::parser::DustTag;
|
2020-04-11 00:27:27 +00:00
|
|
|
use crate::parser::Template;
|
2020-04-11 22:25:48 +00:00
|
|
|
use crate::parser::TemplateElement;
|
|
|
|
use crate::renderer::errors::CompileError;
|
|
|
|
use crate::renderer::errors::RenderError;
|
2020-04-11 23:11:14 +00:00
|
|
|
use crate::renderer::renderable::Renderable;
|
2020-04-12 00:31:44 +00:00
|
|
|
use crate::renderer::walkable::Walkable;
|
2020-04-11 22:25:48 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::ops::Index;
|
2020-04-11 00:27:27 +00:00
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct CompiledTemplate<'a> {
|
|
|
|
template: Template<'a>,
|
2020-04-11 22:25:48 +00:00
|
|
|
pub name: String,
|
2020-04-11 00:27:27 +00:00
|
|
|
}
|
|
|
|
|
2020-04-11 00:55:44 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2020-04-11 00:58:55 +00:00
|
|
|
pub struct DustRenderer<'a> {
|
2020-04-11 22:25:48 +00:00
|
|
|
templates: HashMap<String, &'a Template<'a>>,
|
2020-04-11 00:55:44 +00:00
|
|
|
}
|
|
|
|
|
2020-04-11 22:25:48 +00:00
|
|
|
pub fn compile_template<'a>(
|
|
|
|
source: &'a str,
|
|
|
|
name: String,
|
|
|
|
) -> Result<CompiledTemplate<'a>, CompileError> {
|
2020-04-11 00:27:27 +00:00
|
|
|
// TODO: Make this all consuming
|
2020-04-11 22:25:48 +00:00
|
|
|
// TODO: This could use better error management
|
2020-04-11 00:27:27 +00:00
|
|
|
let (_remaining, parsed_template) = template(source).expect("Failed to compile template");
|
2020-04-11 22:25:48 +00:00
|
|
|
Ok(CompiledTemplate {
|
2020-04-11 00:27:27 +00:00
|
|
|
template: parsed_template,
|
|
|
|
name: name,
|
2020-04-11 22:25:48 +00:00
|
|
|
})
|
2020-04-11 00:27:27 +00:00
|
|
|
}
|
2020-04-11 00:55:44 +00:00
|
|
|
|
2020-04-11 00:58:55 +00:00
|
|
|
impl<'a> DustRenderer<'a> {
|
|
|
|
pub fn new() -> DustRenderer<'a> {
|
|
|
|
DustRenderer {
|
2020-04-11 22:25:48 +00:00
|
|
|
templates: HashMap::new(),
|
2020-04-11 00:55:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn load_source(&mut self, template: &'a CompiledTemplate) {
|
2020-04-11 22:25:48 +00:00
|
|
|
self.templates
|
|
|
|
.insert(template.name.clone(), &template.template);
|
2020-04-11 00:55:44 +00:00
|
|
|
}
|
2020-04-11 00:58:55 +00:00
|
|
|
|
2020-04-11 22:49:47 +00:00
|
|
|
pub fn render<C>(&self, name: &str, context: &C) -> Result<String, RenderError>
|
2020-04-11 22:25:48 +00:00
|
|
|
where
|
2020-04-11 22:49:47 +00:00
|
|
|
C: Index<&'a str>,
|
2020-04-11 23:11:14 +00:00
|
|
|
<C as std::ops::Index<&'a str>>::Output: Renderable,
|
2020-04-11 22:25:48 +00:00
|
|
|
{
|
|
|
|
let main_template = match self.templates.get(name) {
|
|
|
|
Some(tmpl) => tmpl,
|
|
|
|
None => {
|
|
|
|
return Err(RenderError {
|
|
|
|
message: format!("No template named {} in context", name),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
self.render_template(main_template, context)
|
|
|
|
}
|
2020-04-11 00:58:55 +00:00
|
|
|
|
2020-04-11 22:49:47 +00:00
|
|
|
fn render_template<C>(&self, template: &Template, context: &C) -> Result<String, RenderError>
|
2020-04-11 22:25:48 +00:00
|
|
|
where
|
2020-04-11 22:49:47 +00:00
|
|
|
C: Index<&'a str>,
|
2020-04-11 23:11:14 +00:00
|
|
|
<C as std::ops::Index<&'a str>>::Output: Renderable,
|
2020-04-11 22:25:48 +00:00
|
|
|
{
|
|
|
|
let mut output = String::new();
|
|
|
|
for elem in &template.contents.elements {
|
|
|
|
match elem {
|
|
|
|
TemplateElement::TESpan(span) => output.push_str(span.contents),
|
2020-04-11 22:40:36 +00:00
|
|
|
TemplateElement::TETag(dt) => {
|
|
|
|
output.push_str(&self.render_tag(dt, context)?);
|
|
|
|
}
|
2020-04-11 22:25:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(output)
|
|
|
|
}
|
2020-04-11 22:40:36 +00:00
|
|
|
|
2020-04-11 22:49:47 +00:00
|
|
|
fn render_tag<C>(&self, tag: &DustTag, context: &C) -> Result<String, RenderError>
|
2020-04-11 22:40:36 +00:00
|
|
|
where
|
2020-04-11 22:49:47 +00:00
|
|
|
C: Index<&'a str>,
|
2020-04-11 23:11:14 +00:00
|
|
|
<C as std::ops::Index<&'a str>>::Output: Renderable,
|
2020-04-11 22:40:36 +00:00
|
|
|
{
|
|
|
|
match tag {
|
|
|
|
DustTag::DTComment(comment) => (),
|
2020-04-11 23:03:41 +00:00
|
|
|
DustTag::DTReference(reference) => {
|
|
|
|
let val = context.index("name");
|
2020-04-11 23:11:14 +00:00
|
|
|
return Ok(val.render());
|
2020-04-11 23:03:41 +00:00
|
|
|
}
|
2020-04-11 22:40:36 +00:00
|
|
|
_ => (), // TODO: Implement the rest
|
|
|
|
}
|
|
|
|
Ok("".to_owned())
|
|
|
|
}
|
2020-04-11 00:55:44 +00:00
|
|
|
}
|
2020-04-12 00:31:44 +00:00
|
|
|
|
|
|
|
fn walk_path<'a, C>(context: &'a C, path: &Vec<&str>) -> &'a C
|
|
|
|
where
|
|
|
|
C: Walkable<Output = C>,
|
|
|
|
{
|
|
|
|
let mut output: &C = context;
|
|
|
|
|
|
|
|
for elem in path.iter() {
|
|
|
|
output = context.walk(elem);
|
|
|
|
}
|
|
|
|
|
|
|
|
output
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
2020-04-12 01:03:21 +00:00
|
|
|
impl<'a, I: Walkable> Walkable for HashMap<&str, I> {
|
|
|
|
type Output = I;
|
2020-04-12 00:31:44 +00:00
|
|
|
|
2020-04-12 01:03:21 +00:00
|
|
|
fn walk(&self, segment: &str) -> &I {
|
2020-04-12 00:31:44 +00:00
|
|
|
self.get(segment).unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-12 01:03:21 +00:00
|
|
|
impl<'a> Walkable for u32 {
|
|
|
|
type Output = u32;
|
2020-04-12 00:34:16 +00:00
|
|
|
|
2020-04-12 01:03:21 +00:00
|
|
|
fn walk(&self, segment: &str) -> &u32 {
|
2020-04-12 00:34:16 +00:00
|
|
|
panic!("Tried to walk down a str");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-12 01:03:21 +00:00
|
|
|
fn do_the_walk<'a>(c: &'a (dyn Walkable<Output = u32> + 'a), path: &str) -> &'a u32 {
|
|
|
|
c.walk(path)
|
|
|
|
}
|
|
|
|
|
2020-04-12 00:31:44 +00:00
|
|
|
#[test]
|
|
|
|
fn test_walk_path() {
|
2020-04-12 01:03:21 +00:00
|
|
|
let context: HashMap<&str, u32> = [("cat", 1), ("dog", 2), ("tiger", 3)]
|
|
|
|
.iter()
|
|
|
|
.cloned()
|
|
|
|
.collect();
|
|
|
|
assert_eq!(do_the_walk(&context, "cat"), &1);
|
2020-04-12 00:31:44 +00:00
|
|
|
}
|
|
|
|
}
|