aboutsummaryrefslogtreecommitdiff
path: root/src/template.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/template.rs')
-rw-r--r--src/template.rs157
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,
}
}