Skip to main content

embeddenator_cli/commands/
update.rs

1//! Update command implementations (add, remove, modify, compact)
2//!
3//! These commands enable incremental modifications to existing engrams
4//! without full re-ingestion.
5
6use anyhow::Result;
7use embeddenator_fs::embrfs::EmbrFS;
8use embeddenator_vsa::ReversibleVSAConfig;
9use std::path::PathBuf;
10
11/// Add a new file to an existing engram
12pub fn handle_update_add(
13    engram: PathBuf,
14    manifest: PathBuf,
15    file: PathBuf,
16    logical_path: Option<String>,
17    verbose: bool,
18) -> Result<()> {
19    if verbose {
20        println!(
21            "Embeddenator v{} - Incremental Add",
22            env!("CARGO_PKG_VERSION")
23        );
24        println!("===================================");
25    }
26
27    // Validate inputs
28    if !engram.exists() {
29        anyhow::bail!("Engram file not found: {}", engram.display());
30    }
31    if !manifest.exists() {
32        anyhow::bail!("Manifest file not found: {}", manifest.display());
33    }
34    if !file.exists() {
35        anyhow::bail!("Input file not found: {}", file.display());
36    }
37
38    // Load existing engram and manifest (auto-detects holographic mode)
39    let mut fs = EmbrFS::load(&engram, &manifest)?;
40    let config = ReversibleVSAConfig::default();
41
42    // Determine logical path
43    let logical = logical_path.unwrap_or_else(|| {
44        file.file_name()
45            .and_then(|s| s.to_str())
46            .unwrap_or("unnamed")
47            .to_string()
48    });
49
50    if verbose {
51        println!("Adding file: {} -> {}", file.display(), logical);
52    }
53
54    // Add the file (will fail if already exists)
55    fs.add_file(&file, logical.clone(), verbose, &config)?;
56
57    // Save updated engram and manifest
58    fs.save_engram(&engram)?;
59    fs.save_manifest(&manifest)?;
60
61    if verbose {
62        println!("\nAdd complete!");
63        println!("  File added: {}", logical);
64        println!(
65            "  Total files: {}",
66            fs.manifest.files.iter().filter(|f| !f.deleted).count()
67        );
68    }
69
70    Ok(())
71}
72
73/// Remove a file from the engram (mark as deleted)
74pub fn handle_update_remove(
75    engram: PathBuf,
76    manifest: PathBuf,
77    path: String,
78    verbose: bool,
79) -> Result<()> {
80    if verbose {
81        println!(
82            "Embeddenator v{} - Incremental Remove",
83            env!("CARGO_PKG_VERSION")
84        );
85        println!("======================================");
86    }
87
88    // Validate inputs
89    if !engram.exists() {
90        anyhow::bail!("Engram file not found: {}", engram.display());
91    }
92    if !manifest.exists() {
93        anyhow::bail!("Manifest file not found: {}", manifest.display());
94    }
95
96    // Load existing engram and manifest (auto-detects holographic mode)
97    let mut fs = EmbrFS::load(&engram, &manifest)?;
98
99    if verbose {
100        println!("Removing file: {}", path);
101    }
102
103    // Remove the file (marks as deleted)
104    fs.remove_file(&path, verbose)?;
105
106    // Save updated manifest (engram doesn't change for removal)
107    fs.save_manifest(&manifest)?;
108
109    if verbose {
110        println!("\nRemove complete!");
111        println!("  File removed: {}", path);
112        println!("  Note: Use 'update compact' to reclaim space from deleted files");
113    }
114
115    Ok(())
116}
117
118/// Modify an existing file in the engram
119pub fn handle_update_modify(
120    engram: PathBuf,
121    manifest: PathBuf,
122    file: PathBuf,
123    logical_path: Option<String>,
124    verbose: bool,
125) -> Result<()> {
126    if verbose {
127        println!(
128            "Embeddenator v{} - Incremental Modify",
129            env!("CARGO_PKG_VERSION")
130        );
131        println!("======================================");
132    }
133
134    // Validate inputs
135    if !engram.exists() {
136        anyhow::bail!("Engram file not found: {}", engram.display());
137    }
138    if !manifest.exists() {
139        anyhow::bail!("Manifest file not found: {}", manifest.display());
140    }
141    if !file.exists() {
142        anyhow::bail!("Input file not found: {}", file.display());
143    }
144
145    // Load existing engram and manifest (auto-detects holographic mode)
146    let mut fs = EmbrFS::load(&engram, &manifest)?;
147    let config = ReversibleVSAConfig::default();
148
149    // Determine logical path
150    let logical = logical_path.unwrap_or_else(|| {
151        file.file_name()
152            .and_then(|s| s.to_str())
153            .unwrap_or("unnamed")
154            .to_string()
155    });
156
157    if verbose {
158        println!("Modifying file: {} -> {}", file.display(), logical);
159    }
160
161    // Modify the file (marks old as deleted, adds new version)
162    fs.modify_file(&file, logical.clone(), verbose, &config)?;
163
164    // Save updated engram and manifest
165    fs.save_engram(&engram)?;
166    fs.save_manifest(&manifest)?;
167
168    if verbose {
169        println!("\nModify complete!");
170        println!("  File updated: {}", logical);
171        println!("  Note: Use 'update compact' to reclaim space from old versions");
172    }
173
174    Ok(())
175}
176
177/// Compact the engram by rebuilding without deleted files
178pub fn handle_update_compact(engram: PathBuf, manifest: PathBuf, verbose: bool) -> Result<()> {
179    if verbose {
180        println!(
181            "Embeddenator v{} - Compact Engram",
182            env!("CARGO_PKG_VERSION")
183        );
184        println!("===================================");
185    }
186
187    // Validate inputs
188    if !engram.exists() {
189        anyhow::bail!("Engram file not found: {}", engram.display());
190    }
191    if !manifest.exists() {
192        anyhow::bail!("Manifest file not found: {}", manifest.display());
193    }
194
195    // Load existing engram and manifest (auto-detects holographic mode)
196    let mut fs = EmbrFS::load(&engram, &manifest)?;
197
198    // Count deleted files before compact
199    let deleted_count = fs.manifest.files.iter().filter(|f| f.deleted).count();
200
201    if deleted_count == 0 {
202        if verbose {
203            println!("No deleted files to compact. Engram is already optimal.");
204        }
205        return Ok(());
206    }
207
208    let config = ReversibleVSAConfig::default();
209
210    if verbose {
211        println!("Found {} deleted file(s) to remove", deleted_count);
212        println!("Rebuilding engram...");
213    }
214
215    // Compact (rebuilds without deleted files)
216    fs.compact(verbose, &config)?;
217
218    // Save compacted engram and manifest
219    fs.save_engram(&engram)?;
220    fs.save_manifest(&manifest)?;
221
222    if verbose {
223        println!("\nCompact complete!");
224        println!("  Removed {} deleted file(s)", deleted_count);
225        println!(
226            "  Active files: {}",
227            fs.manifest.files.iter().filter(|f| !f.deleted).count()
228        );
229    }
230
231    Ok(())
232}