1use crate::crypto::encryption::{EncryptionConfig, EncryptionManager};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::fs;
5use std::path::Path;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct IndexEntry {
10 pub path: String,
11 pub hash: String,
12 pub mode: String,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Index {
18 pub entries: HashMap<String, IndexEntry>,
19}
20
21impl Default for Index {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl Index {
28 pub fn new() -> Self {
30 Index {
31 entries: HashMap::new(),
32 }
33 }
34
35 pub fn load(repo_path: &Path) -> Result<Self, String> {
37 Self::load_with_encryption(repo_path, None)
38 }
39
40 pub fn load_with_encryption(
42 repo_path: &Path,
43 passphrase: Option<&str>,
44 ) -> Result<Self, String> {
45 let index_path = repo_path.join(".lit").join("index");
46
47 if !index_path.exists() {
48 return Ok(Index::new());
49 }
50
51 let encrypted_data =
52 fs::read(&index_path).map_err(|e| format!("Failed to read index: {}", e))?;
53
54 let data = {
56 let encryption_config = EncryptionConfig::load(repo_path)?;
57 let mut encryption_manager = EncryptionManager::new(encryption_config);
58
59 if let Some(pass) = passphrase {
60 encryption_manager.initialize(pass)?;
61 }
62
63 encryption_manager.decrypt(&encrypted_data)?
64 };
65
66 serde_json::from_slice(&data).map_err(|e| format!("Failed to parse index: {}", e))
67 }
68
69 pub fn save(&self, repo_path: &Path) -> Result<(), String> {
71 self.save_with_encryption(repo_path, None)
72 }
73
74 pub fn save_with_encryption(
76 &self,
77 repo_path: &Path,
78 passphrase: Option<&str>,
79 ) -> Result<(), String> {
80 let index_path = repo_path.join(".lit").join("index");
81
82 let data = serde_json::to_vec_pretty(self)
83 .map_err(|e| format!("Failed to serialize index: {}", e))?;
84
85 let final_data = {
87 let encryption_config = EncryptionConfig::load(repo_path)?;
88 let mut encryption_manager = EncryptionManager::new(encryption_config);
89
90 if let Some(pass) = passphrase {
91 encryption_manager.initialize(pass)?;
92 }
93
94 encryption_manager.encrypt(&data)?
95 };
96
97 fs::write(&index_path, final_data).map_err(|e| format!("Failed to write index: {}", e))
98 }
99
100 pub fn add(&mut self, path: String, hash: String, mode: String) {
102 self.entries
103 .insert(path.clone(), IndexEntry { path, hash, mode });
104 }
105
106 pub fn remove(&mut self, path: &str) -> Option<IndexEntry> {
108 self.entries.remove(path)
109 }
110
111 pub fn sorted_entries(&self) -> Vec<&IndexEntry> {
113 let mut entries: Vec<&IndexEntry> = self.entries.values().collect();
114 entries.sort_by(|a, b| a.path.cmp(&b.path));
115 entries
116 }
117
118 pub fn clear(&mut self) {
120 self.entries.clear();
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use tempfile::TempDir;
128
129 #[test]
130 fn test_index() {
131 let temp_dir = TempDir::new().unwrap();
132 let repo_path = temp_dir.path();
133
134 fs::create_dir_all(repo_path.join(".lit")).unwrap();
135
136 let mut index = Index::new();
137 index.add(
138 "file.txt".to_string(),
139 "abc123".to_string(),
140 "100644".to_string(),
141 );
142
143 index.save(repo_path).unwrap();
144
145 let loaded = Index::load(repo_path).unwrap();
146 assert_eq!(loaded.entries.len(), 1);
147 assert!(loaded.entries.contains_key("file.txt"));
148 }
149}