+use std::collections::HashMap;
use std::fs::File;
use std::path::PathBuf;
use std::io::Read;
write!(f, "\n]]]")
},
Token::IteratorDirective{collection, member_label, children} => {
- write!(f, "IteratorDirective {}: {} [[[\n", collection, member_label)?;
+ write!(f, "{} in {}\n", collection, member_label)?;
for child in children {
write!(f, "\t{}\n", child)?;
}
}
}
+#[derive(Clone)]
+pub enum TemplateValue {
+ String(String),
+ Unsigned(u64),
+ Bool(bool),
+ Collection(Vec<TemplateContext>),
+ Context(TemplateContext)
+}
+
+impl TemplateValue {
+ fn render(&self) -> String {
+ match self {
+ TemplateValue::String(string) => string.to_string(),
+ TemplateValue::Unsigned(number) => format!("{}", number),
+ TemplateValue::Bool(bool) => format!("{}", bool),
+ _ => "".to_string()
+ }
+ }
+}
+
+pub type TemplateContext = HashMap<String, TemplateValue>;
+
+struct TemplateContextGetter {}
+impl TemplateContextGetter {
+ fn get(context: &TemplateContext, path: &str) -> Option<TemplateValue> {
+ let path_parts: Vec<&str> = path.split('.').collect();
+ TemplateContextGetter::recursively_get_value(context, &path_parts)
+ }
+
+ fn recursively_get_value(context: &TemplateContext, path: &[&str]) -> Option<TemplateValue> {
+ match context.get(path[0]) {
+ Some(TemplateValue::Context(next)) if path.len() > 1 => TemplateContextGetter::recursively_get_value(next, &path[1..]),
+ Some(value) if path.len() == 1 => Some(value.clone()),
+ _ => None
+ }
+ }
+}
+
pub struct ParsedTemplate {
pub tokens: Vec<Token>
}
+impl ParsedTemplate {
+ pub fn render(&self, context: &TemplateContext) -> String {
+ ParsedTemplate::render_tokens(&self.tokens, context)
+ }
+
+ pub fn render_tokens(tokens: &Vec<Token>, context: &TemplateContext) -> String {
+ let mut rendered_template: String = String::new();
+ for token in tokens {
+ match token {
+ Token::Text(contents) => rendered_template.push_str(&contents),
+ Token::DisplayDirective { content } => {
+ match TemplateContextGetter::get(context, &content) {
+ Some(value) => rendered_template.push_str(&value.render()),
+ None => panic!("{} is not a valid key", content)
+ }
+ },
+ Token::ConditionalDirective { condition, children} => {
+ let mut negator = false;
+ let mut condition = condition.to_string();
+ if condition.starts_with('!') {
+ negator = true;
+ condition = condition[1..].to_string();
+ }
+ match TemplateContextGetter::get(context, &condition) {
+ Some(TemplateValue::Bool(value)) => {
+ if negator ^ value {
+ rendered_template.push_str(&ParsedTemplate::render_tokens(children, context))
+ }
+ },
+ _ => panic!("{} is not a boolean value", condition)
+ }
+ },
+ Token::IteratorDirective { collection, member_label, children } => {
+ match TemplateContextGetter::get(context, &collection) {
+ Some(TemplateValue::Collection(collection)) => {
+ for member in collection {
+ let mut child_context = context.clone();
+ child_context.insert(
+ member_label.to_string(),
+ TemplateValue::Context(member)
+ );
+ rendered_template.push_str(&ParsedTemplate::render_tokens(&children, &child_context))
+ }
+ },
+ _ => panic!("{} is not a collection", collection)
+ }
+ }
+ }
+ }
+ rendered_template
+ }
+}
+
pub fn parse(template: &str) -> ParsedTemplate {
let mut tokens = Vec::new();
tokenize(template, &mut tokens);