Parse a script file into items: each command stanza plus the comments and blank lines around it.
(content: &str)
| 59 | /// Parse a script file into items: each command stanza plus the comments and blank |
| 60 | /// lines around it. |
| 61 | pub fn parse_file(content: &str) -> anyhow::Result<Vec<Item>> { |
| 62 | let lines: Vec<&str> = content.lines().collect(); |
| 63 | let mut items = Vec::new(); |
| 64 | let mut i = 0; |
| 65 | while i < lines.len() { |
| 66 | let line = lines[i]; |
| 67 | // Blank lines and column-0 comments are preserved as-is. |
| 68 | if line.trim().is_empty() || line.starts_with('#') { |
| 69 | items.push(Item::Verbatim(line.to_string())); |
| 70 | i += 1; |
| 71 | continue; |
| 72 | } |
| 73 | // A stanza: slurp the input block up to the `----` separator. |
| 74 | let start = i; |
| 75 | while i < lines.len() && lines[i] != "----" { |
| 76 | i += 1; |
| 77 | } |
| 78 | ensure!( |
| 79 | i < lines.len(), |
| 80 | "stanza starting at line {} has no `----` separator", |
| 81 | start + 1 |
| 82 | ); |
| 83 | let input = lines[start..i].join("\n"); |
| 84 | i += 1; // consume `----` |
| 85 | // The expected output runs to the next blank line (or end of file). |
| 86 | let exp_start = i; |
| 87 | while i < lines.len() && !lines[i].trim().is_empty() { |
| 88 | i += 1; |
| 89 | } |
| 90 | let expected = lines[exp_start..i].join("\n"); |
| 91 | let command = parse_command(&input) |
| 92 | .with_context(|| format!("parsing stanza at line {}", start + 1))?; |
| 93 | items.push(Item::Stanza(Stanza { |
| 94 | input, |
| 95 | expected, |
| 96 | command, |
| 97 | })); |
| 98 | } |
| 99 | Ok(items) |
| 100 | } |
| 101 | |
| 102 | /// Reproduce a script file with each stanza's expected output replaced by its |
| 103 | /// actual output, for `REWRITE`. `actuals` has one entry per [`Item::Stanza`], in |
no test coverage detected