Repository: cobalt-org/liquid-rust
Pull request #76 (2017-02-25 15:04:34)

add default filter
More to come. It's not covered in #74 and missing in #11 (see my comment there).

Mimics behavior from the [shopify implementation](https://github.com/Shopify/liquid/blob/master/lib/liquid/standardfilters.rb#L356)

fiji-flo: resolved merge conflicts
johannhof: Cool, thanks!

Patch:
diff --git a/src/filters.rs b/src/filters.rs
index 5f4f2d5..daca1e1 100644
--- a/src/filters.rs
+++ b/src/filters.rs
@@ -3,7 +3,7 @@ use std::error::Error;
 use std::cmp::Ordering;
 
 use value::Value;
-use value::Value::{Array, Num, Object, Str};
+use value::Value::{Array, Bool, Num, Object, Str};
 
 use chrono::DateTime;
 
@@ -696,10 +696,25 @@ pub fn upcase(input: &Value, _args: &[Value]) -> FilterResult {
     }
 }
 
+pub fn default(input: &Value, args: &[Value]) -> FilterResult {
+    try!(check_args_len(args, 1));
+    if match *input {
+        Str(ref s) => s.is_empty(),
+        Object(ref o) => o.is_empty(),
+        Array(ref a) => a.is_empty(),
+        Bool(b) => !b,
+        Num(_) => false,
+    } {
+        Ok(args[0].clone())
+    } else {
+        Ok(input.clone())
+    }
+}
+
 #[cfg(test)]
 mod tests {
 
-    use value::Value::*;
+    use std::collections::HashMap;
     use super::*;
 
     macro_rules! unit {
@@ -1496,4 +1511,18 @@ mod tests {
         assert_eq!(unit!(upcase, tos!("Hello World 21")),
                    tos!("HELLO WORLD 21"));
     }
+
+    #[test]
+    fn unit_default() {
+        assert_eq!(unit!(default, tos!(""), &[tos!("bar")]), tos!("bar"));
+        assert_eq!(unit!(default, tos!("foo"), &[tos!("bar")]), tos!("foo"));
+        assert_eq!(unit!(default, Num(0_f32), &[tos!("bar")]), Num(0_f32));
+        assert_eq!(unit!(default, Array(vec![]), &[Num(1_f32)]), Num(1_f32));
+        assert_eq!(unit!(default, Array(vec![tos!("")]), &[Num(1_f32)]),
+                   Array(vec![tos!("")]));
+        assert_eq!(unit!(default, Object(HashMap::new()), &[Num(1_f32)]),
+                   Num(1_f32));
+        assert_eq!(unit!(default, Bool(false), &[Num(1_f32)]), Num(1_f32));
+        assert_eq!(unit!(default, Bool(true), &[Num(1_f32)]), Bool(true));
+    }
 }
diff --git a/src/template.rs b/src/template.rs
index 61c0ca4..a0e0cb4 100644
--- a/src/template.rs
+++ b/src/template.rs
@@ -1,10 +1,10 @@
 use Renderable;
 use context::Context;
-use filters::{abs, append, capitalize, ceil, date, divided_by, downcase, escape, escape_once,
-              first, floor, join, last, lstrip, minus, modulo, newline_to_br, pluralize, plus,
-              prepend, remove, remove_first, replace, replace_first, reverse, round, rstrip, size,
-              slice, sort, split, strip, strip_html, strip_newlines, times, truncate,
-              truncatewords, uniq, upcase};
+use filters::{abs, append, capitalize, ceil, date, default, divided_by, downcase, escape,
+              escape_once, first, floor, join, last, lstrip, minus, modulo, newline_to_br,
+              pluralize, plus, prepend, remove, remove_first, replace, replace_first, reverse,
+              round, rstrip, size, slice, sort, split, strip, strip_html, strip_newlines, times,
+              truncate, truncatewords, uniq, upcase};
 use error::Result;
 
 pub struct Template {
@@ -19,6 +19,7 @@ impl Renderable for Template {
         context.maybe_add_filter("capitalize", Box::new(capitalize));
         context.maybe_add_filter("ceil", Box::new(ceil));
         context.maybe_add_filter("date", Box::new(date));
+        context.maybe_add_filter("default", Box::new(default));
         context.maybe_add_filter("divided_by", Box::new(divided_by));
         context.maybe_add_filter("downcase", Box::new(downcase));
         context.maybe_add_filter("escape", Box::new(escape));


Test patch:
diff --git a/tests/filters.rs b/tests/filters.rs
index 8290a1e..70c52f7 100644
--- a/tests/filters.rs
+++ b/tests/filters.rs
@@ -458,3 +458,20 @@ pub fn truncatewords() {
     let output = template.render(&mut data);
     assert_eq!(output.unwrap(), Some("first...".to_string()));
 }
+
+#[test]
+pub fn default() {
+    let text = "{{ text | default: 'bar' }}";
+    let options: LiquidOptions = Default::default();
+    let template = parse(text, options).unwrap();
+
+    let mut data = Context::new();
+
+    let output = template.render(&mut data);
+    assert_eq!(output.unwrap(), Some("bar".to_string()));
+
+
+    data.set_val("text", Value::Str("foo".to_string()));
+    let output = template.render(&mut data);
+    assert_eq!(output.unwrap(), Some("foo".to_string()));
+}