mod parse;
use std::{borrow::Borrow, collections::BTreeMap};
use parse::Token;
pub trait TemplateInput {
fn get(&self, key: &str) -> Option<String>;
}
impl<K: Borrow<str> + Ord, V: ToString> TemplateInput for BTreeMap<K, V> {
fn get(&self, key: &str) -> Option<String> {
self.get(key).map(|v| v.to_string())
}
}
#[derive(Debug, Clone)]
enum Command {
String(String),
Stache(String),
}
#[derive(Debug, Clone)]
pub struct Template {
commands: Vec<Command>,
}
impl Template {
pub fn parse(source: impl AsRef<str>) -> Template {
let parsed = parse::parse(source.as_ref());
Template {
commands: parsed
.into_iter()
.map(|t| match t {
Token::Str(s) => Command::String(s.to_owned()),
Token::Stache(s) => Command::Stache(s.to_owned()),
})
.collect(),
}
}
pub fn render(&self, inputs: &impl TemplateInput) -> String {
let mut output = String::new();
for command in &self.commands {
match command {
Command::String(s) => {
output.push_str(s);
}
Command::Stache(key) => {
if let Some(value) = inputs.get(key) {
output.push_str(&value);
} else {
output.push_str("{{");
output.push_str(key);
output.push_str("}}");
}
}
}
}
output
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn template_works() {
let tpl = Template::parse("hewo! you are {{meow}}");
struct Input;
impl TemplateInput for Input {
fn get(&self, _key: &str) -> Option<String> {
Some("gay".to_owned())
}
}
assert_eq!(tpl.render(&Input), "hewo! you are gay");
}
#[test]
fn template_missing_works() {
let tpl = Template::parse("hewo! you are {{meow}}");
struct Input;
impl TemplateInput for Input {
fn get(&self, _key: &str) -> Option<String> {
None
}
}
assert_eq!(tpl.render(&Input), "hewo! you are {{meow}}");
}
}