codelens_engine/memory/
archive.rs1use std::path::Path;
2
3use anyhow::{Result, bail};
4
5use super::audit::{AuditRecorder, MemoryAuditEvent};
6use super::paths::{MemoryTier, collect_memory_files, resolve_memory_path};
7use super::policy::{ARCHIVE_DIRNAME, MemoryPolicy};
8
9pub fn archive_memory(memories_dir: &Path, name: &str) -> Result<()> {
10 archive_memory_rec(memories_dir, name, None)
11}
12
13pub fn archive_memory_rec(
15 memories_dir: &Path,
16 name: &str,
17 recorder: Option<&dyn AuditRecorder>,
18) -> Result<()> {
19 let policy = MemoryPolicy::load(memories_dir);
20 if policy.is_read_only(name) {
21 bail!("memory '{name}' is read-only and cannot be archived");
22 }
23 let source = resolve_memory_path(memories_dir, name)?;
24 if !source.is_file() {
25 bail!("memory not found: {name}");
26 }
27 let archive_dir = memories_dir.join(ARCHIVE_DIRNAME);
28 std::fs::create_dir_all(&archive_dir)?;
29 let dest = archive_dir.join(source.file_name().expect("file name"));
30 if dest.exists() {
31 bail!("archive already contains an entry for: {name}");
32 }
33 let path_str = source.to_string_lossy().to_string();
34 std::fs::rename(&source, &dest)?;
35 if let Some(rec) = recorder {
36 rec.record(&MemoryAuditEvent::Archived {
37 tier: MemoryTier::Project,
38 path: path_str,
39 });
40 }
41 Ok(())
42}
43
44pub fn list_archived(memories_dir: &Path) -> Result<Vec<String>> {
46 let archive_dir = memories_dir.join(ARCHIVE_DIRNAME);
47 if !archive_dir.is_dir() {
48 return Ok(Vec::new());
49 }
50 let mut names = Vec::new();
51 collect_memory_files(memories_dir, &archive_dir, &mut names);
52 for name in &mut names {
53 *name = format!("archived/{}", name);
54 }
55 names.sort();
56 Ok(names)
57}
58
59pub fn restore_archived(memories_dir: &Path, name: &str) -> Result<()> {
62 restore_archived_rec(memories_dir, name, None)
63}
64
65pub fn restore_archived_rec(
67 memories_dir: &Path,
68 name: &str,
69 recorder: Option<&dyn AuditRecorder>,
70) -> Result<()> {
71 let short_name = name.trim_start_matches("archived/");
72 let archive_dir = memories_dir.join(ARCHIVE_DIRNAME);
73 let source = resolve_memory_path(&archive_dir, short_name)?;
74 if !source.is_file() {
75 bail!("archived memory not found: {name}");
76 }
77 let dest = resolve_memory_path(memories_dir, short_name)?;
78 if dest.exists() {
79 bail!("a memory with the name '{short_name}' already exists; delete it first");
80 }
81 let path_str = dest.to_string_lossy().to_string();
82 std::fs::rename(&source, &dest)?;
83 if let Some(rec) = recorder {
84 rec.record(&MemoryAuditEvent::Restored {
85 tier: MemoryTier::Project,
86 path: path_str,
87 });
88 }
89 Ok(())
90}