Skip to content

Commit

Permalink
Implementing filters for capitalize, downcase, first, last, prepend, …
Browse files Browse the repository at this point in the history
…append and pluralize (#18)
  • Loading branch information
gnunicorn authored and Johann Hofmann committed May 15, 2016
1 parent 0a7643d commit 9e8bc53
Show file tree
Hide file tree
Showing 3 changed files with 290 additions and 1 deletion.
142 changes: 142 additions & 0 deletions src/filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,46 @@ pub fn upcase(input: &Value, _args: &[Value]) -> FilterResult {
}
}


pub fn downcase(input: &Value, _args: &[Value]) -> FilterResult {
match *input {
Str(ref s) => Ok(Str(s.to_lowercase())),
_ => Err(InvalidType("String expected".to_owned())),
}
}


pub fn capitalize(input: &Value, _args: &[Value]) -> FilterResult {
match *input {
Str(ref s) => Ok(Str(s.char_indices().fold(String::new(), |word, (_, chr)| {
let next_char = match word.chars().last() {
Some(last) =>
if last.is_whitespace() {
chr.to_uppercase().next().unwrap()
} else {
chr
},
_ => chr.to_uppercase().next().unwrap(),
}.to_string();
word + &next_char
}))),
_ => Err(InvalidType("String expected".to_owned())),
}
}


pub fn pluralize(input: &Value, args: &[Value]) -> FilterResult {

if args.len() != 2 {
return Err(InvalidArgumentCount(format!("expected 2, {} given", args.len())));
}
match *input {
Num(1f32) => Ok(args[0].clone()),
Num(_) => Ok(args[1].clone()),
_ => Err(InvalidType("Number expected".to_owned())),
}
}

