pocket_cli/vcs/
pile.rs

1//! Pile (staging area) functionality for Pocket VCS
2//!
3//! Handles the staging of changes before they are committed.
4
5use std::path::{Path, PathBuf};
6use std::collections::HashMap;
7use std::fs;
8use serde::{Serialize, Deserialize};
9use anyhow::{Result, anyhow};
10
11use crate::vcs::{ObjectId, ObjectStore, ShoveId};
12
13/// Status of a file in the pile
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub enum PileStatus {
16    Added,
17    Modified,
18    Deleted,
19    Renamed(PathBuf),
20}
21
22/// An entry in the pile
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct PileEntry {
25    pub status: PileStatus,
26    pub object_id: ObjectId,
27    pub original_path: PathBuf,
28}
29
30/// The pile (staging area)
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct Pile {
33    /// The base shove this pile is built on
34    pub base_shove: Option<ShoveId>,
35    
36    /// Entries in the pile, keyed by path
37    pub entries: HashMap<PathBuf, PileEntry>,
38}
39
40impl Pile {
41    /// Create a new empty pile
42    pub fn new() -> Self {
43        Self {
44            base_shove: None,
45            entries: HashMap::new(),
46        }
47    }
48    
49    /// Load a pile from a file
50    pub fn load(path: &Path) -> Result<Self> {
51        if !path.exists() {
52            return Ok(Self::new());
53        }
54        
55        let content = fs::read_to_string(path)?;
56        let pile: Self = toml::from_str(&content)?;
57        Ok(pile)
58    }
59    
60    /// Save the pile to a file
61    pub fn save(&self, path: &Path) -> Result<()> {
62        let content = toml::to_string_pretty(self)?;
63        fs::write(path, content)?;
64        Ok(())
65    }
66    
67    /// Add a file to the pile
68    pub fn add_path(&mut self, path: &Path, object_store: &ObjectStore) -> Result<()> {
69        // Store the file content in the object store
70        let object_id = object_store.store_file(path)?;
71        
72        // Add the file to the pile
73        let entry = PileEntry {
74            status: PileStatus::Added, // This would be determined based on repo state
75            object_id,
76            original_path: path.to_path_buf(),
77        };
78        
79        self.entries.insert(path.to_path_buf(), entry);
80        Ok(())
81    }
82    
83    /// Remove a file from the pile
84    pub fn remove_path(&mut self, path: &Path) -> Result<()> {
85        if !self.entries.contains_key(path) {
86            return Err(anyhow!("Path not in pile: {}", path.display()));
87        }
88        
89        self.entries.remove(path);
90        Ok(())
91    }
92    
93    /// Clear the pile
94    pub fn clear(&mut self) -> Result<()> {
95        self.entries.clear();
96        Ok(())
97    }
98    
99    /// Check if the pile is empty
100    pub fn is_empty(&self) -> bool {
101        self.entries.is_empty()
102    }
103    
104    /// Get the number of entries in the pile
105    pub fn len(&self) -> usize {
106        self.entries.len()
107    }
108    
109    // Additional methods would be implemented here:
110    // - add_all: Add all changes to the pile
111    // - add_pattern: Add files matching a pattern
112    // - add_interactive: Interactive adding
113    // - etc.
114}