handbar 0.0.5

Fork of Handlebars.
//! Helpers for boolean operations
use serde_json::Value as Json;

use crate::json::value::JsonTruthy;

handbar_helper!(eq: |x: Json, y: Json| x == y);
handbar_helper!(ne: |x: Json, y: Json| x != y);
handbar_helper!(gt: |x: i64, y: i64| x > y);
handbar_helper!(gte: |x: i64, y: i64| x >= y);
handbar_helper!(lt: |x: i64, y: i64| x < y);
handbar_helper!(lte: |x: i64, y: i64| x <= y);
handbar_helper!(and: |x: Json, y: Json| x.is_truthy(false) && y.is_truthy(false));
handbar_helper!(or: |x: Json, y: Json| x.is_truthy(false) || y.is_truthy(false));
handbar_helper!(not: |x: Json| !x.is_truthy(false));
handbar_helper!(exists: |x: Json| !x.is_null());
handbar_helper!(not_exists: |x: Json| x.is_null());
handbar_helper!(len: |x: Json| {
    match x {
        Json::Array(a) => a.len(),
        Json::Object(m) => m.len(),
        Json::String(s) => s.len(),
        _ => 0
    }
});
handbar_helper!(default: |x: Json, y: Json| {
    match x {
        Json::Null => y.to_owned(),
        _ => x.to_owned(),
    }
});
handbar_helper!(starts_with: |x: Json, y: Json| {
    match (x, y) {
        (Json::String(x), Json::String(y)) => x.starts_with(y),
        (Json::Array(x), y) => match x.first() {
            Some(x) => x == y,
            None => false,
        },
        _ => false,
    }
});
handbar_helper!(ends_with: |x: Json, y: Json| {
    match (x, y) {
        (Json::String(x), Json::String(y)) => x.ends_with(y),
        (Json::Array(x), y) => match x.last() {
            Some(x) => x == y,
            None => false,
        },
        _ => false,
    }
});
handbar_helper!(contains: |x: Json, y: Json| {
    match (x, y) {
        (Json::String(x), Json::String(y)) => x.contains(y),
        (Json::Array(x), y) => x.contains(y),
        (Json::Object(x), Json::String(y)) => x.contains_key(y),
        _ => false,
    }
});
handbar_helper!(env: |name: String| {
    match std::env::var(name) {
        Ok(val) => Json::String(val),
        _ => Json::Null,
    }
});
handbar_helper!(split: |text: String, by: String| {
    let mut list: Vec<Json> = Vec::new();
    for part in text.split(&by) {
        list.push(Json::String(part.to_string()));
    }

    Json::Array(list)
});

#[cfg(test)]
mod test_conditions {
    fn test_condition(condition: &str, expected: bool) {
        let handbar = crate::Handbar::new();

        let result = handbar
            .render_template(
                &format!(
                    "{{{{#if {condition}}}}}lorem{{{{else}}}}ipsum{{{{/if}}}}",
                    condition = condition
                ),
                &json!({}),
            )
            .unwrap();
        assert_eq!(&result, if expected { "lorem" } else { "ipsum" });
    }

    #[test]
    fn foo() {
        test_condition("(gt 5 3)", true);
        test_condition("(gt 3 5)", false);
        test_condition("(or (gt 3 5) (gt 5 3))", true);
        test_condition("(not [])", true);
        test_condition("(and null 4)", false);
        test_condition("(exists not_existed_value)", false);
        test_condition("(exists null)", false);
        test_condition("(exists 5)", true);
    }

