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 encryption_manager = match passphrase {
62 Some(pass) => {
63 let mut manager = EncryptionManager::new(encryption_config);
64 manager.initialize(pass)?;
65 manager
66 }
67 None => EncryptionManager::new_auto(encryption_config, repo_path),
68 };
69
70 encryption_manager.decrypt(&encrypted_data)?
71 };
72
73 serde_json::from_slice(&data).map_err(|e| format!("Failed to parse index: {}", e))
74 }
75
76 pub fn save(&self, repo_path: &Path) -> Result<(), String> {
78 self.save_with_encryption(repo_path, None)
79 }
80
81 pub fn save_with_encryption(
83 &self,
84 repo_path: &Path,
85 passphrase: Option<&str>,
86 ) -> Result<(), String> {
87 let index_path = repo_path.join(".lit").join("index");
88
89 let data = serde_json::to_vec_pretty(self)
90 .map_err(|e| format!("Failed to serialize index: {}", e))?;
91
92 let final_data = {
94 let encryption_config = EncryptionConfig::load(repo_path)?;
95 let encryption_manager = match passphrase {
96 Some(pass) => {
97 let mut manager = EncryptionManager::new(encryption_config);
98 manager.initialize(pass)?;
99 manager
100 }
101 None => EncryptionManager::new_auto(encryption_config, repo_path),
102 };
103
104 encryption_manager.encrypt(&data)?
105 };
106
107 fs::write(&index_path, final_data).map_err(|e| format!("Failed to write index: {}", e))
108 }
109
110 pub fn add(&mut self, path: String, hash: String, mode: String) {
112 self.entries
113 .insert(path.clone(), IndexEntry { path, hash, mode });
114 }
115
116 pub fn remove(&mut self, path: &str) -> Option<IndexEntry> {
118 self.entries.remove(path)
119 }
120
121 pub fn sorted_entries(&self) -> Vec<&IndexEntry> {
123 let mut entries: Vec<&IndexEntry> = self.entries.values().collect();
124 entries.sort_by(|a, b| a.path.cmp(&b.path));
125 entries
126 }
127
128 pub fn clear(&mut self) {
130 self.entries.clear();
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use tempfile::TempDir;
138
139 #[test]
140 fn test_index() {
141 let temp_dir = TempDir::new().unwrap();
142 let repo_path = temp_dir.path();
143
144 fs::create_dir_all(repo_path.join(".lit")).unwrap();
145
146 let mut index = Index::new();
147 index.add(
148 "file.txt".to_string(),
149 "abc123".to_string(),
150 "100644".to_string(),
151 );
152
153 index.save(repo_path).unwrap();
154
155 let loaded = Index::load(repo_path).unwrap();
156 assert_eq!(loaded.entries.len(), 1);
157 assert!(loaded.entries.contains_key("file.txt"));
158 }
159}