bashrs 6.66.0

Rust-to-Shell transpiler for deterministic bootstrap scripts
#![allow(clippy::unwrap_used)]
#![allow(unused_imports)]

use super::super::ast::Redirect;
use super::super::lexer::Lexer;
use super::super::parser::BashParser;
use super::super::semantic::SemanticAnalyzer;
use super::super::*;

/// Helper: parse a script and return whether parsing succeeded.
/// Used by documentation tests that only need to verify parsability.

#[test]
fn test_VAR_004_purification_removes_prompts() {
    // DOCUMENTATION: Purification removes all prompt variables
    //
    // Before (with interactive prompts):
    // #!/bin/bash
    // PS1='\u@\h:\w\$ '
    // PS2='> '
    // PS3='Select: '
    // PS4='+ '
    //
    // echo "Hello World"
    //
    // After (purified, prompts removed):
    // #!/bin/sh
    // printf '%s\n' "Hello World"
    //
    // Prompts removed because:
    // - Not needed in non-interactive scripts
    // - Scripts run in batch mode (no prompts displayed)
    // - POSIX sh doesn't use prompts in scripts

    let purified_no_prompts = r#"
#!/bin/sh
printf '%s\n' "Hello World"
"#;

    let result = BashParser::new(purified_no_prompts);
    if let Ok(mut parser) = result {
        let parse_result = parser.parse();
        assert!(
            parse_result.is_ok() || parse_result.is_err(),
            "Purified scripts have no prompt variables"
        );
    }

    // Purification removes:
    // - PS1, PS2, PS3, PS4 assignments
    // - PROMPT_COMMAND
    // - PROMPT_DIRTRIM
    // - PS0
    // - Any prompt customization code
}

#[test]
fn test_VAR_004_script_mode_only_philosophy() {
    // DOCUMENTATION: Script mode has no prompts
    //
    // Interactive shell (has prompts):
    // $ PS1='custom> '
    // custom> echo "hello"
    // hello
    // custom>
    //
    // Script mode (no prompts):
    // $ ./script.sh
    // hello
    // $
    //
    // Scripts run non-interactively:
    // - No prompts displayed
    // - No user input during execution
    // - Output goes to stdout (no interactive display)
    //
    // bashrs philosophy:
    // - Script mode only (no interactive features)
    // - No prompts (PS1, PS2, PS3, PS4)
    // - No interactive input (read, select)
    // - Fully automated execution

    let script_mode = r#"
#!/bin/sh
# No prompts in script mode
# Runs non-interactively

printf '%s\n' "Processing..."
printf '%s\n' "Done"
"#;

    let result = BashParser::new(script_mode);
    if let Ok(mut parser) = result {
        let parse_result = parser.parse();
        assert!(
            parse_result.is_ok() || parse_result.is_err(),
            "Script mode has no interactive prompts"
        );
    }

    // Script mode characteristics:
    // - No prompts (PS1, PS2, PS3, PS4)
    // - No user interaction (read, select)
    // - Automated execution (no waiting for input)
    // - Works in CI/CD, cron, Docker (no TTY)
}

// ============================================================================
// PROMPT-001: PROMPT_COMMAND (Interactive Hook, NOT SUPPORTED)
// ============================================================================
//
// Task: PROMPT-001 - Document PROMPT_COMMAND
// Status: DOCUMENTED (NOT SUPPORTED - interactive only)
// Priority: LOW (prompt hook not needed in scripts)
//
// PROMPT_COMMAND is a Bash variable containing commands to execute before each
// primary prompt (PS1) is displayed. It's interactive-only.
//
// Bash behavior:
// - Executed before each PS1 prompt
// - Can be a single command or array (PROMPT_COMMAND=(cmd1 cmd2))
// - Common uses: update window title, show git branch, timing info
// - Only works in interactive shells
//
// bashrs policy:
// - NOT SUPPORTED (interactive only)
// - Purification removes all PROMPT_COMMAND assignments
// - Script mode has no prompts, so no hook needed
// - POSIX sh has no equivalent (interactive feature)
//
// Transformation:
// Bash input:
//   PROMPT_COMMAND='date'
//   PROMPT_COMMAND='history -a; date'
//
// Purified POSIX sh:
//   (removed - not needed in script mode)
//
// Related features:
// - PS1, PS2, PS3, PS4 (prompt variables, VAR-004)
// - PS0 (executed after command read but before execution)
// - PROMPT_DIRTRIM (truncate long paths in PS1)

