eazygit 0.5.1

A fast TUI for Git with staging, conflicts, rebase, and palette-first UX
Documentation
//! Clipboard manager for copy/paste operations.
//!
//! Provides an internal clipboard for copying commit hashes,
//! file paths, and other text between operations.

use std::collections::VecDeque;
use std::time::Instant;

/// Type of clipboard content.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ClipboardType {
    /// Commit hash
    CommitHash,
    /// File path
    FilePath,
    /// Branch name
    BranchName,
    /// Plain text
    Text,
}

/// A clipboard entry.
#[derive(Debug, Clone)]
pub struct ClipboardEntry {
    /// Content
    pub content: String,
    /// Type of content
    pub content_type: ClipboardType,
    /// When copied
    pub copied_at: Instant,
}

/// Clipboard manager with history.
pub struct ClipboardManager {
    /// Current clipboard content
    current: Option<ClipboardEntry>,
    /// Clipboard history
    history: VecDeque<ClipboardEntry>,
    /// Max history size
    max_history: usize,
}

impl ClipboardManager {
    /// Create a new clipboard manager.
    pub fn new(max_history: usize) -> Self {
        Self {
            current: None,
            history: VecDeque::with_capacity(max_history),
            max_history,
        }
    }
    
    /// Copy content to clipboard.
    pub fn copy(&mut self, content: impl Into<String>, content_type: ClipboardType) {
        let entry = ClipboardEntry {
            content: content.into(),
            content_type,
            copied_at: Instant::now(),
        };
        
        // Save old to history
        if let Some(old) = self.current.take() {
            if self.history.len() >= self.max_history {
                self.history.pop_front();
            }
            self.history.push_back(old);
        }
        
        self.current = Some(entry);
    }
    
    /// Copy a commit hash.
    pub fn copy_hash(&mut self, hash: impl Into<String>) {
        self.copy(hash, ClipboardType::CommitHash);
    }
    
    /// Copy a file path.
    pub fn copy_path(&mut self, path: impl Into<String>) {
        self.copy(path, ClipboardType::FilePath);
    }
    
    /// Copy a branch name.
    pub fn copy_branch(&mut self, branch: impl Into<String>) {
        self.copy(branch, ClipboardType::BranchName);
    }
    
    /// Get current clipboard content.
    pub fn get(&self) -> Option<&str> {
        self.current.as_ref().map(|e| e.content.as_str())
    }
    
    /// Get current clipboard entry.
    pub fn current(&self) -> Option<&ClipboardEntry> {
        self.current.as_ref()
    }
    
    /// Get clipboard history.
    pub fn history(&self) -> impl Iterator<Item = &ClipboardEntry> {
        self.history.iter().rev()
    }
    
    /// Restore from history by index (0 = most recent).
    pub fn restore(&mut self, index: usize) -> bool {
        let history_vec: Vec<_> = self.history.iter().rev().cloned().collect();
        if let Some(entry) = history_vec.get(index).cloned() {
            self.copy(entry.content, entry.content_type);
            true
        } else {
            false
        }
    }
    
    /// Check if clipboard has content.
    pub fn has_content(&self) -> bool {
        self.current.is_some()
    }
    
    /// Clear clipboard.
    pub fn clear(&mut self) {
        self.current = None;
    }
    
    /// Get history count.
    pub fn history_count(&self) -> usize {
        self.history.len()
    }
}

impl Default for ClipboardManager {
    fn default() -> Self {
        Self::new(20) // Keep 20 items in history
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_copy_and_get() {
        let mut cm = ClipboardManager::default();
        
        cm.copy_hash("abc123");
        assert_eq!(cm.get(), Some("abc123"));
        assert!(matches!(cm.current().unwrap().content_type, ClipboardType::CommitHash));
    }
    
    #[test]
    fn test_history() {
        let mut cm = ClipboardManager::default();
        
        cm.copy_hash("hash1");
        cm.copy_hash("hash2");
        cm.copy_hash("hash3");
        
        assert_eq!(cm.get(), Some("hash3"));
        assert_eq!(cm.history_count(), 2);
    }
}