comlexr 1.5.0

Dynamically build Command objects with conditional expressions
Documentation
extern crate comlexr;
extern crate rstest;

use std::env;

use comlexr::{cmd, cmd_mut};
use rstest::rstest;

#[test]
fn expression() {
    let command = cmd!("echo", "test");

    assert_eq!(format!("{command:?}"), r#""echo" "test""#.to_string());
}

#[test]
fn mut_command() {
    let mut command = cmd!(
        cd "/";
        env {
            "TEST_1": "test"
        };
        "echo"
    );
    cmd_mut!(
        cd "~/";
        env {
            "TEST_2": "test",
        };
        &mut command,
        "test",
        for &["1", "2"],
    );
    assert_eq!(
        format!("{command:?}"),
        r#"cd "~/" && TEST_1="test" TEST_2="test" "echo" "test" "1" "2""#.to_string()
    );
}

#[test]
fn current_dir() {
    let command = cmd!(
        cd "~/";
        "echo",
        "test",
    );

    assert_eq!(format!("{command:?}"), r#"cd "~/" && "echo" "test""#);
}

#[test]
fn env_vars() {
    let command = cmd!(
        env {
            "TEST": "test",
        };
        "echo",
        "test",
    );

    assert_eq!(format!("{command:?}"), r#"TEST="test" "echo" "test""#);
}

#[test]
fn conditional_env_vars() {
    env::set_var("TEST", "realvalue");
    env::set_var("TEST2", "won't see");

    let command = cmd!(
        env {
            "TEST":? "test",
            "TEST2": "test2"
        };
        "echo",
        "test",
    );

    assert_eq!(format!("{command:?}"), r#"TEST2="test2" "echo" "test""#);

    let command = cmd!(
        env {
            "TEST": "test",
            "TEST2": "test2"
        };
        "echo",
        "test",
    );

    assert_eq!(
        format!("{command:?}"),
        r#"TEST="test" TEST2="test2" "echo" "test""#
    );
}

#[test]
fn cd_env_vars() {
    let command = cmd!(
        cd "~/";
        env {
            "TEST": "test",
        };
        "echo",
        "test",
    );

    assert_eq!(
        format!("{command:?}"),
        r#"cd "~/" && TEST="test" "echo" "test""#
    );
}

#[rstest]
#[case(false, false, r#""echo" "test""#)]
#[case(true, false, r#""echo" "test" "single""#)]
#[case(false, true, r#""echo" "test" "multi" "arg""#)]
#[case(true, true, r#""echo" "test" "single" "multi" "arg""#)]
fn if_statement(#[case] single: bool, #[case] multi: bool, #[case] expected: &str) {
    let command = cmd!(
        "echo",
        "test",
        if single => "single",
        if multi => [
            "multi",
            "arg",
        ],
    );

    assert_eq!(format!("{command:?}"), expected.to_string());
}

#[rstest]
#[case(&[], r#""echo" "test""#)]
#[case(&["1", "2"], r#""echo" "test" "1" "2""#)]
fn for_iter(#[case] iter: &[&str], #[case] expected: &str) {
    let command = cmd!(
        "echo",
        "test",
        for iter,
    );

    assert_eq!(format!("{command:?}"), expected.to_string());
}

#[rstest]
#[case(&[], &[], r#""echo" "test""#)]
#[case(&["1", "2"], &[], r#""echo" "test" "1" "2""#)]
#[case(&[], &["3", "4"], r#""echo" "test" "multi" "3" "multi" "4""#)]
#[case(&["1", "2"], &["3", "4"], r#""echo" "test" "1" "2" "multi" "3" "multi" "4""#)]
fn for_in(#[case] single_iter: &[&str], #[case] multi_iter: &[&str], #[case] expected: &str) {
    let command = cmd!(
        "echo",
        "test",
        for arg in single_iter => arg,
        for arg in multi_iter => [
            "multi",
            arg,
        ],
    );

    assert_eq!(format!("{command:?}"), expected.to_string());
}

struct TestStruct(&'static str);

#[rstest]
#[case(&[], &[], r#""echo" "test""#)]
#[case(&[TestStruct("1"), TestStruct("2")], &[], r#""echo" "test" "1" "2""#)]
#[case(&[], &[TestStruct("3"), TestStruct("4")], r#""echo" "test" "multi" "3" "multi" "4""#)]
#[case(&[TestStruct("1"), TestStruct("2")], &[TestStruct("3"), TestStruct("4")], r#""echo" "test" "1" "2" "multi" "3" "multi" "4""#)]
fn for_in_pat(
    #[case] single_iter: &[TestStruct],
    #[case] multi_iter: &[TestStruct],
    #[case] expected: &str,
) {
    let command = cmd!(
        "echo",
        "test",
        for TestStruct(arg) in single_iter => arg,
        for TestStruct(arg) in multi_iter => [
            "multi",
            arg,
        ],
    );

    assert_eq!(format!("{command:?}"), expected.to_string());
}

#[rstest]
#[case(None, None, r#""echo" "test""#)]
#[case(Some("arg"), None, r#""echo" "test" "arg""#)]
#[case(None, Some("arg"), r#""echo" "test" "multi" "arg""#)]
#[case(Some("1"), Some("2"), r#""echo" "test" "1" "multi" "2""#)]
fn if_let(#[case] single: Option<&str>, #[case] multi: Option<&str>, #[case] expected: &str) {
    let command = cmd!(
        "echo",
        "test",
        if let Some(arg) = single => arg,
        if let Some(arg) = multi => [
            "multi",
            arg,
        ],
    );

    assert_eq!(format!("{command:?}"), expected.to_string());
}

enum TestArgs {
    Arg1,
    Arg2,
    Arg3,
}

#[rstest]
#[case(TestArgs::Arg1, r#""echo" "test" "arg1""#)]
#[case(TestArgs::Arg2, r#""echo" "test" "arg1" "arg2""#)]
#[case(TestArgs::Arg3, r#""echo" "test" "arg1" "arg2" "arg3""#)]
fn match_statement(#[case] match_arg: TestArgs, #[case] expected: &str) {
    let command = cmd!(
        "echo",
        "test",
        match match_arg {
            TestArgs::Arg1 => "arg1",
            TestArgs::Arg2 => ["arg1", "arg2"],
            TestArgs::Arg3 => ["arg1", "arg2", "arg3"],
        }
    );

    assert_eq!(format!("{command:?}"), expected.to_string());
}

#[rstest]
#[case(TestArgs::Arg1, true, r#""echo" "test" "arg1""#)]
#[case(TestArgs::Arg1, false, r#""echo" "test""#)]
#[case(TestArgs::Arg2, true, r#""echo" "test" "arg1" "arg2""#)]
#[case(TestArgs::Arg2, false, r#""echo" "test""#)]
#[case(TestArgs::Arg3, true, r#""echo" "test" "arg1" "arg2" "arg3""#)]
#[case(TestArgs::Arg3, false, r#""echo" "test""#)]
fn match_statement_conditional(
    #[case] match_arg: TestArgs,
    #[case] flag: bool,
    #[case] expected: &str,
) {
    let command = cmd!(
        "echo",
        "test",
        match match_arg {
            TestArgs::Arg1 if flag => "arg1",
            TestArgs::Arg2 if flag => ["arg1", "arg2"],
            TestArgs::Arg3 if flag => ["arg1", "arg2", "arg3"],
            _ => [],
        }
    );

    assert_eq!(format!("{command:?}"), expected.to_string());
}

#[rstest]
#[case(None, None, r#""echo" "test""#)]
#[case(Some("arg"), None, r#""echo" "test" "arg""#)]
#[case(None, Some("arg"), r#""echo" "test" "multi" "arg""#)]
#[case(Some("1"), Some("2"), r#""echo" "test" "1" "multi=2""#)]
fn multi_match(#[case] single: Option<&str>, #[case] multi: Option<&str>, #[case] expected: &str) {
    let command = cmd!(
        "echo",
        "test",
        match (single, multi) {
            (None, None) => [],
            (Some(single), None) => single,
            (None, Some(multi)) => ["multi", multi],
            (Some(single), Some(multi)) => [single, format!("multi={multi}")],
        },
    );

    assert_eq!(format!("{command:?}"), expected.to_string());
}

#[rstest]
#[case(1, r#""echo" "test" "1" "2" "3""#)]
#[case(2, r#""echo" "test" "2" "4" "6""#)]
#[case(3, r#""echo" "test" "3" "6" "9""#)]
fn closure_expr(#[case] input: usize, #[case] expected: &str) {
    let arr = vec![1, 2, 3];
    let command = cmd!("echo", "test", || arr
        .into_iter()
        .map(|i| format!("{}", i * input)));

    assert_eq!(format!("{command:?}"), expected.to_string());
}

#[rstest]
#[case(1, r#""echo" "test" "--test=1a" "--test=2a" "--test=3a""#)]
#[case(2, r#""echo" "test" "--test=2a" "--test=4a" "--test=6a""#)]
#[case(3, r#""echo" "test" "--test=3a" "--test=6a" "--test=9a""#)]
fn closure_block(#[case] input: usize, #[case] expected: &str) {
    let arr = vec![1, 2, 3];
    let suffix = "a";
    let command = cmd!("echo", "test", || {
        let prefix = "--test";
        arr.into_iter()
            .map(move |i| format!("{prefix}={}{suffix}", i * input))
    });

    assert_eq!(format!("{command:?}"), expected.to_string());
}