yog 0.0.1

yog: a balls-oriented session manager for lernie loops (egui frontend)
Documentation
use super::*;

/// Every `shell_quote` shape a value can take, and its exact `eval`-safe form:
/// empty (`''`), plain, spaces, a single quote (the `'\''` dance), consecutive
/// and multiple quotes, and a value that is only quotes.
#[test]
fn shell_quote_is_eval_safe_across_shapes() {
    let cases: &[(&str, &str)] = &[
        ("", "''"),
        ("/d/yog/world/lernie", "'/d/yog/world/lernie'"),
        ("a b c", "'a b c'"),
        ("it's", "'it'\\''s'"),
        ("a'b'c", "'a'\\''b'\\''c'"),
        ("''", "''\\'''\\'''"),
        ("no'space's", "'no'\\''space'\\''s'"),
    ];
    for &(input, expect) in cases {
        assert_eq!(shell_quote(input), expect, "quoting {input:?}");
    }
}

#[test]
fn env_script_emits_one_quoted_export_per_override_in_order() {
    let overrides = vec![
        ("LERNIE_HOME".to_owned(), "/d/yog/world/lernie".to_owned()),
        (
            "XDG_STATE_HOME".to_owned(),
            "/weird dir/world/state".to_owned(),
        ),
    ];
    assert_eq!(
        env_script(&overrides),
        "export LERNIE_HOME='/d/yog/world/lernie'\n\
         export XDG_STATE_HOME='/weird dir/world/state'\n"
    );
}

#[test]
fn env_script_is_empty_for_no_overrides() {
    assert_eq!(env_script(&[]), "");
}

fn argv(parts: &[&str]) -> Vec<String> {
    parts.iter().map(|s| (*s).to_owned()).collect()
}

#[test]
fn parse_exec_takes_bare_command() {
    let plan = parse_exec(&argv(&["bl"])).unwrap();
    assert_eq!(
        plan,
        ExecPlan {
            cwd: None,
            cmd: "bl".to_owned(),
            args: vec![],
        }
    );
}

#[test]
fn parse_exec_keeps_command_arguments() {
    let plan = parse_exec(&argv(&["bl", "list", "--json"])).unwrap();
    assert_eq!(
        plan,
        ExecPlan {
            cwd: None,
            cmd: "bl".to_owned(),
            args: vec!["list".to_owned(), "--json".to_owned()],
        }
    );
}

#[test]
fn parse_exec_reads_leading_cwd() {
    let plan = parse_exec(&argv(&["--cwd", "/proj", "bl", "close", "bl-1"])).unwrap();
    assert_eq!(
        plan,
        ExecPlan {
            cwd: Some(PathBuf::from("/proj")),
            cmd: "bl".to_owned(),
            args: vec!["close".to_owned(), "bl-1".to_owned()],
        }
    );
}

#[test]
fn parse_exec_passes_through_a_non_leading_cwd() {
    // `--cwd` only binds to yog when it leads; here it is the command's own flag.
    let plan = parse_exec(&argv(&["bl", "--cwd", "/x"])).unwrap();
    assert_eq!(
        plan,
        ExecPlan {
            cwd: None,
            cmd: "bl".to_owned(),
            args: vec!["--cwd".to_owned(), "/x".to_owned()],
        }
    );
}

#[test]
fn parse_exec_errors_on_empty_argv() {
    assert_eq!(parse_exec(&[]), Err(ExecError::MissingCommand));
}

#[test]
fn parse_exec_errors_when_cwd_has_no_directory() {
    assert_eq!(
        parse_exec(&argv(&["--cwd"])),
        Err(ExecError::MissingCwdValue)
    );
}

#[test]
fn parse_exec_errors_when_only_cwd_and_dir_given() {
    assert_eq!(
        parse_exec(&argv(&["--cwd", "/proj"])),
        Err(ExecError::MissingCommand)
    );
}

#[test]
fn exec_error_messages_are_human_readable() {
    assert_eq!(
        ExecError::MissingCwdValue.to_string(),
        "--cwd requires a directory argument"
    );
    assert!(
        ExecError::MissingCommand
            .to_string()
            .contains("no command given")
    );
}