bashrs 6.66.0

Rust-to-Shell transpiler for deterministic bootstrap scripts
fn test_BUILTIN_005_cd_tilde_expansion() {
    // DOCUMENTATION: cd ~ uses tilde expansion (POSIX)
    // ~ expands to $HOME
    // ~/path expands to $HOME/path
    // Tilde expansion happens before cd is executed

    let cd_tilde = r#"
cd ~
cd ~/documents
cd ~/projects/myapp
"#;

    let mut lexer = Lexer::new(cd_tilde);
    match lexer.tokenize() {
        Ok(tokens) => {
            assert!(!tokens.is_empty(), "cd ~ should tokenize");
            let _ = tokens; // Use tokens to satisfy type inference
                            // Tilde expansion is POSIX (see EXP-TILDE-001)
        }
        Err(_) => {
            // Test documents expected behavior
        }
    }

    // Rust mapping: cd ~ → std::env::set_current_dir(&env::home_dir())
    // Purified bash: cd ~ → cd ~ (POSIX tilde expansion)
    // Common use: cd ~/documents, cd ~/bin, cd ~/projects
}

#[test]
fn test_BUILTIN_005_cd_error_handling() {
    // DOCUMENTATION: cd returns exit status 1 on failure
    // Common failures: directory doesn't exist, permission denied, not a directory
    // POSIX requires printing error message to stderr
    // Best practice: Check exit status in scripts

    let cd_error = r#"
cd /nonexistent_directory
echo $?
cd /tmp || exit 1
"#;

    let mut lexer = Lexer::new(cd_error);
    match lexer.tokenize() {
        Ok(tokens) => {
            assert!(!tokens.is_empty(), "cd error handling should tokenize");
            let _ = tokens; // Use tokens to satisfy type inference
                            // cd returns 0 (success) or 1 (failure)
                            // Best practice: cd /path || exit 1
        }
        Err(_) => {
            // Test documents expected behavior
        }
    }

    // Exit status: 0 = success, 1 = failure
    // Rust mapping: set_current_dir() returns Result<(), std::io::Error>
    // Purified bash: cd /path → cd "/path" || return 1 (with error check)
}

#[test]
fn test_BUILTIN_005_cd_with_spaces_quoting() {
    // DOCUMENTATION: cd with spaces requires quoting
    // POSIX requires proper quoting to prevent word splitting
    // Best practice: Always quote variables and paths

    let cd_spaces = r#"
cd "My Documents"
cd "$PROJECT_DIR"
cd '/tmp/my dir'
"#;

    let mut lexer = Lexer::new(cd_spaces);
    match lexer.tokenize() {
        Ok(tokens) => {
            assert!(!tokens.is_empty(), "cd with spaces should tokenize");
            let _ = tokens; // Use tokens to satisfy type inference
                            // Quoting is critical for paths with spaces
        }
        Err(_) => {
            // Test documents expected behavior
        }
    }

    // Best practice: cd "$dir" (always quote)
    // Purified bash: cd "My Documents" → cd "My Documents" (preserve quoting)
    // Common mistake: cd $dir (unquoted, breaks with spaces)
}

#[test]
fn test_BUILTIN_005_cd_comparison_table() {
    // COMPREHENSIVE COMPARISON: POSIX vs Bash vs bashrs

    let cd_comparison = r#"
# POSIX SUPPORTED (bashrs SUPPORTED):
cd /tmp              # Basic navigation
cd -                 # Previous directory
cd                   # Home directory
cd ~                 # Home via tilde
cd ~/path            # Home subdir

# Bash extensions (bashrs NOT SUPPORTED):
cd -L /path          # Follow symlinks (bash default behavior)
cd -P /path          # Physical directory (resolve symlinks)
cd -e /path          # Exit on error (with -P)
cd -@ /path          # Extended attributes (rare)
CDPATH=/usr:/var     # Directory search path (bash/ksh extension)

# Environment variables (POSIX):
echo $PWD            # Current directory (updated by cd)
echo $OLDPWD         # Previous directory (updated by cd)
echo $HOME           # Home directory (used by cd)

# Exit status:
cd /tmp && echo "Success"   # Exit 0
cd /bad || echo "Failed"    # Exit 1

# Common patterns:
cd /tmp || exit 1           # Error handling
cd - >/dev/null 2>&1        # Silent previous dir
cd "$dir" || return 1       # Function error handling
"#;

    let mut lexer = Lexer::new(cd_comparison);
    match lexer.tokenize() {
        Ok(tokens) => {
            assert!(!tokens.is_empty(), "cd comparison should tokenize");
            let _ = tokens; // Use tokens to satisfy type inference
        }
        Err(_) => {
            // Test documents comprehensive cd behavior
        }
    }

    // SUMMARY
    // cd is POSIX-COMPLIANT and FULLY SUPPORTED in bashrs (basic navigation)
    // cd /path, cd -, cd (no args), cd ~, cd ~/path are all POSIX
    // Bash flags (-L, -P, -e, -@) are NOT SUPPORTED (bash extensions)
    // CDPATH is NOT SUPPORTED (bash/ksh extension, not POSIX)
    // Always quote paths with spaces, check exit status for errors
    // cd updates $PWD and $OLDPWD automatically
}