pub fn minus(input: &Value, args: &[Value]) -> FilterResult {

let num = match *input {
Expand Down Expand Up @@ -148,6 +188,50 @@ pub fn replace(input: &Value, args: &[Value]) -> FilterResult {
}
}

pub fn prepend(input: &Value, args: &[Value]) -> FilterResult {
match *input {
Str(ref x) => match args.first() {
Some(&Str(ref a)) => Ok(Str(format!("{}{}", a, x))),
_ => return Err(InvalidArgument(0, "Str expected".to_owned())),
},
_ => Err(InvalidType("String expected".to_owned())),
}
}


pub fn append(input: &Value, args: &[Value]) -> FilterResult {
match *input {
Str(ref x) => match args.first() {
Some(&Str(ref a)) => Ok(Str(format!("{}{}", x, a))),
_ => return Err(InvalidArgument(0, "Str expected".to_owned())),
},
_ => Err(InvalidType("String expected".to_owned())),
}
}

pub fn first(input: &Value, _args: &[Value]) -> FilterResult {
match *input {
Str(ref x) => match x.chars().next() {
Some(c) => Ok(Str(c.to_string())),
_ => Ok(Str("".to_owned()))
},
Array(ref x) => Ok(x.first().unwrap_or(&Str("".to_owned())).to_owned()),
_ => Err(InvalidType("String or Array expected".to_owned())),
}
}


pub fn last(input: &Value, _args: &[Value]) -> FilterResult {
match *input {
Str(ref x) => match x.chars().last() {
Some(c) => Ok(Str(c.to_string())),
_ => Ok(Str("".to_owned()))
},
Array(ref x) => Ok(x.last().unwrap_or(&Str("".to_owned())).to_owned()),
_ => Err(InvalidType("String or Array expected".to_owned())),
}
}

#[cfg(test)]
mod tests {

Expand All @@ -173,6 +257,7 @@ mod tests {
fn unit_size() {
assert_eq!(unit!(size, tos!("abc")), Num(3f32));
assert_eq!(unit!(size, tos!("this has 22 characters")), Num(22f32));
assert_eq!(unit!(size, Array(vec![Num(0f32), Num(1f32), Num(2f32), Num(3f32), Num(4f32)])), Num(5f32));
}

#[test]
Expand All @@ -182,6 +267,38 @@ mod tests {
tos!("HELLO WORLD 21"));
}

#[test]
fn unit_downcase() {
assert_eq!(unit!(downcase, tos!("Abc")), tos!("abc"));
assert_eq!(unit!(downcase, tos!("Hello World 21")),
tos!("hello world 21"));
}

#[test]
fn unit_capitalize() {
assert_eq!(unit!(capitalize, tos!("abc")), tos!("Abc"));
assert_eq!(unit!(capitalize, tos!("hello world 21")),
tos!("Hello World 21"));

// sure that Umlauts work
assert_eq!(unit!(capitalize, tos!("über ètat, y̆es?")),
tos!("Über Ètat, Y\u{306}es?"));

// Weird UTF-8 White space is kept – this is a no-break whitespace!
assert_eq!(unit!(capitalize, tos!("hello world​")),
tos!("Hello World​"));

}

#[test]
fn unit_pluralize() {
assert_eq!(unit!(pluralize, Num(1f32), &[tos!("one"), tos!("many")]),
tos!("one"));

assert_eq!(unit!(pluralize, Num(2f32), &[tos!("one"), tos!("many")]),
tos!("many"));
}

#[test]
fn unit_minus() {
assert_eq!(unit!(minus, Num(2f32), &[Num(1f32)]), Num(1f32));
Expand Down Expand Up @@ -241,4 +358,29 @@ mod tests {
tos!("foofoo"));
}

#[test]
fn unit_prepend() {
assert_eq!(unit!(prepend, tos!("barbar"), &[tos!("foo")]),
tos!("foobarbar"));
}

#[test]
fn unit_append() {
assert_eq!(unit!(append, tos!("sam"), &[tos!("son")]),
tos!("samson"));
}

#[test]
fn unit_first() {
assert_eq!(unit!(first, Array(vec![Num(0f32), Num(1f32), Num(2f32), Num(3f32), Num(4f32)])), Num(0f32));
assert_eq!(unit!(first, Array(vec![tos!("test"), tos!("two")])), tos!("test"));
assert_eq!(unit!(first, Array(vec![])), tos!(""));
}

#[test]
fn unit_last() {
assert_eq!(unit!(last, Array(vec![Num(0f32), Num(1f32), Num(2f32), Num(3f32), Num(4f32)])), Num(4f32));
assert_eq!(unit!(last, Array(vec![tos!("test"), tos!("last")])), tos!("last"));
assert_eq!(unit!(last, Array(vec![])), tos!(""));
}
}
10 changes: 9 additions & 1 deletion src/template.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use Renderable;
use context::Context;
use filters::{size, upcase, minus, plus, replace, times, divided_by, ceil, floor, round};
use filters::{size, upcase, downcase, capitalize, minus, plus, times, divided_by, ceil, floor, round, prepend, append, first, last, pluralize, replace};
use error::Result;

pub struct Template {
Expand All @@ -9,16 +9,24 @@ pub struct Template {

impl Renderable for Template {
fn render(&self, context: &mut Context) -> Result<Option<String>> {

context.add_filter("size", Box::new(size));
context.add_filter("upcase", Box::new(upcase));
context.add_filter("downcase", Box::new(downcase));
context.add_filter("capitalize", Box::new(capitalize));
context.add_filter("minus", Box::new(minus));
context.add_filter("plus", Box::new(plus));
context.add_filter("times", Box::new(times));
context.add_filter("divided_by", Box::new(divided_by));
context.add_filter("ceil", Box::new(ceil));
context.add_filter("floor", Box::new(floor));
context.add_filter("round", Box::new(round));
context.add_filter("first", Box::new(first));
context.add_filter("last", Box::new(last));
context.add_filter("prepend", Box::new(prepend));
context.add_filter("append", Box::new(append));
context.add_filter("replace", Box::new(replace));
context.add_filter("pluralize", Box::new(pluralize));

let mut buf = String::new();
for el in &self.elements {
Expand Down
139 changes: 139 additions & 0 deletions tests/filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,61 @@ pub fn upcase() {
assert_eq!(output.unwrap(), Some("HELLO".to_string()));
}


#[test]
pub fn downcase() {
let text = "{{ text | downcase}}";
let options : LiquidOptions = Default::default();
let template = parse(&text, options).unwrap();

let mut data = Context::new();
data.set_val("text", Value::Str("HELLO tHeRe".to_string()));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("hello there".to_string()));
}


#[test]
pub fn capitalize() {
let text = "{{ text | capitalize}}";
let options : LiquidOptions = Default::default();
let template = parse(&text, options).unwrap();

let mut data = Context::new();
data.set_val("text", Value::Str("hello world".to_string()));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("Hello World".to_string()));
}


#[test]
pub fn pluralize() {
let text = "{{ count | pluralize: 'one', 'many'}}";
let options : LiquidOptions = Default::default();
let template = parse(&text, options).unwrap();

let mut data = Context::new();
data.set_val("count", Value::Num(1f32));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("one".to_string()));


let mut data = Context::new();
data.set_val("count", Value::Num(0f32));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("many".to_string()));

let mut data = Context::new();
data.set_val("count", Value::Num(10f32));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("many".to_string()));
}

#[test]
pub fn minus() {
let text = "{{ num | minus : 2 }}";
Expand Down Expand Up @@ -60,6 +115,62 @@ pub fn minus_error() {
assert!(output.is_err());
}

#[test]
pub fn first() {
let text = "{{ nums | first }}";
let options : LiquidOptions = Default::default();
let template = parse(&text, options).unwrap();

// array of numbers
let mut data = Context::new();
data.set_val("nums", Value::Array(vec![Value::Num(12f32), Value::Num(1f32)]));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("12".to_string()));

// array of strings
let mut data = Context::new();
data.set_val("nums", Value::Array(vec![Value::Str("first".to_owned())]));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("first".to_string()));

let mut data = Context::new();
data.set_val("nums", Value::Str("first".to_owned()));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("f".to_string()));
}


#[test]
pub fn last() {
let text = "{{ list | last }}";
let options : LiquidOptions = Default::default();
let template = parse(&text, options).unwrap();

// array of numbers
let mut data = Context::new();
data.set_val("list", Value::Array(vec![Value::Num(12f32), Value::Num(100f32)]));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("100".to_string()));

// array of strings
let mut data = Context::new();
data.set_val("list", Value::Array(vec![Value::Str("first".to_owned()), Value::Str("second".to_owned())]));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("second".to_string()));

let mut data = Context::new();
data.set_val("list", Value::Str("last".to_owned()));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("t".to_string()));
}


#[test]
pub fn replace() {
let text = "{{ text | replace: 'bar', 'foo' }}";
Expand All @@ -72,3 +183,31 @@ pub fn replace() {
let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("foo2foo".to_string()));
}


#[test]
pub fn prepend() {
let text = "{{ text | prepend: 'fifo' }}";
let options : LiquidOptions = Default::default();
let template = parse(&text, options).unwrap();

let mut data = Context::new();
data.set_val("text", Value::Str("bar2bar".to_string()));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("fifobar2bar".to_string()));
}


#[test]
pub fn append() {
let text = "{{ text | append: 'lifo' }}";
let options : LiquidOptions = Default::default();
let template = parse(&text, options).unwrap();

let mut data = Context::new();
data.set_val("text", Value::Str("roobarb".to_string()));

let output = template.render(&mut data);
assert_eq!(output.unwrap(), Some("roobarblifo".to_string()));
}

0 comments on commit 9e8bc53

Please sign in to comment.