LCOV - code coverage report
Current view: top level - src - diff.rs (source / functions) Coverage Total Hit
Test: Hunky Coverage Lines: 100.0 % 64 64
Test Date: 2026-10-10 16:52:03 Functions: 50.0 % 22 11

            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            1 :     pub fn format(&self) -> String {
      47            1 :         self.lines.join("")
      48            1 :     }
      49              : 
      50            1 :     pub fn count_changes(&self) -> usize {
      51            1 :         let mut add_lines = 0;
      52            1 :         let mut remove_lines = 0;
      53              : 
      54            3 :         for line in &self.lines {
      55            3 :             if line.starts_with('+') && !line.starts_with("+++") {
      56            2 :                 add_lines += 1;
      57            2 :             } else if line.starts_with('-') && !line.starts_with("---") {
      58            1 :                 remove_lines += 1;
      59            1 :             }
      60              :         }
      61              : 
      62              :         // Count pairs of add/remove as 1 change, plus any unpaired lines
      63            1 :         let pairs = add_lines.min(remove_lines);
      64            1 :         let unpaired = (add_lines + remove_lines) - (2 * pairs);
      65            1 :         pairs + unpaired
      66            1 :     }
      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 index_only: bool,
      87              :     pub file_path: PathBuf,
      88              :     pub old_start: usize,
      89              :     pub new_start: usize,
      90              :     pub content_hash: u64,
      91              : }
      92              : 
      93              : impl HunkId {
      94           71 :     pub fn new(file_path: &Path, old_start: usize, new_start: usize, lines: &[String]) -> Self {
      95              :         use std::collections::hash_map::DefaultHasher;
      96              : 
      97           71 :         let mut hasher = DefaultHasher::new();
      98          200 :         for line in lines {
      99          200 :             line.hash(&mut hasher);
     100          200 :         }
     101           71 :         let content_hash = hasher.finish();
     102              : 
     103           71 :         Self {
     104           71 :             index_only: false,
     105           71 :             file_path: file_path.to_path_buf(),
     106           71 :             old_start,
     107           71 :             new_start,
     108           71 :             content_hash,
     109           71 :         }
     110           71 :     }
     111              : }
     112              : 
     113              : /// Tracks which hunks have been seen by the user
     114              : #[derive(Debug, Clone)]
     115              : pub struct SeenTracker {
     116              :     seen_hunks: HashSet<HunkId>,
     117              : }
     118              : 
     119              : #[allow(dead_code)]
     120              : impl SeenTracker {
     121            2 :     pub fn new() -> Self {
     122            2 :         Self {
     123            2 :             seen_hunks: HashSet::new(),
     124            2 :         }
     125            2 :     }
     126              : 
     127            3 :     pub fn mark_seen(&mut self, hunk_id: &HunkId) {
     128            3 :         self.seen_hunks.insert(hunk_id.clone());
     129            3 :     }
     130              : 
     131            6 :     pub fn is_seen(&self, hunk_id: &HunkId) -> bool {
     132            6 :         self.seen_hunks.contains(hunk_id)
     133            6 :     }
     134              : 
     135            1 :     pub fn clear(&mut self) {
     136            1 :         self.seen_hunks.clear();
     137            1 :     }
     138              : 
     139              :     #[allow(dead_code)]
     140            1 :     pub fn remove_file_hunks(&mut self, file_path: &PathBuf) {
     141            1 :         self.seen_hunks
     142            1 :             .retain(|hunk_id| &hunk_id.file_path != file_path);
     143            1 :     }
     144              : }
     145              : 
     146              : impl Default for SeenTracker {
     147            1 :     fn default() -> Self {
     148            1 :         Self::new()
     149            1 :     }
     150              : }
     151              : 
     152              : #[cfg(test)]
     153              : #[path = "../tests/diff.rs"]
     154              : mod tests;
        

Generated by: LCOV version 2.0-1