extern crate serde;
extern crate serde_json;
#[cfg(test)]
#[cfg_attr(test, macro_use)]
extern crate serde_derive;
mod compiler;
pub mod error;
mod instruction;
pub mod syntax;
mod template;
use error::*;
use serde::Serialize;
use serde_json::Value;
use std::collections::HashMap;
use std::fmt::Write;
use template::Template;
pub type ValueFormatter = Fn(&Value, &mut String) -> Result<()>;
pub fn escape(value: &str, output: &mut String) {
let value_str = value;
let mut last_emitted = 0;
for (i, ch) in value.bytes().enumerate() {
match ch as char {
'<' | '>' | '&' | '\'' | '"' => {
output.push_str(&value_str[last_emitted..i]);
let s = match ch as char {
'>' => ">",
'<' => "<",
'&' => "&",
'\'' => "'",
'"' => """,
_ => unreachable!(),
};
output.push_str(s);
last_emitted = i + 1;
}
_ => {}
}
}
if last_emitted < value_str.len() {
output.push_str(&value_str[last_emitted..]);
}
}
pub fn format(value: &Value, output: &mut String) -> Result<()> {
match value {
Value::Null => Ok(()),
Value::Bool(b) => {
write!(output, "{}", b)?;
Ok(())
}
Value::Number(n) => {
write!(output, "{}", n)?;
Ok(())
}
Value::String(s) => {
escape(s, output);
Ok(())
}
_ => Err(unprintable_error()),
}
}
pub fn format_unescaped(value: &Value, output: &mut String) -> Result<()> {
match value {
Value::Null => Ok(()),
Value::Bool(b) => {
write!(output, "{}", b)?;
Ok(())
}
Value::Number(n) => {
write!(output, "{}", n)?;
Ok(())
}
Value::String(s) => {
output.push_str(s);
Ok(())
}
_ => Err(unprintable_error()),
}
}
pub struct TinyTemplate<'template> {
templates: HashMap<&'template str, Template<'template>>,
formatters: HashMap<&'template str, Box<ValueFormatter>>,
}
impl<'template> TinyTemplate<'template> {
pub fn new() -> TinyTemplate<'template> {
let mut tt = TinyTemplate {
templates: HashMap::default(),
formatters: HashMap::default(),
};
tt.add_formatter("unescaped", format_unescaped);
tt
}
pub fn add_template(&mut self, name: &'template str, text: &'template str) -> Result<()> {
let template = Template::compile(text)?;
self.templates.insert(name, template);
Ok(())
}
pub fn add_formatter<F>(&mut self, name: &'template str, formatter: F)
where
F: 'static + Fn(&Value, &mut String) -> Result<()>,
{
self.formatters.insert(name, Box::new(formatter));
}
pub fn render<C>(&self, template: &str, context: &C) -> Result<String>
where
C: Serialize,
{
let value = serde_json::to_value(context)?;
match self.templates.get(template) {
Some(tmpl) => tmpl.render(&value, &self.templates, &self.formatters),
None => Err(Error::GenericError {
msg: format!("Unknown template '{}'", template),
}),
}
}
}
impl<'template> Default for TinyTemplate<'template> {
fn default() -> TinyTemplate<'template> {
TinyTemplate::new()
}
}