Skip to main content

whycodes_core/
edit_trailer.rs

1//! Machine-readable line counts at the end of a file-mutating tool result.
2//!
3//! `write`, `edit` and `apply_patch` end a successful result with a line of
4//! the form `[+12 -3]`; the session sums these into its edit statistics.
5//! The counts are what the tool replaced (an `edit` counts the whole
6//! replaced block), not a minimal diff, so consumers label them approximate.
7
8/// `[+added -removed]`, placed on its own line at the end of a result.
9pub fn edit_trailer(added: usize, removed: usize) -> String {
10    format!("[+{added} -{removed}]")
11}
12
13/// Sum every `[+N -M]` line in `content` (a multi-file patch emits one per
14/// file). `None` when there is no trailer at all.
15pub fn parse_edit_trailers(content: &str) -> Option<(u64, u64)> {
16    let mut found = false;
17    let mut added = 0u64;
18    let mut removed = 0u64;
19    for line in content.lines() {
20        let Some((plus, minus)) = parse_one(line.trim_end()) else {
21            continue;
22        };
23        found = true;
24        added += plus;
25        removed += minus;
26    }
27    found.then_some((added, removed))
28}
29
30fn parse_one(line: &str) -> Option<(u64, u64)> {
31    let inner = line.strip_prefix("[+")?.strip_suffix(']')?;
32    let (plus, minus) = inner.split_once(" -")?;
33    Some((plus.parse().ok()?, minus.parse().ok()?))
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn trailer_round_trips_and_sums() {
42        assert_eq!(edit_trailer(12, 3), "[+12 -3]");
43        assert_eq!(parse_edit_trailers("Wrote a.rs\n\n[+12 -3]"), Some((12, 3)));
44        assert_eq!(
45            parse_edit_trailers(
46                "Patch applied to `a` (1 hunk).\n[+1 -1]\nPatch applied to `b` (2 hunks).\n[+4 -0]\n"
47            ),
48            Some((5, 1))
49        );
50        assert_eq!(parse_edit_trailers("Edited a.rs"), None);
51        assert_eq!(parse_edit_trailers("[+x -1]"), None);
52        assert_eq!(parse_edit_trailers("[+1 -]"), None);
53        assert_eq!(parse_edit_trailers("[+1]"), None);
54        assert_eq!(parse_edit_trailers("+1 -2]"), None);
55    }
56}