Skip to content

Commit

Permalink
feat(filters): array manipulation filters
Browse files Browse the repository at this point in the history
Implemented some jekyll filters related to arrays:
- push
- pop
- unshift
- shift
- array_to_sentence_string
  • Loading branch information
Goncalerta committed Oct 30, 2018
1 parent 7bc6315 commit 94e6660
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 0 deletions.
219 changes: 219 additions & 0 deletions src/filters/array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
use liquid_value::Value;

use super::{check_args_len, invalid_input};
use interpreter::FilterResult;
use std::fmt::Write;

/// Receives a `Value::Array` as an input.
/// Returns a copy of the input with the given value appended at the end.
pub fn push(input: &Value, args: &[Value]) -> FilterResult {
check_args_len(args, 1, 0)?;

let value = args[0].clone();
let mut array = input
.as_array()
.ok_or_else(|| invalid_input("Array expected"))?
.clone();
array.push(value);

Ok(Value::array(array))
}

/// Receives a `Value::Array` as an input.
/// Returns a copy of the input with the last element removed.
pub fn pop(input: &Value, args: &[Value]) -> FilterResult {
check_args_len(args, 0, 0)?;

let mut array = input
.as_array()
.ok_or_else(|| invalid_input("Array expected"))?
.clone();
array.pop();

Ok(Value::array(array))
}

/// Receives a `Value::Array` as an input.
/// Returns a copy of the input with the given value appended at the beginning.
pub fn unshift(input: &Value, args: &[Value]) -> FilterResult {
check_args_len(args, 1, 0)?;

let value = args[0].clone();
let mut array = input
.as_array()
.ok_or_else(|| invalid_input("Array expected"))?
.clone();
array.insert(0, value);

Ok(Value::array(array))
}

/// Receives a `Value::Array` as an input.
/// Returns a copy of the input with the first element removed.
pub fn shift(input: &Value, args: &[Value]) -> FilterResult {
check_args_len(args, 0, 0)?;

let mut array = input
.as_array()
.ok_or_else(|| invalid_input("Array expected"))?
.clone();

if !array.is_empty() {
array.remove(0);
}

Ok(Value::array(array))
}

/// Convert an array into a sentence.
pub fn array_to_sentence_string(input: &Value, args: &[Value]) -> FilterResult {
check_args_len(args, 0, 1)?;

let connector = args.get(0).map(|arg| arg.to_str());
let connector = connector
.as_ref()
.map(|s| s.as_ref())
.unwrap_or_else(|| "and");

let mut array = input
.as_array()
.ok_or_else(|| invalid_input("Array expected"))?
.iter();

let mut sentence = array
.next()
.map(|v| v.to_string())
.unwrap_or_else(|| "".to_string());

let last = array.next_back();

for value in array {
write!(sentence, ", {}", value).expect("It should be safe to write to a string.");
}

if let Some(last) = last {
write!(sentence, ", {} {}", connector, last)
.expect("It should be safe to write to a string.");
}

Ok(Value::scalar(sentence))
}

#[cfg(test)]
mod tests {

use super::*;

macro_rules! unit {
($a:ident, $b:expr) => {{
unit!($a, $b, &[])
}};
($a:ident, $b:expr, $c:expr) => {{
$a(&$b, $c).unwrap()
}};
}

#[test]
fn unit_push() {
let input = Value::Array(vec![Value::scalar("Seattle"), Value::scalar("Tacoma")]);
let args = &[Value::scalar("Spokane")];
let desired_result = Value::Array(vec![
Value::scalar("Seattle"),
Value::scalar("Tacoma"),
Value::scalar("Spokane"),
]);
assert_eq!(unit!(push, input, args), desired_result);
}

#[test]
fn unit_pop() {
let input = Value::Array(vec![Value::scalar("Seattle"), Value::scalar("Tacoma")]);
let args = &[];
let desired_result = Value::Array(vec![Value::scalar("Seattle")]);
assert_eq!(unit!(pop, input, args), desired_result);
}

#[test]
fn unit_pop_empty() {
let input = Value::Array(vec![]);
let args = &[];
let desired_result = Value::Array(vec![]);
assert_eq!(unit!(pop, input, args), desired_result);
}

#[test]
fn unit_unshift() {
let input = Value::Array(vec![Value::scalar("Seattle"), Value::scalar("Tacoma")]);
let args = &[Value::scalar("Olympia")];
let desired_result = Value::Array(vec![
Value::scalar("Olympia"),
Value::scalar("Seattle"),
Value::scalar("Tacoma"),
]);
assert_eq!(unit!(unshift, input, args), desired_result);
}

#[test]
fn unit_shift() {
let input = Value::Array(vec![Value::scalar("Seattle"), Value::scalar("Tacoma")]);
let args = &[];
let desired_result = Value::Array(vec![Value::scalar("Tacoma")]);
assert_eq!(unit!(shift, input, args), desired_result);
}

#[test]
fn unit_shift_empty() {
let input = Value::Array(vec![]);
let args = &[];
let desired_result = Value::Array(vec![]);
assert_eq!(unit!(shift, input, args), desired_result);
}

#[test]
fn unit_array_to_sentence_string() {
let input = Value::Array(vec![
Value::scalar("foo"),
Value::scalar("bar"),
Value::scalar("baz"),
]);
let args = &[];
let desired_result = Value::scalar("foo, bar, and baz");
assert_eq!(unit!(array_to_sentence_string, input, args), desired_result);
}

#[test]
fn unit_array_to_sentence_string_two_elements() {
let input = Value::Array(vec![Value::scalar("foo"), Value::scalar("bar")]);
let args = &[];
let desired_result = Value::scalar("foo, and bar");
assert_eq!(unit!(array_to_sentence_string, input, args), desired_result);
}

#[test]
fn unit_array_to_sentence_string_one_element() {
let input = Value::Array(vec![Value::scalar("foo")]);
let args = &[];
let desired_result = Value::scalar("foo");
assert_eq!(unit!(array_to_sentence_string, input, args), desired_result);
}

#[test]
fn unit_array_to_sentence_string_no_elements() {
let input = Value::Array(vec![]);
let args = &[];
let desired_result = Value::scalar("");
assert_eq!(unit!(array_to_sentence_string, input, args), desired_result);
}

#[test]
fn unit_array_to_sentence_string_custom_connector() {
let input = Value::Array(vec![
Value::scalar("foo"),
Value::scalar("bar"),
Value::scalar("baz"),
]);
let args = &[Value::scalar("or")];
let desired_result = Value::scalar("foo, bar, or baz");
assert_eq!(unit!(array_to_sentence_string, input, args), desired_result);
}
}
4 changes: 4 additions & 0 deletions src/filters/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#[cfg(feature = "extra-filters")]
mod array;
mod date;
mod html;
mod math;
mod url;

#[cfg(feature = "extra-filters")]
pub use self::array::{array_to_sentence_string, pop, push, shift, unshift};
pub use self::date::date;
#[cfg(feature = "extra-filters")]
pub use self::date::date_in_tz;
Expand Down
7 changes: 7 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ impl ParserBuilder {
).filter(
"date_in_tz",
filters::date_in_tz as interpreter::FnFilterValue,
).filter("push", filters::push as interpreter::FnFilterValue)
.filter("pop", filters::pop as interpreter::FnFilterValue)
.filter("unshift", filters::unshift as interpreter::FnFilterValue)
.filter("shift", filters::shift as interpreter::FnFilterValue)
.filter(
"array_to_sentence_string",
filters::array_to_sentence_string as interpreter::FnFilterValue,
)
}

Expand Down

0 comments on commit 94e6660

Please sign in to comment.