LCOV - code coverage report
Current view: top level - src - diff.rs (source / functions) Coverage Total Hit
Test: Hunky Coverage Lines: 100.0 % 63 63
Test Date: 2026-02-25 04:31:59 Functions: 100.0 % 22 22

            Line data    Source code
       1              : use std::collections::HashSet;
       2              : use std::hash::{Hash, Hasher};
       3              : use std::path::{Path, PathBuf};
       4              : use std::time::SystemTime;
       5              : 
       6              : #[derive(Debug, Clone)]
       7              : pub struct DiffSnapshot {
       8              :     #[allow(dead_code)]
       9              :     pub timestamp: SystemTime,
      10              :     pub files: Vec<FileChange>,
      11              : }
      12              : 
      13              : /// Metadata about a git commit for the review mode commit picker
      14              : #[derive(Debug, Clone)]
      15              : pub struct CommitInfo {
      16              :     pub sha: String,
      17              :     pub short_sha: String,
      18              :     pub summary: String,
      19              :     pub author: String,
      20              : }
      21              : 
      22              : #[derive(Debug, Clone)]
      23              : pub struct FileChange {
      24              :     pub path: PathBuf,
      25              :     pub status: String,
      26              :     pub hunks: Vec<Hunk>,
      27              : }
      28              : 
      29              : #[derive(Debug, Clone)]
      30              : pub struct Hunk {
      31              :     pub old_start: usize,
      32              :     pub new_start: usize,
      33              :     pub lines: Vec<String>,
      34              :     pub seen: bool,
      35              :     pub staged: bool,
      36              :     /// Track which individual lines are staged (by index in lines vec)
      37              :     pub staged_line_indices: HashSet<usize>,
      38              :     /// In-memory tracking for review mode: whether this hunk has been accepted
      39              :     pub accepted: bool,
      40              :     #[allow(dead_code)]
      41              :     pub id: HunkId,
      42              : }
      43              : 
      44              : impl Hunk {
      45              :     #[allow(dead_code)]
      46            2 :     pub fn format(&self) -> String {
      47            2 :         self.lines.join("")
      48            2 :     }
      49              : 
      50            2 :     pub fn count_changes(&self) -> usize {
      51            2 :         let mut add_lines = 0;
      52            2 :         let mut remove_lines = 0;
      53              : 
      54            6 :         for line in &self.lines {
      55            6 :             if line.starts_with('+') && !line.starts_with("+++") {
      56            4 :                 add_lines += 1;
      57            4 :             } else if line.starts_with('-') && !line.starts_with("---") {
      58            2 :                 remove_lines += 1;
      59            2 :             }
      60              :         }
      61              : 
      62              :         // Count pairs of add/remove as 1 change, plus any unpaired lines
      63            2 :         let pairs = add_lines.min(remove_lines);
      64            2 :         let unpaired = (add_lines + remove_lines) - (2 * pairs);
      65            2 :         pairs + unpaired
      66            2 :     }
      67              : 
      68           67 :     pub fn new(old_start: usize, new_start: usize, lines: Vec<String>, file_path: &Path) -> Self {
      69           67 :         let id = HunkId::new(file_path, old_start, new_start, &lines);
      70           67 :         Self {
      71           67 :             old_start,
      72           67 :             new_start,
      73           67 :             lines,
      74           67 :             seen: false,
      75           67 :             staged: false,
      76           67 :             staged_line_indices: HashSet::new(),
      77           67 :             accepted: false,
      78           67 :             id,
      79           67 :         }
      80           67 :     }
      81              : }
      82              : 
      83              : /// Unique identifier for a hunk based on file path, line numbers, and content hash
      84              : #[derive(Debug, Clone, PartialEq, Eq, Hash)]
      85              : pub struct HunkId {
      86              :     pub file_path: PathBuf,
      87              :     pub old_start: usize,
      88              :     pub new_start: usize,
      89              :     pub content_hash: u64,
      90              : }
      91              : 
      92              : impl HunkId {
      93           75 :     pub fn new(file_path: &Path, old_start: usize, new_start: usize, lines: &[String]) -> Self {
      94              :         use std::collections::hash_map::DefaultHasher;
      95              : 
      96           75 :         let mut hasher = DefaultHasher::new();
      97          247 :         for line in lines {
      98          247 :             line.hash(&mut hasher);
      99          247 :         }
     100           75 :         let content_hash = hasher.finish();
     101              : 
     102           75 :         Self {
     103           75 :             file_path: file_path.to_path_buf(),
     104           75 :             old_start,
     105           75 :             new_start,
     106           75 :             content_hash,
     107           75 :         }
     108           75 :     }
     109              : }
     110              : 
     111              : /// Tracks which hunks have been seen by the user
     112              : #[derive(Debug, Clone)]
     113              : pub struct SeenTracker {
     114              :     seen_hunks: HashSet<HunkId>,
     115              : }
     116              : 
     117              : #[allow(dead_code)]
     118              : impl SeenTracker {
     119            4 :     pub fn new() -> Self {
     120            4 :         Self {
     121            4 :             seen_hunks: HashSet::new(),
     122            4 :         }
     123            4 :     }
     124              : 
     125            6 :     pub fn mark_seen(&mut self, hunk_id: &HunkId) {
     126            6 :         self.seen_hunks.insert(hunk_id.clone());
     127            6 :     }
     128              : 
     129           12 :     pub fn is_seen(&self, hunk_id: &HunkId) -> bool {
     130           12 :         self.seen_hunks.contains(hunk_id)
     131           12 :     }
     132              : 
     133            2 :     pub fn clear(&mut self) {
     134            2 :         self.seen_hunks.clear();
     135            2 :     }
     136              : 
     137              :     #[allow(dead_code)]
     138            2 :     pub fn remove_file_hunks(&mut self, file_path: &PathBuf) {
     139            2 :         self.seen_hunks
     140            2 :             .retain(|hunk_id| &hunk_id.file_path != file_path);
     141            2 :     }
     142              : }
     143              : 
     144              : impl Default for SeenTracker {
     145            2 :     fn default() -> Self {
     146            2 :         Self::new()
     147            2 :     }
     148              : }
     149              : 
     150              : #[cfg(test)]
     151              : #[path = "../tests/diff.rs"]
     152              : mod tests;
        

Generated by: LCOV version 2.0-1