use crate::matching::match_output; use crate::parse::{ShoutFile, is_comment_line, trim_trailing_empty}; use crate::run::CommandResult; fn escape_dollar(line: &str) -> String { if line.starts_with("$ ") { format!("\\{line}") } else { line.to_string() } } pub fn rewrite_file( file: &ShoutFile, results: &[CommandResult], original_content: &str, ) -> String { let lines: Vec<&str> = original_content.split('\n').collect(); let mut output: Vec = Vec::new(); let mut cmd_idx = 0; let mut i = 0; while i < lines.len() { let line = lines[i]; if is_comment_line(line) { output.push(line.to_string()); i += 1; } else if line.starts_with("$ ") && !line.starts_with("\\$ ") { // Emit the command line as-is output.push(line.to_string()); let cmd = file.commands.get(cmd_idx); let result = results.get(cmd_idx); if cmd.is_none() || result.is_none() { cmd_idx += 1; i += 1; continue; } let cmd = cmd.unwrap(); let result = result.unwrap(); // Skip past old expected output lines in the original let mut j = i + 1; while j < lines.len() && !is_comment_line(lines[j]) && !(lines[j].starts_with("$ ") && !lines[j].starts_with("\\$ ")) { j += 1; } // Collect old expected lines let old_expected_raw: Vec = lines[i + 1..j].iter().map(|s| s.to_string()).collect(); // Check if old expected output had an exit code marker let old_trimmed = trim_trailing_empty(&old_expected_raw); let old_exit_marker = if let Some(last) = old_trimmed.last() { if last.starts_with('[') && last.ends_with(']') { let inner = &last[1..last.len() - 1]; if inner == "*" || inner.parse::().is_ok() { Some(last.clone()) } else { None } } else { None } } else { None }; // Count trailing blank lines let mut trailing_blanks = 0; for k in (0..old_expected_raw.len()).rev() { if old_expected_raw[k].is_empty() { trailing_blanks += 1; } else { break; } } // If wildcards match, keep original expected output if match_output(&cmd.expected, &result.actual) { for ol in &old_expected_raw { output.push(ol.clone()); } } else { // Replace with actual output for al in &result.actual { output.push(escape_dollar(al)); } // Re-add exit code marker if it existed if let Some(marker) = old_exit_marker { output.push(marker); } // Preserve trailing blank lines as separators for _ in 0..trailing_blanks { output.push(String::new()); } } i = j; cmd_idx += 1; } else if cmd_idx == 0 { // Lines before first command (directives, etc.) output.push(line.to_string()); i += 1; } else { i += 1; } } output.join("\n") }