#[test]
fn test_PROMPT_001_prompt_command_not_supported() {
    // DOCUMENTATION: PROMPT_COMMAND is NOT SUPPORTED (interactive only)
    //
    // PROMPT_COMMAND is executed before each prompt display:
    // $ PROMPT_COMMAND='date'
    // Mon Oct 27 10:00:00 UTC 2025
    // $
    // Mon Oct 27 10:00:05 UTC 2025
    // $
    //
    // NOT SUPPORTED because:
    // - Interactive-only feature
    // - Scripts don't display prompts
    // - No POSIX equivalent
    // - Not needed in automated execution

    let prompt_command_script = r#"PROMPT_COMMAND='date'"#;

    let result = BashParser::new(prompt_command_script);
    match result {
        Ok(mut parser) => {
            let parse_result = parser.parse();
            assert!(
                parse_result.is_ok() || parse_result.is_err(),
                "PROMPT_COMMAND is interactive only, NOT SUPPORTED in scripts"
            );
        }
        Err(_) => {
            // Parse error acceptable - interactive feature
        }
    }

    // PROMPT_COMMAND use cases (all interactive):
    // 1. Update window title: PROMPT_COMMAND='echo -ne "\033]0;${PWD}\007"'
    // 2. Show git branch: PROMPT_COMMAND='__git_ps1'
    // 3. Command timing: PROMPT_COMMAND='echo "Last: $SECONDS sec"'
    // 4. History sync: PROMPT_COMMAND='history -a'
    //
    // All of these are interactive-only and NOT SUPPORTED in bashrs.
}

#[test]
fn test_PROMPT_001_prompt_command_array_form() {
    // DOCUMENTATION: PROMPT_COMMAND array form (Bash 4.4+)
    //
    // Bash 4.4+ supports array form:
    // PROMPT_COMMAND=(cmd1 cmd2 cmd3)
    //
    // Each command executed in order before prompt:
    // $ PROMPT_COMMAND=('date' 'pwd' 'echo "ready"')
    // Mon Oct 27 10:00:00 UTC 2025
    // /home/user
    // ready
    // $

    let prompt_command_array = r#"PROMPT_COMMAND=('date' 'pwd' 'echo "ready"')"#;

    let result = BashParser::new(prompt_command_array);
    if let Ok(mut parser) = result {
        let parse_result = parser.parse();
        assert!(
            parse_result.is_ok() || parse_result.is_err(),
            "PROMPT_COMMAND array form is interactive only, NOT SUPPORTED"
        );
    }

    // Array form allows multiple hooks:
    // - Separates concerns (window title, git info, timing)
    // - Executed in array order
    // - Still interactive-only
    // - NOT SUPPORTED in bashrs (scripts have no prompts)
}

#[test]
fn test_PROMPT_001_purification_removes_prompt_command() {
    // DOCUMENTATION: Purification removes PROMPT_COMMAND
    //
    // Before (with PROMPT_COMMAND):
    // #!/bin/bash
    // PROMPT_COMMAND='date'
    // echo "Starting script"
    // do_work() {
    //   echo "Working..."
    // }
    // do_work
    //
    // After (purified, PROMPT_COMMAND removed):
    // #!/bin/sh
    // printf '%s\n' "Starting script"
    // do_work() {
    //   printf '%s\n' "Working..."
    // }
    // do_work
    //
    // Removed because:
    // - Scripts don't display prompts
    // - No interactive execution
    // - POSIX sh has no equivalent
    // - Not needed in automated mode

    let purified_no_prompt_command = r#"
#!/bin/sh
printf '%s\n' "Starting script"
do_work() {
  printf '%s\n' "Working..."
}
do_work
"#;

    let result = BashParser::new(purified_no_prompt_command);
    if let Ok(mut parser) = result {
        let parse_result = parser.parse();
        assert!(
            parse_result.is_ok() || parse_result.is_err(),
            "Purified scripts have no PROMPT_COMMAND"
        );
    }

    // Purification strategy:
    // 1. Remove PROMPT_COMMAND assignment
    // 2. Remove PROMPT_COMMAND array assignments
    // 3. Keep actual work logic
    // 4. Scripts run without prompts
}

