]> git.r.bdr.sh - rbdr/blog/blobdiff - src/template.rs
Improve the error handling
[rbdr/blog] / src / template.rs
index 03325063a94e45f5fe291f7396e17ac32c1879c0..0213fc3c0d5c849a68fa824c6ad36ecd54c16529 100644 (file)
@@ -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<String> {
         ParsedTemplate::render_tokens(&self.tokens, context)
     }
 
-    pub fn render_tokens(tokens: &Vec<Token>, context: &TemplateContext) -> String {
+    pub fn render_tokens(tokens: &Vec<Token>, context: &TemplateContext) -> Result<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)
-                    }
+                    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<ParsedTemplate> {
     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<Token>) {
+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("{{")
-            .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<Token>) {
         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<Token>) {
                         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<Token>) {
                         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<Token>) {
         }
     }
     tokens.push(Token::Text(remaining_template.to_string()));
+    Ok(())
 }
 
 // File helpers.