locrian 0.2.2

A simple embeddable functional programming language.
Documentation
use crate::eval::{EvalFn, EvalResult};

pub(crate) fn concat(args: Vec<EvalResult>) -> EvalResult {
    EvalResult::String(
        args.iter()
            .map(|val| val.to_string())
            .collect::<Vec<_>>()
            .concat(),
    )
}

pub(crate) fn split(args: Vec<EvalResult>) -> EvalResult {
    if let Some(EvalResult::String(s)) = args.get(0) {
        EvalResult::Array(
            if let Some(EvalResult::String(separator)) = args.get(1) {
                s.split(separator)
                    .map(|s| s.to_string())
                    .collect::<Vec<_>>()
            } else {
                s.as_bytes()
                    .iter()
                    .map(|c| c.to_string())
                    .collect::<Vec<_>>()
            }
            .iter()
            .map(|s| EvalResult::String(s.clone()))
            .collect(),
        )
    } else {
        EvalResult::None
    }
}

pub(crate) fn ify(args: Vec<EvalResult>) -> EvalResult {
    if let Some(v) = args.get(0) {
        EvalResult::String(v.to_string())
    } else {
        EvalResult::String("".to_string())
    }
}

pub(crate) fn replace(args: Vec<EvalResult>) -> EvalResult {
    if let Some(EvalResult::String(s)) = args.get(0)
        && let Some(pat) = args.get(1)
        && let Some(replacement) = args.get(2)
    {
        EvalResult::String(s.replace(&pat.to_string(), &replacement.to_string()))
    } else {
        EvalResult::None
    }
}

pub(crate) static format: EvalFn = func!(
    r#"
    use(["string", "array", "bool"],
        'let({
            "splits": split($0, "{}"),
            "replacements": append($1, "")},
            'pipe(
                map(splits,
                    'concat($it, at(replacements, $i))),
                'concat(...$0))))
    "#
);

pub(crate) static escape: EvalFn = func!(
    r#"
    use(["string"],
        'pipe($0,
            'replace($0, "~n", lf),
            'replace($0, "~t", tab),
            'replace($0, "~e", esc)))
    "#
);

#[cfg(test)]
mod tests {

    use crate::{eval::EvalResult, eval_str, stdlib::STDLIB};

    #[test]
    fn test_concat() {
        assert_eq!(
            eval_str(
                r#"string:concat("hello", " ", "world", 3, string:lf)"#,
                STDLIB.clone()
            )
            .unwrap(),
            EvalResult::String("hello world3\n".to_string())
        );
    }

    #[test]
    fn test_split() {
        assert_eq!(
            eval_str(r#"string:split("hello{}world", "{}")"#, STDLIB.clone()).unwrap(),
            eval_str(r#"["hello", "world"]"#, STDLIB.clone()).unwrap()
        )
    }

    #[test]
    fn test_format() {
        assert_eq!(
            eval_str(
                r#"string:format("hello, {}! {}", ["world", 3])"#,
                STDLIB.clone()
            )
            .unwrap(),
            EvalResult::String("hello, world! 3".to_string())
        )
    }

    #[test]
    fn test_replace() {
        assert_eq!(
            eval_str(
                r#"string:replace("hello world hello world", "world", "Mars")"#,
                STDLIB.clone()
            )
            .unwrap(),
            EvalResult::String("hello Mars hello Mars".to_string())
        );
    }

    #[test]
    fn test_escape() {
        assert_eq!(
            eval_str(
                r#"string:escape("~t~e[1mhello world~e[0m~n")"#,
                STDLIB.clone()
            )
            .unwrap(),
            EvalResult::String("\t\x1b[1mhello world\x1b[0m\n".to_string())
        );
    }
}