Skip to main content

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}