diff options
Diffstat (limited to 'src/template.rs')
| -rw-r--r-- | src/template.rs | 157 |
1 files changed, 101 insertions, 56 deletions
diff --git a/src/template.rs b/src/template.rs index 0213fc3..9bd8644 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,42 +1,58 @@ -use std::io::{Error, ErrorKind::Other, Result}; use std::collections::HashMap; use std::fs::File; -use std::path::PathBuf; use std::io::Read; +use std::io::{Error, ErrorKind::Other, Result}; +use std::path::Path; -const TXT_TEMPLATE: &'static str = include_str!("../templates/index.txt"); -const HTML_TEMPLATE: &'static str = include_str!("../templates/index.html"); -const GMI_TEMPLATE: &'static str = include_str!("../templates/index.gmi"); -const RSS_TEMPLATE: &'static str = include_str!("../templates/feed.xml"); +const TXT_TEMPLATE: &str = include_str!("../templates/index.txt"); +const HTML_TEMPLATE: &str = include_str!("../templates/index.html"); +const GMI_TEMPLATE: &str = include_str!("../templates/index.gmi"); +const RSS_TEMPLATE: &str = include_str!("../templates/feed.xml"); // Parse and Render pub enum Token { Text(String), - DisplayDirective { content: String }, - ConditionalDirective { condition: String, children: Vec<Token>}, - IteratorDirective { collection: String, member_label: String, children: Vec<Token> } + DisplayDirective { + content: String, + }, + ConditionalDirective { + condition: String, + children: Vec<Token>, + }, + IteratorDirective { + collection: String, + member_label: String, + children: Vec<Token>, + }, } impl std::fmt::Display for Token { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Token::Text(label) => write!(f, "Text {}", label), - Token::DisplayDirective{content} => write!(f, "DisplayDirective {}", content), - Token::ConditionalDirective{condition, children} => { + Token::DisplayDirective { content } => write!(f, "DisplayDirective {}", content), + Token::ConditionalDirective { + condition, + children, + } => { write!(f, "ConditionalDirective {} [[[\n", condition)?; for child in children { write!(f, "\t{}\n", child)?; } write!(f, "\n]]]") - }, - Token::IteratorDirective{collection, member_label, children} => { + } + Token::IteratorDirective { + collection, + member_label, + children, + } => { write!(f, "{} in {}\n", collection, member_label)?; for child in children { write!(f, "\t{}\n", child)?; } write!(f, "\n]]]") - }, + } } } } @@ -47,7 +63,7 @@ pub enum TemplateValue { Unsigned(u64), Bool(bool), Collection(Vec<TemplateContext>), - Context(TemplateContext) + Context(TemplateContext), } impl TemplateValue { @@ -56,7 +72,7 @@ impl TemplateValue { TemplateValue::String(string) => string.to_string(), TemplateValue::Unsigned(number) => format!("{}", number), TemplateValue::Bool(bool) => format!("{}", bool), - _ => "".to_string() + _ => "".to_string(), } } } @@ -72,15 +88,17 @@ impl TemplateContextGetter { 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(TemplateValue::Context(next)) if path.len() > 1 => { + TemplateContextGetter::recursively_get_value(next, &path[1..]) + } Some(value) if path.len() == 1 => Some(value.clone()), - _ => None + _ => None, } } } pub struct ParsedTemplate { - pub tokens: Vec<Token> + pub tokens: Vec<Token>, } impl ParsedTemplate { @@ -94,11 +112,15 @@ impl ParsedTemplate { match token { Token::Text(contents) => rendered_template.push_str(&contents), Token::DisplayDirective { content } => { - let value = TemplateContextGetter::get(context, &content) - .ok_or_else(|| Error::new(Other, format!("{} is not a valid key", content)))?; + let value = TemplateContextGetter::get(context, &content).ok_or_else(|| { + Error::new(Other, format!("{} is not a valid key", content)) + })?; rendered_template.push_str(&value.render()); - }, - Token::ConditionalDirective { condition, children} => { + } + Token::ConditionalDirective { + condition, + children, + } => { let mut negator = false; let mut condition = condition.to_string(); if condition.starts_with('!') { @@ -106,22 +128,34 @@ impl ParsedTemplate { condition = condition[1..].to_string(); } - let value = TemplateContextGetter::get(context, &condition) - .ok_or_else(|| Error::new(Other, format!("{} is not a valid key", condition)))?; + let value = + TemplateContextGetter::get(context, &condition).ok_or_else(|| { + Error::new(Other, format!("{} is not a valid key", condition)) + })?; match value { TemplateValue::Bool(value) => { if negator ^ value { - rendered_template.push_str(&ParsedTemplate::render_tokens(children, context)?) + rendered_template + .push_str(&ParsedTemplate::render_tokens(children, context)?) } Ok(()) - }, - _ => Err(Error::new(Other, format!("{} is not a boolean value", condition))), + } + _ => Err(Error::new( + Other, + format!("{} is not a boolean value", condition), + )), }?; - }, - Token::IteratorDirective { collection, member_label, children } => { - let value = TemplateContextGetter::get(context, &collection) - .ok_or_else(|| Error::new(Other, format!("{} is not a valid key", collection)))?; + } + Token::IteratorDirective { + collection, + member_label, + children, + } => { + let value = + TemplateContextGetter::get(context, &collection).ok_or_else(|| { + Error::new(Other, format!("{} is not a valid key", collection)) + })?; match value { TemplateValue::Collection(collection) => { @@ -129,13 +163,19 @@ impl ParsedTemplate { let mut child_context = context.clone(); child_context.insert( member_label.to_string(), - TemplateValue::Context(member) + TemplateValue::Context(member), ); - rendered_template.push_str(&ParsedTemplate::render_tokens(&children, &child_context)?) + rendered_template.push_str(&ParsedTemplate::render_tokens( + &children, + &child_context, + )?) } Ok(()) - }, - _ => Err(Error::new(Other, format!("{} is not a collection", collection))), + } + _ => Err(Error::new( + Other, + format!("{} is not a collection", collection), + )), }?; } } @@ -147,16 +187,15 @@ impl ParsedTemplate { pub fn parse(template: &str) -> Option<ParsedTemplate> { let mut tokens = Vec::new(); tokenize(template, &mut tokens).ok()?; - Some(ParsedTemplate { - tokens - }) + Some(ParsedTemplate { tokens }) } fn tokenize(template: &str, tokens: &mut Vec<Token>) -> Result<()> { let mut remaining_template = template; while !remaining_template.is_empty() && remaining_template.contains("{{") { - let directive_start_index = remaining_template.find("{{") + let directive_start_index = remaining_template + .find("{{") .ok_or_else(|| Error::new(Other, "Was expecting at least one tag opener"))?; if directive_start_index > 0 { let text = remaining_template[..directive_start_index].to_string(); @@ -164,8 +203,10 @@ fn tokenize(template: &str, tokens: &mut Vec<Token>) -> Result<()> { } remaining_template = &remaining_template[directive_start_index..]; - let directive_end_index = remaining_template.find("}}") - .ok_or_else(|| Error::new(Other, "Was expecting }} after {{"))? + 2; + let directive_end_index = remaining_template + .find("}}") + .ok_or_else(|| Error::new(Other, "Was expecting }} after {{"))? + + 2; let directive = &remaining_template[..directive_end_index]; remaining_template = &remaining_template[directive_end_index..]; @@ -174,10 +215,10 @@ fn tokenize(template: &str, tokens: &mut Vec<Token>) -> Result<()> { // Simple Directives '=' => { let content = directive[3..directive.len() - 2].trim(); - tokens.push(Token::DisplayDirective{ - content: content.to_string() + tokens.push(Token::DisplayDirective { + content: content.to_string(), }); - }, + } // Block Directives '?' | '~' => { let content = directive[3..directive.len() - 2].trim(); @@ -189,11 +230,11 @@ fn tokenize(template: &str, tokens: &mut Vec<Token>) -> Result<()> { let directive_block = &remaining_template[..closing_block]; remaining_template = &remaining_template[closing_block + 5..]; tokenize(directive_block, &mut children)?; - tokens.push(Token::ConditionalDirective{ + tokens.push(Token::ConditionalDirective { condition: content.to_string(), - children + children, }); - }, + } '~' => { let parts: Vec<_> = content.splitn(2, ':').collect(); let closing_block = remaining_template.find("{{~}}").unwrap(); @@ -204,14 +245,14 @@ fn tokenize(template: &str, tokens: &mut Vec<Token>) -> Result<()> { tokens.push(Token::IteratorDirective { collection: parts[0].trim().to_string(), member_label: parts[1].trim().to_string(), - children + children, }); } - }, - _ => unreachable!() + } + _ => unreachable!(), } - }, - _ => unreachable!() + } + _ => unreachable!(), } } tokens.push(Token::Text(remaining_template.to_string())); @@ -220,11 +261,15 @@ fn tokenize(template: &str, tokens: &mut Vec<Token>) -> Result<()> { // File helpers. -pub fn find(template_directory: &PathBuf, filename: &str) -> Option<String> { +pub fn find(template_directory: &Path, filename: &str) -> Option<String> { let template_path = template_directory.join(filename); if template_path.exists() { let mut contents = String::new(); - if File::open(template_path).ok()?.read_to_string(&mut contents).is_ok() { + if File::open(template_path) + .ok()? + .read_to_string(&mut contents) + .is_ok() + { return Some(contents); } } @@ -237,6 +282,6 @@ fn find_default(filename: &str) -> Option<String> { "index.html" => Some(HTML_TEMPLATE.to_string()), "index.gmi" => Some(GMI_TEMPLATE.to_string()), "index.rss" => Some(RSS_TEMPLATE.to_string()), - &_ => None + &_ => None, } } |