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: assert that BashParser handles the input without panicking.
/// Accepts both successful parses and parse errors (documentation tests
/// only verify the parser doesn't crash, not that the input is valid).
#[test]
fn test_REDIR_001_basic_input_redirection() {
    // DOCUMENTATION: Basic input redirection (<) is SUPPORTED (POSIX)
    //
    // Input redirection connects stdin to file:
    // $ wc -l < file.txt
    // $ grep "pattern" < input.txt
    // $ sort < unsorted.txt
    //
    // POSIX-compliant: Works in sh, dash, ash, bash
    //
    // Semantics:
    // - File contents become stdin for command
    // - More efficient than cat file | cmd (no pipe, no subshell)
    // - File must be readable
    // - Exit status: Command exit status (not related to file open)

    let input_redir = r#"
wc -l < file.txt
grep "pattern" < input.txt
sort < unsorted.txt
"#;

    let result = BashParser::new(input_redir);
    match result {
        Ok(mut parser) => {
            let parse_result = parser.parse();
            assert!(
                parse_result.is_ok() || parse_result.is_err(),
                "Input redirection is POSIX-compliant"
            );
        }
        Err(_) => {
            // Parse error acceptable - < may not be fully implemented yet
        }
    }
}

#[test]
fn test_REDIR_001_input_vs_file_argument() {
    // DOCUMENTATION: Input redirection (<) vs file argument
    //
    // Two ways to read files:
    // 1. Input redirection: cmd < file.txt (stdin redirected)
    // 2. File argument: cmd file.txt (explicit argument)
    //
    // Differences:
    // - Some commands accept file args: cat file.txt
    // - Some commands only read stdin: wc (with no args)
    // - Redirection works with any command that reads stdin
    //
    // Examples:
    // $ cat < file.txt    # Reads from stdin (redirected from file)
    // $ cat file.txt      # Reads from file argument
    // (Both produce same output)
    //
    // $ wc -l < file.txt  # Reads from stdin (shows line count only)
    // $ wc -l file.txt    # Reads from file (shows "count filename")

    let input_comparison = r#"
#!/bin/sh
# Input redirection (stdin)
cat < file.txt

# File argument (explicit)
cat file.txt

# Both work, slightly different behavior
wc -l < file.txt    # Shows: 42
wc -l file.txt      # Shows: 42 file.txt
"#;

    let result = BashParser::new(input_comparison);
    match result {
        Ok(mut parser) => {
            let parse_result = parser.parse();
            assert!(
                parse_result.is_ok() || parse_result.is_err(),
                "Input redirection vs file args documented"
            );
        }
        Err(_) => {
            // Parse error acceptable
        }
    }
}

#[test]
fn test_REDIR_001_while_read_pattern() {
    // DOCUMENTATION: while read loop with input redirection
    //
    // Common pattern: Read file line-by-line
    // $ while read line; do
    // >   echo "Line: $line"
    // > done < input.txt
    //
    // Alternative without redirection:
    // $ cat input.txt | while read line; do
    // >   echo "Line: $line"
    // > done
    //
    // Difference:
    // - Redirection (<): while loop runs in current shell
    // - Pipe (|): while loop runs in subshell (variables lost)
    //
    // bashrs recommendation: Use < redirection when possible

    let while_read = r#"
#!/bin/sh
# Read file line-by-line with < redirection
while read line; do
    printf 'Line: %s\n' "$line"
done < input.txt

# Count lines in file
count=0
while read line; do
    count=$((count + 1))
done < data.txt
printf 'Total lines: %d\n' "$count"
"#;

    let result = BashParser::new(while_read);
    match result {
        Ok(mut parser) => {
            let parse_result = parser.parse();
            assert!(
                parse_result.is_ok() || parse_result.is_err(),
                "while read with < is POSIX-compliant"
            );
        }
        Err(_) => {
            // Parse error acceptable
        }
    }
}

#[test]
fn test_REDIR_001_multiple_redirections() {
    // DOCUMENTATION: Multiple redirections on same command
    //
    // Can combine input (<) with output (>, >>):
    // $ sort < input.txt > output.txt
    // $ grep "pattern" < file.txt >> results.txt
    //
    // Order doesn't matter for < and >:
    // $ sort < input.txt > output.txt
    // $ sort > output.txt < input.txt
    // (Both equivalent)
    //
    // File descriptors:
    // - < redirects fd 0 (stdin)
    // - > redirects fd 1 (stdout)
    // - 2> redirects fd 2 (stderr)

    let multiple_redir = r#"
#!/bin/sh
# Sort file and save result
sort < input.txt > output.txt

# Filter and append to results
grep "ERROR" < logfile.txt >> errors.txt

# Order doesn't matter
tr 'a-z' 'A-Z' > uppercase.txt < lowercase.txt
"#;

    let result = BashParser::new(multiple_redir);
    match result {
        Ok(mut parser) => {
            let parse_result = parser.parse();
            assert!(
                parse_result.is_ok() || parse_result.is_err(),
                "Multiple redirections are POSIX-compliant"
            );
        }
        Err(_) => {
            // Parse error acceptable
        }
    }
}

#[test]
fn test_REDIR_001_rust_file_open_mapping() {
    // DOCUMENTATION: Rust File::open() mapping for input redirection
    //
    // Bash input redirection:
    // $ grep "pattern" < input.txt
    //
    // Rust equivalent (Option 1 - File::open):
    // use std::fs::File;
    // use std::io::{BufReader, BufRead};
    //
    // let file = File::open("input.txt")?;
    // let reader = BufReader::new(file);
    // for line in reader.lines() {
    //     if line?.contains("pattern") {
    //         println!("{}", line?);
    //     }
    // }
    //
    // Rust equivalent (Option 2 - Command with file arg):
    // Command::new("grep")
    //     .arg("pattern")
    //     .arg("input.txt")
    //     .output()?;
    //
    // bashrs strategy:
    // - Prefer file arguments when command supports them
    // - Use File::open() + stdin redirect when needed
    // - Quote filenames to prevent injection

    // This test documents the Rust mapping strategy
}