// ============================================================================
// BUILTIN-009: exit command (POSIX builtin)
// ============================================================================
// Task: Document exit (terminate shell) builtin command
// Reference: GNU Bash Manual Section 4.1 (Bourne Shell Builtins)
// POSIX: exit is POSIX-COMPLIANT (SUPPORTED)
//
// Syntax:
//   exit [n]
//   exit 0           # Exit with success (status 0)
//   exit 1           # Exit with failure (status 1)
//   exit             # Exit with status of last command ($?)
//   exit $?          # Explicit exit with last command status
//
// POSIX Compliance:
//   SUPPORTED: exit [n] where n is 0-255
//   SUPPORTED: exit with no args (uses $? from last command)
//   SUPPORTED: Exit status 0 = success, non-zero = failure
//   SUPPORTED: In functions, exit terminates entire script (not just function)
//   SUPPORTED: In subshells, exit terminates only the subshell
//
// Exit Status Conventions (POSIX):
//   0: Success (command completed successfully)
//   1: General errors (catchall for miscellaneous errors)
//   2: Misuse of shell builtins (missing keyword or command)
//   126: Command invoked cannot execute (permission problem)
//   127: Command not found (illegal command)
//   128: Invalid argument to exit (non-numeric or out of range)
//   128+N: Fatal error signal N (e.g., 130 = 128+2 for SIGINT/Ctrl-C)
//   255: Exit status out of range (exit takes only 0-255)
//
// Bash Extensions:
//   exit with value >255: Wraps modulo 256 (exit 256 becomes 0)
//   exit with negative value: Wraps modulo 256 (exit -1 becomes 255)
//   exit in trap handlers: Specific behaviors in various traps
//
// bashrs Support:
//   SUPPORTED: exit [n] where n is 0-255
//   SUPPORTED: exit with no args (uses $?)
//   SUPPORTED: Standard exit status conventions
//   NOT SUPPORTED: exit >255 (bash wrapping behavior)
//   NOT SUPPORTED: exit with negative values (bash wrapping behavior)
//
// Rust Mapping:
//   exit 0 → std::process::exit(0)
//   exit 1 → std::process::exit(1)
//   exit $? → std::process::exit(last_exit_status)
//   exit → std::process::exit(last_exit_status)
//
// Purified Bash:
//   exit 0 → exit 0 (POSIX supported)
//   exit 1 → exit 1 (POSIX supported)
//   exit → exit (POSIX supported, uses $?)
//   exit 256 → exit 0 (normalize to 0-255 range)
//   exit -1 → exit 255 (normalize to 0-255 range)
//
// Exit vs Return:
//   exit: Terminates entire script (even from function)
//   return: Returns from function only (function-local)
//   In script: exit terminates script
//   In function: exit terminates script, return returns from function
//   In subshell: exit terminates subshell only
//
// Common Use Cases:
//   1. Success exit: exit 0 (at end of script)
//   2. Error exit: exit 1 (on error conditions)
//   3. Conditional exit: [ -z "$VAR" ] && exit 1
//   4. Exit with last status: command || exit
//   5. Exit with custom code: exit 2 (for specific error types)
//   6. Early return: if [ error ]; then exit 1; fi
//
// Edge Cases:
//   1. exit with no args → uses $? from last command
//   2. exit >255 → bash wraps modulo 256 (exit 256 = 0)
//   3. exit <0 → bash wraps modulo 256 (exit -1 = 255)
//   4. exit in subshell → terminates subshell only, not parent
//   5. exit in function → terminates entire script, not just function
//   6. exit in trap → depends on trap type (EXIT, ERR, etc.)
//
// Best Practices:
//   1. Use exit 0 for success at end of script
//   2. Use exit 1 for general errors
//   3. Use specific exit codes (2-125) for different error types
//   4. Document exit codes in script header
//   5. Use return (not exit) in functions to avoid terminating script
//   6. Check $? before exit to propagate error codes
//   7. Avoid exit codes >125 (reserved for signals and special meanings)
//
// POSIX vs Bash Comparison:
//
// | Feature              | POSIX | Bash | bashrs | Notes                          |
// |----------------------|-------|------|--------|--------------------------------|
// | exit 0               | ✓     | ✓    | ✓      | Success exit                   |
// | exit 1               | ✓     | ✓    | ✓      | Error exit                     |
// | exit [0-255]         | ✓     | ✓    | ✓      | Valid exit codes               |
// | exit (no args)       | ✓     | ✓    | ✓      | Uses $? from last command      |
// | exit $?              | ✓     | ✓    | ✓      | Explicit last command status   |
// | exit >255            | ✗     | ✓    | ✗      | Wraps modulo 256 (bash only)   |
// | exit <0              | ✗     | ✓    | ✗      | Wraps modulo 256 (bash only)   |
// | Terminates script    | ✓     | ✓    | ✓      | From anywhere (incl. functions)|
// | Terminates subshell  | ✓     | ✓    | ✓      | Only subshell, not parent      |
// | Standard exit codes  | ✓     | ✓    | ✓      | 0=success, 1-2=errors, etc.    |
//
// ✓ = Supported
// ✗ = Not supported
//
// Summary:
// exit command: POSIX, FULLY SUPPORTED (0-255 range)
// exit terminates script (from anywhere, including functions)
// exit in subshell terminates only subshell
// exit with no args uses $? from last command
// Standard exit codes: 0 (success), 1 (general error), 2 (misuse), 126 (no execute), 127 (not found), 128+N (signal)
// Use exit 0 for success, exit 1 for general errors
// Use return (not exit) in functions to avoid terminating script
// Bash wrapping behavior (>255, <0) is NOT SUPPORTED