ralph_workflow/git_helpers/repo/diff_review.rs
1/// The level of truncation applied to a diff for review.
2///
3/// This enum tracks how much a diff has been abbreviated and determines
4/// what instructions should be given to the reviewer agent.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
6pub enum DiffTruncationLevel {
7 /// No truncation - full diff is included
8 #[default]
9 Full,
10 /// Diff was semantically truncated - high-priority files shown, instruction to explore
11 Abbreviated,
12 /// Only file paths listed - instruction to explore each file's diff
13 FileList,
14 /// File list was abbreviated - instruction to explore and discover files
15 FileListAbbreviated,
16}
17
18/// The result of diff truncation for review purposes.
19///
20/// Contains both the potentially-truncated content and metadata about
21/// what truncation was applied, along with version context information.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct DiffReviewContent {
24 /// The content to include in the review prompt
25 pub content: String,
26 /// The level of truncation applied
27 pub truncation_level: DiffTruncationLevel,
28 /// Total number of files in the full diff (for context in messages)
29 pub total_file_count: usize,
30 /// Number of files shown in the abbreviated content (if applicable)
31 pub shown_file_count: Option<usize>,
32 /// The OID (commit SHA) that this diff is compared against (baseline)
33 pub baseline_oid: Option<String>,
34 /// Short form (first 8 chars) of the baseline OID for display
35 pub baseline_short: Option<String>,
36 /// Description of what the baseline represents (e.g., "`review_baseline`", "`start_commit`")
37 pub baseline_description: String,
38}
39
40impl DiffReviewContent {
41 /// Generate a human-readable header describing the diff's version context.
42 ///
43 /// This header is meant to be included at the beginning of the diff content
44 /// to provide clarity about what state of the code the diff represents.
45 ///
46 /// # Returns
47 ///
48 /// A formatted string like:
49 /// ```text
50 /// Diff Context: Compared against review_baseline abc12345
51 /// Current state: Working directory (includes unstaged changes)
52 /// ```
53 ///
54 /// If no baseline information is available, returns a generic message.
55 #[must_use]
56 pub fn format_context_header(&self) -> String {
57 let baseline_line = self
58 .baseline_short
59 .as_ref()
60 .map(|short| {
61 format!(
62 "Diff Context: Compared against {} {}",
63 self.baseline_description, short
64 )
65 })
66 .unwrap_or_else(|| "Diff Context: Version information not available".to_string());
67
68 let truncation_line = match self.truncation_level {
69 DiffTruncationLevel::Full => None,
70 DiffTruncationLevel::Abbreviated => Some(format!(
71 "Note: Diff abbreviated - {}/{} files shown",
72 self.shown_file_count.unwrap_or(0),
73 self.total_file_count
74 )),
75 DiffTruncationLevel::FileList => Some(format!(
76 "Note: Only file list shown - {} files changed",
77 self.total_file_count
78 )),
79 DiffTruncationLevel::FileListAbbreviated => Some(format!(
80 "Note: File list abbreviated - {}/{} files shown",
81 self.shown_file_count.unwrap_or(0),
82 self.total_file_count
83 )),
84 };
85
86 let lines: Vec<String> = [Some(baseline_line), truncation_line]
87 .into_iter()
88 .flatten()
89 .collect();
90
91 if lines.is_empty() {
92 String::new()
93 } else {
94 format!("{}\n", lines.join("\n"))
95 }
96 }
97}