#[test]
fn test_REDIR_001_error_handling() {
    // DOCUMENTATION: Error handling for input redirection
    //
    // File errors:
    // - File doesn't exist: Shell prints error, command doesn't run
    // - No read permission: Shell prints error, command doesn't run
    // - File is directory: Shell prints error, command doesn't run
    //
    // Examples:
    // $ cat < missing.txt
    // sh: missing.txt: No such file or directory
    //
    // $ cat < /etc/shadow
    // sh: /etc/shadow: Permission denied
    //
    // Exit status: Non-zero (typically 1) when file open fails

    let error_handling = r#"
#!/bin/sh
# Check if file exists before redirecting
if [ -f input.txt ]; then
    grep "pattern" < input.txt
else
    printf 'Error: input.txt not found\n' >&2
    exit 1
fi

# Check read permissions
if [ -r data.txt ]; then
    wc -l < data.txt
else
    printf 'Error: Cannot read data.txt\n' >&2
    exit 1
fi
"#;

    let result = BashParser::new(error_handling);
    match result {
        Ok(mut parser) => {
            let parse_result = parser.parse();
            assert!(
                parse_result.is_ok() || parse_result.is_err(),
                "Error handling is POSIX-compliant"
            );
        }
        Err(_) => {
            // Parse error acceptable
        }
    }
}

#[test]
fn test_REDIR_001_common_use_cases() {
    // DOCUMENTATION: Common use cases for input redirection
    //
    // Use Case 1: Count lines in file
    // $ wc -l < file.txt
    //
    // Use Case 2: Sort file contents
    // $ sort < unsorted.txt > sorted.txt
    //
    // Use Case 3: Search in file
    // $ grep "pattern" < logfile.txt
    //
    // Use Case 4: Process file line-by-line
    // $ while read line; do echo "$line"; done < file.txt
    //
    // Use Case 5: Transform file contents
    // $ tr 'a-z' 'A-Z' < lowercase.txt > uppercase.txt
    //
    // Use Case 6: Filter and count
    // $ grep "ERROR" < logfile.txt | wc -l

    let use_cases = r#"
#!/bin/sh
# Use Case 1: Count lines
wc -l < file.txt

# Use Case 2: Sort file
sort < unsorted.txt > sorted.txt

# Use Case 3: Search in file
grep "pattern" < logfile.txt

# Use Case 4: Process line-by-line
while read line; do
    printf 'Line: %s\n' "$line"
done < file.txt

# Use Case 5: Transform contents
tr 'a-z' 'A-Z' < lowercase.txt > uppercase.txt

# Use Case 6: Filter and count
grep "ERROR" < logfile.txt | wc -l
"#;

    let result = BashParser::new(use_cases);
    match result {
        Ok(mut parser) => {
            let parse_result = parser.parse();
            assert!(
                parse_result.is_ok() || parse_result.is_err(),
                "Common use cases are POSIX-compliant"
            );
        }
        Err(_) => {
            // Parse error acceptable
        }
    }
}

#[test]
fn test_REDIR_001_bash_vs_posix_input_redir() {
    // DOCUMENTATION: Bash vs POSIX input redirection features
    //
    // Feature                  | POSIX sh           | Bash extensions
    // -------------------------|-------------------|------------------
    // Basic < redirect         | ✅ Supported       | ✅ Supported
    // File descriptor (0<)     | ✅ Supported       | ✅ Supported
    // Here-document (<<)       | ✅ Supported       | ✅ Supported
    // Here-string (<<<)        | ❌ Not available   | ✅ Bash 2.05b+
    // Process substitution     | ❌ Not available   | ✅ <(cmd)
    // Named pipes (FIFOs)      | ✅ Supported       | ✅ Supported
    //
    // bashrs policy:
    // - Support POSIX < redirection fully
    // - Support << here-documents (POSIX)
    // - NOT SUPPORTED: <<< here-strings, <(cmd) process substitution
    // - Generate POSIX-compliant redirections only

    let posix_redir = r#"cat < file.txt"#;
    let bash_herestring = r#"grep "pattern" <<< "$variable""#;

    // POSIX input redirection - SUPPORTED
    let posix_result = BashParser::new(posix_redir);
    match posix_result {
        Ok(mut parser) => {
            let _ = parser.parse();
            // POSIX < should parse (if implemented)
        }
        Err(_) => {
            // Parse error acceptable if not yet implemented
        }
    }

    // Bash here-string - NOT SUPPORTED (Bash extension)
    let bash_result = BashParser::new(bash_herestring);
    match bash_result {
        Ok(mut parser) => {
            let _ = parser.parse();
            // <<< is Bash extension, may or may not parse
        }
        Err(_) => {
            // Parse error expected for Bash extensions
        }
    }

    // Summary:
    // POSIX input redirection: Fully supported (<, <<, fd redirects)
    // Bash extensions: NOT SUPPORTED (<<<, <(cmd))
    // bashrs: Generate POSIX-compliant redirections only
}

// ============================================================================
// REDIR-002: Output Redirection (>, >>) (POSIX, SUPPORTED)
// ============================================================================

#[test]

include!("part3_2_redir_002.rs");