pretty-please 0.1.0

The polite sudo.
Documentation
use std::path::PathBuf;

use pretty_please::cli::CommandInput;
use pretty_please::error::PleaseError;
use pretty_please::exec::{Runtime, plan};
use pretty_please::shell::Shell;

fn runtime(is_root: bool, sudo_path: Option<&str>) -> Runtime {
    Runtime {
        is_root,
        sudo_path: sudo_path.map(PathBuf::from),
    }
}

#[test]
fn prepends_sudo_for_normal_commands() {
    let plan = plan(
        CommandInput::Explicit(vec!["chmod".into(), "600".into(), "secret.txt".into()]),
        &runtime(false, Some("/usr/bin/sudo")),
    )
    .expect("command should plan");

    assert_eq!(plan.program, PathBuf::from("/usr/bin/sudo"));
    assert_eq!(plan.args, vec!["chmod", "600", "secret.txt"]);
    assert_eq!(plan.note, None);
}

#[test]
fn leaves_existing_sudo_alone() {
    let plan = plan(
        CommandInput::Explicit(vec!["sudo".into(), "chmod".into(), "600".into()]),
        &runtime(false, Some("/usr/bin/sudo")),
    )
    .expect("command should plan");

    assert_eq!(plan.program, PathBuf::from("sudo"));
    assert_eq!(plan.args, vec!["chmod", "600"]);
    assert!(
        plan.note
            .as_deref()
            .is_some_and(|note| note.contains("already starts with sudo"))
    );
}

#[test]
fn skips_sudo_for_root() {
    let plan = plan(
        CommandInput::Explicit(vec!["chmod".into(), "600".into(), "secret.txt".into()]),
        &runtime(true, Some("/usr/bin/sudo")),
    )
    .expect("command should plan");

    assert_eq!(plan.program, PathBuf::from("chmod"));
    assert_eq!(plan.args, vec!["600", "secret.txt"]);
    assert!(
        plan.note
            .as_deref()
            .is_some_and(|note| note.contains("already running as root"))
    );
}

#[test]
fn rejects_shell_builtins() {
    let error = plan(
        CommandInput::Explicit(vec!["cd".into(), "/root".into()]),
        &runtime(false, Some("/usr/bin/sudo")),
    )
    .expect_err("builtins should be rejected");

    assert!(matches!(error, PleaseError::Builtin(command) if command == "cd"));
}

#[test]
fn rejects_blank_history_commands() {
    let error = plan(
        CommandInput::History("   ".into()),
        &runtime(false, Some("/usr/bin/sudo")),
    )
    .expect_err("blank history should error");

    assert!(matches!(error, PleaseError::EmptyHistory));
}

#[test]
fn bash_init_snapshot() {
    assert_eq!(
        Shell::Bash.init_script(),
        r#"pls() {
  if [ "$#" -eq 0 ]; then
    local previous
    previous="$(history -p '!!' 2>/dev/null)" || {
      command pls --history-command ""
      return $?
    }

    if [[ "$previous" != *[![:space:]]* ]]; then
      command pls --history-command "$previous"
      return $?
    fi

    builtin eval "command pls ${previous}"
  else
    command pls "$@"
  fi
}
"#
    );
}

#[test]
fn zsh_init_snapshot() {
    assert_eq!(
        Shell::Zsh.init_script(),
        r#"pls() {
  if [ "$#" -eq 0 ]; then
    local previous
    previous="$(fc -ln -1 2>/dev/null)" || {
      command pls --history-command ""
      return $?
    }

    if [[ "$previous" != *[![:space:]]* ]]; then
      command pls --history-command "$previous"
      return $?
    fi

    builtin eval "command pls ${previous}"
  else
    command pls "$@"
  fi
}
"#
    );
}

#[test]
fn fish_init_snapshot() {
    assert_eq!(
        Shell::Fish.init_script(),
        r#"function pls --description 'Re-run the previous command with sudo'
    if test (count $argv) -eq 0
        set -l previous (history --max=1)

        if test -z (string trim -- $previous)
            command pls --history-command "$previous"
            return $status
        end

        eval "command pls $previous"
    else
        command pls $argv
    end
end
"#
    );
}

#[test]
fn pwsh_init_snapshot() {
    assert_eq!(
        Shell::Pwsh.init_script(),
        r#"function pls {
    param(
        [Parameter(ValueFromRemainingArguments = $true)]
        [string[]] $Args
    )

    $pls_bin = Get-Command pls -CommandType Application | Select-Object -First 1 -ExpandProperty Source
    if (-not $pls_bin) {
        Write-Error "pls binary not found in PATH."
        return
    }

    if ($Args.Count -eq 0) {
        $previous = (Get-History -Count 1).CommandLine
        if ([string]::IsNullOrWhiteSpace($previous)) {
            & $pls_bin --history-command $previous
            return
        }

        Invoke-Expression "& '$pls_bin' $previous"
    } else {
        & $pls_bin @Args
    }
}
"#
    );
}

#[test]
fn nu_init_snapshot() {
    assert_eq!(
        Shell::Nu.init_script(),
        r#"def --wrapped pls [...args: string] {
  if ($args | is-empty) {
    let previous = (history | last 1 | get command | first | default "")

    if (($previous | str trim) | is-empty) {
      ^pls --history-command $previous
    } else {
      nu -c $'^pls ($previous)'
    }
  } else {
    ^pls ...$args
  }
}
"#
    );
}