use std::collections::HashMap;
use std::fmt;
use std::ops::{Deref,DerefMut};
#[derive(Clone,PartialEq,Debug)]
pub struct Template<'a>(Pieces<'a>);
impl<'a> Deref for Template<'a> {
type Target = Vec<Piece<'a>>;
fn deref(&self) -> &Self::Target {
&(self.0).0
}
}
impl<'a> DerefMut for Template<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut (self.0).0
}
}
impl<'a> fmt::Display for Template<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl<'a> Template<'a> {
pub fn with_pieces(v: Vec<Piece<'a>>) -> Self {
Template(Pieces(v))
}
pub fn fill_in(&'a self, lookup_tbl: &HashMap<&'a str, &'a str>) -> Text<'a> {
let mut t = Text::new();
for v in self.0.iter() {
match v {
Piece::Text(s) => t.push(s),
Piece::Placeholder{name, before, after} => {
let entry = lookup_tbl.get(name);
match entry {
Some(value) if value.len() > 0 => {
if before.len() > 0 { t.push(before) };
t.push(value);
if after.len() > 0 { t.push(after) };
}
_ => {}
}
}
}
}
t
}
pub fn try_fill_in(&'a self, lookup_tbl: &HashMap<&'a str, &'a str>) -> Result<Text<'a>, TemplateError> {
let mut t = Text::new();
for v in self.0.iter() {
match v {
Piece::Text(s) => t.push(s),
Piece::Placeholder{name, before, after} => {
let entry = lookup_tbl.get(name);
match entry {
Some(value) => {
if before.len() > 0 { t.push(before) };
t.push(value);
if after.len() > 0 { t.push(after) };
},
None => { return Err(TemplateError) }
}
}
}
}
Ok(t)
}
}
impl<'a> From<&'a str> for Template<'a> {
fn from(s: &'a str) -> Self {
enum State{InText, InPlaceholder};
let mut v: Vec<Piece> = vec![];
let mut state = State::InText;
let mut rest = s;
while rest.len() > 0 {
match state {
State::InText => {
if let Some(idx) = rest.find("${") {
v.push(Piece::Text(&rest[..idx]));
rest = &rest[idx+2..];
state = State::InPlaceholder;
} else {
v.push(Piece::Text(rest));
rest = &rest[0..0];
}
}
State::InPlaceholder => {
if let Some(idx) = rest.find("}") {
let (before, name, after) = trim_split(&rest[..idx]);
v.push(Piece::Placeholder{name, before, after});
rest = &rest[idx+1..];
state = State::InText;
} else {
v.push(Piece::Text(rest));
rest = &rest[0..0];
}
}
}
}
Template::with_pieces(v)
}
}
#[derive(Clone,PartialEq,Debug)]
struct Pieces<'a>(Vec<Piece<'a>>);
impl<'a> Pieces<'a> {
}
impl<'a> Deref for Pieces<'a> {
type Target = Vec<Piece<'a>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> fmt::Display for Pieces<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.iter().map(|v| v.fmt(f) ).collect()
}
}
#[derive(Clone,PartialEq,Debug)]
pub enum Piece<'a> {
Text(&'a str),
Placeholder{name: &'a str, before: &'a str, after: &'a str}
}
impl<'a> fmt::Display for Piece<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Piece::Text(s) => { write!(f, "{}", s) },
Piece::Placeholder{name, before, after} => {
write!(f, "${{{}{}{}}}", before, name, after)
}
}
}
}
#[derive(Debug)]
pub struct Text<'a>(Vec<&'a str>);
impl<'a> Deref for Text<'a> {
type Target = Vec<&'a str>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> DerefMut for Text<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'a> Text<'a> {
fn new() -> Self {
Text(Vec::new())
}
}
impl<'a> fmt::Display for Text<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.iter().map(|v| v.fmt(f) ).collect()
}
}
#[derive(Debug)]
pub struct TemplateError;
fn trim_split
(s: &str) -> (&str, &str, &str) {
let mut name = s;
let before = if let Some((last,_)) = s.chars().enumerate().take_while(|(_,v)| v.is_whitespace()).last() {
name = &name[last+1..];
&s[..last+1]
} else {
""
};
let after = if let Some((first,_)) = name.chars().rev().enumerate().take_while(|(_,v)| v.is_whitespace()).last() {
let res = &name[name.len() - first - 1..];
name = &name[..name.len() - first - 1];
res
} else {
""
};
(before, name, after)
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[test]
fn split() {
assert_eq!(trim_split("Hallo"), ("", "Hallo", ""));
assert_eq!(trim_split(" Hallo"), (" ", "Hallo", ""));
assert_eq!(trim_split("Hallo "), ("", "Hallo", " "));
assert_eq!(trim_split(" Hallo "), (" ", "Hallo", " "));
}
#[test]
fn from() {
assert_eq!(*Template::from(""), vec![]);
assert_eq!(*Template::from("{"), vec![Piece::Text("{")]);
assert_eq!(*Template::from("}"), vec![Piece::Text("}")]);
}
#[test]
fn to_string() {
assert_eq!(Template::from("").to_string(), "");
assert_eq!(Template::from("{").to_string(), "{");
assert_eq!(Template::from("}").to_string(), "}");
assert_eq!(Template::from("${}").to_string(), "${}");
assert_eq!(Template::from("${x}").to_string(), "${x}");
assert_eq!(Template::from(" ${x}").to_string(), " ${x}");
assert_eq!(Template::from("${x} ").to_string(), "${x} ");
assert_eq!(Template::from("${x }").to_string(), "${x }");
assert_eq!(Template::from("${ x}").to_string(), "${ x}");
assert_eq!(Template::from("${ x }").to_string(), "${ x }");
assert_eq!(Template::from("Hallo ${name}").to_string(), "Hallo ${name}");
}
#[test]
fn fill_in() {
let mut dict = HashMap::new();
dict.insert("k", "v");
dict.insert("l", "");
assert_eq!(Template::from("${}").fill_in(&dict).to_string(), "");
assert_eq!(Template::from("${k}").fill_in(&dict).to_string(), "v");
assert_eq!(Template::from("${ k }").fill_in(&dict).to_string(), " v ");
assert_eq!(Template::from("${l}").fill_in(&dict).to_string(), "");
assert_eq!(Template::from("${ l }").fill_in(&dict).to_string(), "");
}
}