| 22 | } |
| 23 | |
| 24 | pub(crate) fn create_diff_summary( |
| 25 | title: &str, |
| 26 | changes: &HashMap<PathBuf, FileChange>, |
| 27 | event_type: PatchEventType, |
| 28 | ) -> Vec<RtLine<'static>> { |
| 29 | struct FileSummary { |
| 30 | display_path: String, |
| 31 | added: usize, |
| 32 | removed: usize, |
| 33 | } |
| 34 | |
| 35 | let count_from_unified = |diff: &str| -> (usize, usize) { |
| 36 | if let Ok(patch) = diffy::Patch::from_str(diff) { |
| 37 | patch |
| 38 | .hunks() |
| 39 | .iter() |
| 40 | .flat_map(|h| h.lines()) |
| 41 | .fold((0, 0), |(a, d), l| match l { |
| 42 | diffy::Line::Insert(_) => (a + 1, d), |
| 43 | diffy::Line::Delete(_) => (a, d + 1), |
| 44 | _ => (a, d), |
| 45 | }) |
| 46 | } else { |
| 47 | // Fallback: manual scan to preserve counts even for unparsable diffs |
| 48 | let mut adds = 0usize; |
| 49 | let mut dels = 0usize; |
| 50 | for l in diff.lines() { |
| 51 | if l.starts_with("+++") || l.starts_with("---") || l.starts_with("@@") { |
| 52 | continue; |
| 53 | } |
| 54 | match l.as_bytes().first() { |
| 55 | Some(b'+') => adds += 1, |
| 56 | Some(b'-') => dels += 1, |
| 57 | _ => {} |
| 58 | } |
| 59 | } |
| 60 | (adds, dels) |
| 61 | } |
| 62 | }; |
| 63 | |
| 64 | let mut files: Vec<FileSummary> = Vec::new(); |
| 65 | for (path, change) in changes.iter() { |
| 66 | match change { |
| 67 | FileChange::Add { content } => files.push(FileSummary { |
| 68 | display_path: path.display().to_string(), |
| 69 | added: content.lines().count(), |
| 70 | removed: 0, |
| 71 | }), |
| 72 | FileChange::Delete => files.push(FileSummary { |
| 73 | display_path: path.display().to_string(), |
| 74 | added: 0, |
| 75 | removed: std::fs::read_to_string(path) |
| 76 | .ok() |
| 77 | .map(|s| s.lines().count()) |
| 78 | .unwrap_or(0), |
| 79 | }), |
| 80 | FileChange::Update { |
| 81 | unified_diff, |