1use std::path::PathBuf;
2
3use termesh_core::{GitContextDiff, GitDiffTarget, GitFileDiff, GitResult};
4
5pub fn bounded_diff(
6 path: PathBuf,
7 target: GitDiffTarget,
8 bytes: &[u8],
9 limit: usize,
10) -> GitResult<GitFileDiff> {
11 let (text, truncated) = bounded_text(bytes, limit);
12 Ok(GitFileDiff { path, target, text, truncated })
13}
14
15pub fn bounded_context_diff(
16 index: &[u8],
17 worktree: &[u8],
18 limit: usize,
19) -> GitResult<GitContextDiff> {
20 let (index, index_truncated) = bounded_text(index, limit);
21 let (worktree, worktree_truncated) = bounded_text(worktree, limit);
22 Ok(GitContextDiff { index, worktree, index_truncated, worktree_truncated })
23}
24
25fn bounded_text(bytes: &[u8], limit: usize) -> (String, bool) {
26 let text = String::from_utf8_lossy(bytes);
27 let truncated = bytes.len() > limit || text.len() > limit;
28 if !truncated {
29 return (text.into_owned(), false);
30 }
31
32 let mut end = limit.min(text.len());
33 while end > 0 && !text.is_char_boundary(end) {
34 end -= 1;
35 }
36 (text[..end].to_owned(), true)
37}
38
39#[cfg(test)]
40mod tests {
41 use termesh_core::GitDiffTarget;
42
43 use super::{bounded_context_diff, bounded_diff};
44
45 #[test]
46 fn bounded_diff_keeps_complete_utf8_and_marks_truncation() {
47 let diff =
50 bounded_diff("src/lib.rs".into(), GitDiffTarget::Worktree, "+αβγ\n".as_bytes(), 5)
51 .unwrap();
52 assert!(diff.truncated);
53 assert!(diff.text.is_char_boundary(diff.text.len()));
54 assert_eq!(diff.text, "+αβ");
55 }
56
57 #[test]
58 fn context_diff_bounds_each_side_independently() {
59 let diff = bounded_context_diff(b"staged-contents", b"worktree-contents", 7).unwrap();
60 assert!(diff.index_truncated);
61 assert!(diff.worktree_truncated);
62 assert_eq!(diff.index, "staged-");
63 assert_eq!(diff.worktree, "worktre");
64 }
65
66 #[test]
67 fn invalid_process_bytes_are_replaced_not_rejected() {
68 let diff =
69 bounded_diff("binary-ish".into(), GitDiffTarget::Index, b"ok\xfftail", 64).unwrap();
70 assert_eq!(diff.text, "ok�tail");
71 assert!(!diff.truncated);
72 }
73}