Build the real per-file unified diff for a change by reusing the exact `atomic diff -c ` builder ([`change_file_diffs`]), then converting each `FileDiff` into the serializable [`DiffFileView`] mirror. Best-effort: on any error this returns an empty vec (the report still carries message/files).
(repo: &Repository, change: &Change, hash: &Hash)
| 995 | /// `FileDiff` into the serializable [`DiffFileView`] mirror. Best-effort: on any |
| 996 | /// error this returns an empty vec (the report still carries message/files). |
| 997 | fn build_change_diff(repo: &Repository, change: &Change, hash: &Hash) -> Vec<DiffFileView> { |
| 998 | // Sensible review defaults: unified, a few context lines, no color/word-diff. |
| 999 | let config = DiffOutputConfig::new().with_color(false); |
| 1000 | debug_assert_eq!(config.format, DiffFormat::Unified); |
| 1001 | |
| 1002 | let file_diffs = match change_file_diffs(repo, change, hash, &config) { |
| 1003 | Ok((file_diffs, _stats)) => file_diffs, |
| 1004 | Err(_) => return Vec::new(), |
| 1005 | }; |
| 1006 | |
| 1007 | let mut out = Vec::with_capacity(file_diffs.len()); |
| 1008 | for fd in file_diffs { |
| 1009 | let status = match fd.status { |
| 1010 | FileChangeStatus::Added => "added", |
| 1011 | FileChangeStatus::Deleted => "deleted", |
| 1012 | _ => "modified", |
| 1013 | } |
| 1014 | .to_string(); |
| 1015 | let path = if !fd.new_path.is_empty() { |
| 1016 | fd.new_path.clone() |
| 1017 | } else { |
| 1018 | fd.old_path.clone() |
| 1019 | }; |
| 1020 | |
| 1021 | let total_lines: usize = fd.hunks.iter().map(|h| h.lines.len()).sum(); |
| 1022 | let hunks = if total_lines > MAX_DIFF_LINES_PER_FILE { |
| 1023 | // Too large to embed: leave a note pointing at the full command. |
| 1024 | vec![DiffHunkView { |
| 1025 | header: format!("@@ {path} @@"), |
| 1026 | lines: vec![DiffLineView { |
| 1027 | tag: " ".to_string(), |
| 1028 | content: format!( |
| 1029 | "[diff omitted: {total_lines} lines exceed embed cap; run: atomic change {} --show-hunks]", |
| 1030 | hash.to_base32() |
| 1031 | ), |
| 1032 | }], |
| 1033 | }] |
| 1034 | } else { |
| 1035 | fd.hunks |
| 1036 | .iter() |
| 1037 | .map(|h| DiffHunkView { |
| 1038 | header: h.header(), |
| 1039 | lines: h |
| 1040 | .lines |
| 1041 | .iter() |
| 1042 | .map(|l| DiffLineView { |
| 1043 | tag: l.status.prefix().to_string(), |
| 1044 | content: l.content.clone(), |
| 1045 | }) |
| 1046 | .collect(), |
| 1047 | }) |
| 1048 | .collect() |
| 1049 | }; |
| 1050 | |
| 1051 | out.push(DiffFileView { |
| 1052 | path, |
| 1053 | status, |
| 1054 | hunks, |
no test coverage detected