    #[test]
    fn test_eq() {
        test_condition("(eq 5 5)", true);
        test_condition("(eq 5 6)", false);
        test_condition(r#"(eq "foo" "foo")"#, true);
        test_condition(r#"(eq "foo" "Foo")"#, false);
        test_condition(r#"(eq [5] [5])"#, true);
        test_condition(r#"(eq [5] [4])"#, false);
        test_condition(r#"(eq 5 "5")"#, false);
        test_condition(r#"(eq 5 [5])"#, false);
    }

    #[test]
    fn test_ne() {
        test_condition("(ne 5 6)", true);
        test_condition("(ne 5 5)", false);
        test_condition(r#"(ne "foo" "foo")"#, false);
        test_condition(r#"(ne "foo" "Foo")"#, true);
    }

    #[test]
    fn nested_conditions() {
        let handbar = crate::Handbar::new();

        let result = handbar
            .render_template("{{#if (gt 5 3)}}lorem{{else}}ipsum{{/if}}", &json!({}))
            .unwrap();
        assert_eq!(&result, "lorem");

        let result = handbar
            .render_template(
                "{{#if (not (gt 5 3))}}lorem{{else}}ipsum{{/if}}",
                &json!({}),
            )
            .unwrap();
        assert_eq!(&result, "ipsum");
    }

    #[test]
    fn test_len() {
        let handbar = crate::Handbar::new();

        let result = handbar
            .render_template("{{len value}}", &json!({"value": [1,2,3]}))
            .unwrap();
        assert_eq!(&result, "3");

        let result = handbar
            .render_template("{{len value}}", &json!({"value": {"a" :1, "b": 2}}))
            .unwrap();
        assert_eq!(&result, "2");

        let result = handbar
            .render_template("{{len value}}", &json!({"value": "tomcat"}))
            .unwrap();
        assert_eq!(&result, "6");

        let result = handbar
            .render_template("{{len value}}", &json!({"value": 3}))
            .unwrap();
        assert_eq!(&result, "0");
    }

    #[test]
    fn test_exists() {
        test_condition("(exists not_existed_value)", false);
        test_condition("(exists null)", false);
        test_condition("(exists 5)", true);
        test_condition(r#"(exists "foo")"#, true);
        test_condition(r#"(exists [])"#, true);
        test_condition(r#"(exists {})"#, true);
    }

    #[test]
    fn test_not_exists() {
        test_condition("(not_exists not_existed_value)", true);
        test_condition("(not_exists null)", true);
        test_condition("(not_exists 5)", false);
        test_condition(r#"(not_exists "foo")"#, false);
        test_condition(r#"(not_exists [])"#, false);
        test_condition(r#"(not_exists {})"#, false);
    }

    #[test]
    fn test_starts_with() {
        test_condition("(starts_with [5, 6] 5)", true);
        test_condition("(starts_with [5, 6] 6)", false);
        test_condition(r#"(starts_with "foo" "fo")"#, true);
        test_condition(r#"(starts_with "foo" "")"#, true);
        test_condition(r#"(starts_with "foo" "oo")"#, false);
        test_condition(r#"(starts_with null null)"#, false);
        test_condition(r#"(starts_with 5 5)"#, false);
        test_condition(r#"(starts_with {"foo": 5} "foo")"#, false);
        test_condition(r#"(starts_with {"foo": 5} "f")"#, false);
    }

    #[test]
    fn test_ends_with() {
        test_condition("(ends_with [5, 6] 5)", false);
        test_condition("(ends_with [5, 6] 6)", true);
        test_condition(r#"(ends_with "foo" "fo")"#, false);
        test_condition(r#"(ends_with "foo" "")"#, true);
        test_condition(r#"(ends_with "foo" "oo")"#, true);
        test_condition(r#"(ends_with null null)"#, false);
        test_condition(r#"(ends_with 5 5)"#, false);
        test_condition(r#"(ends_with {"foo": 5} "foo")"#, false);
        test_condition(r#"(ends_with {"foo": 5} "f")"#, false);
    }

    #[test]
    fn test_contains() {
        test_condition("(contains [5, 6] 5)", true);
        test_condition("(contains [5, 6] 6)", true);
        test_condition(r#"(contains "foo" "fo")"#, true);
        test_condition(r#"(contains "foo" "")"#, true);
        test_condition(r#"(contains "foo" "oo")"#, true);
        test_condition(r#"(contains null null)"#, false);
        test_condition(r#"(contains 5 5)"#, false);
        test_condition(r#"(contains {"foo": 5} "foo")"#, true);
        test_condition(r#"(contains {"foo": 5} "f")"#, false);
    }

    #[test]
    fn test_env() {
        std::env::set_var("HBAR_TEST_ENV", "value");
        std::env::remove_var("HBAR_TEST_NOT_EXISTED_ENV");

        assert_renders![
            (r#"{{env "HBAR_TEST_ENV"}}"#, "value"),
            (r#"{{env "HBAR_TEST_NOT_EXISTED_ENV"}}"#, ""),
        ];
    }

    #[test]
    fn test_default() {
        assert_renders![
            (r#"{{default not_existed 5}}"#, r#"5"#),
            (r#"{{default 5 10}}"#, r#"5"#),
        ];
    }

    #[test]
    fn test_split() {
        assert_renders![
            (r#"{{split "12" ""}}"#, r#"[, 1, 2, , ]"#),
            (r#"{{split "1 2" " "}}"#, r#"[1, 2, ]"#),
        ];
    }
}