eazygit 0.5.1

A fast TUI for Git with staging, conflicts, rebase, and palette-first UX
Documentation
#![allow(dead_code)]
//! Cached diff service wrapper.
//!
//! Wraps GitService.diff() with LRU caching for performance.

use crate::cache::DiffCache;
use crate::services::git::GitService;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};

/// Wrapper around GitService that adds caching for diff operations.
pub struct CachedDiffService {
    git_service: Arc<GitService>,
    cache: Mutex<DiffCache>,
}

impl CachedDiffService {
    /// Create a new cached diff service.
    pub fn new(git_service: Arc<GitService>) -> Self {
        Self {
            git_service,
            cache: Mutex::new(DiffCache::default()),
        }
    }
    
    /// Get diff with caching.
    /// 
    /// If the diff is cached and valid, returns from cache.
    /// Otherwise computes and caches the result.
    pub fn get_diff(
        &self,
        repo_path: &str,
        file_path: &str,
        staged: bool,
        context: usize,
    ) -> Result<DiffResult, String> {
        let path = PathBuf::from(file_path);
        
        // Try cache first
        {
            let mut cache = self.cache.lock().unwrap();
            if let Some(entry) = cache.get(&path, staged) {
                tracing::debug!("Diff cache HIT: {}", file_path);
                return Ok(DiffResult {
                    content: entry.content.clone(),
                    truncated: entry.truncated,
                    total_lines: entry.total_lines,
                    from_cache: true,
                });
            }
        }
        
        tracing::debug!("Diff cache MISS: {}", file_path);
        
        // Compute diff
        let content = self.git_service
            .diff(repo_path, file_path, staged, context)
            .map_err(|e| e.to_string())?;
        
        let total_lines = content.lines().count();
        let truncated = total_lines > 5000;
        
        // Cache result
        {
            let mut cache = self.cache.lock().unwrap();
            cache.put(path, staged, content.clone(), truncated, total_lines);
        }
        
        Ok(DiffResult {
            content,
            truncated,
            total_lines,
            from_cache: false,
        })
    }
    
    /// Invalidate cache for a file (called after staging/unstaging).
    pub fn invalidate(&self, file_path: &str) {
        let path = PathBuf::from(file_path);
        let mut cache = self.cache.lock().unwrap();
        cache.invalidate(&path);
    }
    
    /// Clear entire cache (called after major operations like commit).
    pub fn clear(&self) {
        let mut cache = self.cache.lock().unwrap();
        cache.clear();
    }
    
    /// Get cache statistics.
    pub fn stats(&self) -> crate::cache::diff::CacheStats {
        let cache = self.cache.lock().unwrap();
        cache.stats()
    }
}

/// Result of a diff operation.
#[derive(Debug, Clone)]
pub struct DiffResult {
    pub content: String,
    pub truncated: bool,
    pub total_lines: usize,
    pub from_cache: bool,
}