shout/src/update.rs
2026-04-02 15:18:22 -07:00

116 lines
3.6 KiB
Rust

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<String> = 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<String> = 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::<i32>().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")
}