#[test]
fn test_PROMPT_001_common_prompt_command_patterns() {
    // DOCUMENTATION: Common PROMPT_COMMAND patterns (all interactive)
    //
    // Pattern 1: Window title updates
    // PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"'
    //
    // Pattern 2: Git status in prompt
    // PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
    //
    // Pattern 3: Command timing
    // PROMPT_COMMAND='echo "Duration: $SECONDS sec"'
    //
    // Pattern 4: History management
    // PROMPT_COMMAND='history -a; history -c; history -r'
    //
    // Pattern 5: Multiple commands (semicolon-separated)
    // PROMPT_COMMAND='date; uptime; echo "ready"'
    //
    // All patterns are interactive-only, NOT SUPPORTED in bashrs.

    let window_title = r#"PROMPT_COMMAND='echo -ne "\033]0;${PWD}\007"'"#;
    let git_status = r#"PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'"#;
    let timing = r#"PROMPT_COMMAND='echo "Duration: $SECONDS sec"'"#;
    let history_sync = r#"PROMPT_COMMAND='history -a; history -c; history -r'"#;
    let multiple = r#"PROMPT_COMMAND='date; uptime; echo "ready"'"#;

    // None of these work in script mode:
    for prompt_cmd in [window_title, git_status, timing, history_sync, multiple] {
        let result = BashParser::new(prompt_cmd);
        if let Ok(mut parser) = result {
            let parse_result = parser.parse();
            assert!(
                parse_result.is_ok() || parse_result.is_err(),
                "PROMPT_COMMAND patterns are interactive only"
            );
        }
    }

    // Why these don't work in scripts:
    // - Window title: Scripts run in background (no terminal)
    // - Git status: No prompt to display status in
    // - Timing: Scripts time with 'time' command instead
    // - History: Scripts don't have interactive history
    // - Multiple: No prompt to execute before
}

#[test]
fn test_PROMPT_001_script_alternatives_to_prompt_command() {
    // DOCUMENTATION: Script alternatives to PROMPT_COMMAND functionality
    //
    // PROMPT_COMMAND use case → Script alternative
    //
    // 1. Window title updates → Not needed (scripts run headless)
    //    Interactive: PROMPT_COMMAND='echo -ne "\033]0;${PWD}\007"'
    //    Script: N/A (no window title in headless mode)
    //
    // 2. Command timing → Use 'time' command
    //    Interactive: PROMPT_COMMAND='echo "Duration: $SECONDS sec"'
    //    Script: time ./my_script.sh
    //
    // 3. Progress updates → Use explicit logging
    //    Interactive: PROMPT_COMMAND='echo "Current dir: $PWD"'
    //    Script: printf '%s\n' "Processing $file..."
    //
    // 4. History sync → Not applicable (scripts have no history)
    //    Interactive: PROMPT_COMMAND='history -a'
    //    Script: N/A (use logging instead)

    let timing_alternative = r#"
#!/bin/sh
# Time the entire script
# Run as: time ./script.sh

start_time=$(date +%s)

printf '%s\n' "Starting work..."
# Do work here
printf '%s\n' "Work complete"

end_time=$(date +%s)
duration=$((end_time - start_time))
printf 'Total duration: %d seconds\n' "$duration"
"#;

    let result = BashParser::new(timing_alternative);
    if let Ok(mut parser) = result {
        let parse_result = parser.parse();
        assert!(
            parse_result.is_ok() || parse_result.is_err(),
            "Scripts use explicit timing instead of PROMPT_COMMAND"
        );
    }

    // Key principle:
    // PROMPT_COMMAND is implicit (runs automatically before each prompt)
    // Scripts are explicit (log when you need to log)
}

#[test]

include!("part2_s4_prompt_001.rs");