X-Git-Url: https://git.r.bdr.sh/rbdr/blog/blobdiff_plain/172f4c8807d44ebe38c7f227b7fdc2d6a9dbe323..50f53dc480fda8b3daab7a34454c2dd9f3f5f991:/src/template.rs diff --git a/src/template.rs b/src/template.rs index 0332506..0213fc3 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,3 +1,4 @@ +use std::io::{Error, ErrorKind::Other, Result}; use std::collections::HashMap; use std::fs::File; use std::path::PathBuf; @@ -83,20 +84,19 @@ pub struct ParsedTemplate { } impl ParsedTemplate { - pub fn render(&self, context: &TemplateContext) -> String { + pub fn render(&self, context: &TemplateContext) -> Result { ParsedTemplate::render_tokens(&self.tokens, context) } - pub fn render_tokens(tokens: &Vec, context: &TemplateContext) -> String { + pub fn render_tokens(tokens: &Vec, context: &TemplateContext) -> Result { 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) - } + 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} => { let mut negator = false; @@ -105,50 +105,59 @@ impl ParsedTemplate { negator = true; condition = condition[1..].to_string(); } - match TemplateContextGetter::get(context, &condition) { - Some(TemplateValue::Bool(value)) => { + + 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(()) }, - _ => panic!("{} is not a boolean value", condition) - } + _ => Err(Error::new(Other, format!("{} is not a boolean value", condition))), + }?; }, Token::IteratorDirective { collection, member_label, children } => { - match TemplateContextGetter::get(context, &collection) { - Some(TemplateValue::Collection(collection)) => { + let value = TemplateContextGetter::get(context, &collection) + .ok_or_else(|| Error::new(Other, format!("{} is not a valid key", collection)))?; + + match value { + 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)) + rendered_template.push_str(&ParsedTemplate::render_tokens(&children, &child_context)?) } + Ok(()) }, - _ => panic!("{} is not a collection", collection) - } + _ => Err(Error::new(Other, format!("{} is not a collection", collection))), + }?; } } } - rendered_template + Ok(rendered_template) } } -pub fn parse(template: &str) -> ParsedTemplate { +pub fn parse(template: &str) -> Option { let mut tokens = Vec::new(); - tokenize(template, &mut tokens); - ParsedTemplate { + tokenize(template, &mut tokens).ok()?; + Some(ParsedTemplate { tokens - } + }) } -fn tokenize(template: &str, tokens: &mut Vec) { +fn tokenize(template: &str, tokens: &mut Vec) -> Result<()> { let mut remaining_template = template; while !remaining_template.is_empty() && remaining_template.contains("{{") { let directive_start_index = remaining_template.find("{{") - .expect("Was expecting at least one tag opener"); + .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(); tokens.push(Token::Text(text.to_string())); @@ -156,7 +165,7 @@ fn tokenize(template: &str, tokens: &mut Vec) { remaining_template = &remaining_template[directive_start_index..]; let directive_end_index = remaining_template.find("}}") - .expect("Was expecting }} after {{") + 2; + .ok_or_else(|| Error::new(Other, "Was expecting }} after {{"))? + 2; let directive = &remaining_template[..directive_end_index]; remaining_template = &remaining_template[directive_end_index..]; @@ -179,7 +188,7 @@ fn tokenize(template: &str, tokens: &mut Vec) { let closing_block = remaining_template.find("{{?}}").unwrap(); let directive_block = &remaining_template[..closing_block]; remaining_template = &remaining_template[closing_block + 5..]; - tokenize(directive_block, &mut children); + tokenize(directive_block, &mut children)?; tokens.push(Token::ConditionalDirective{ condition: content.to_string(), children @@ -190,7 +199,7 @@ fn tokenize(template: &str, tokens: &mut Vec) { let closing_block = remaining_template.find("{{~}}").unwrap(); let directive_block = &remaining_template[..closing_block]; remaining_template = &remaining_template[closing_block + 5..]; - tokenize(directive_block, &mut children); + tokenize(directive_block, &mut children)?; if parts.len() == 2 { tokens.push(Token::IteratorDirective { collection: parts[0].trim().to_string(), @@ -206,6 +215,7 @@ fn tokenize(template: &str, tokens: &mut Vec) { } } tokens.push(Token::Text(remaining_template.to_string())); + Ok(()) } // File helpers.