codelens_engine/memory/
policy.rs1use std::path::Path;
2
3use serde::Deserialize;
4
5use super::now_secs;
6
7#[derive(Debug, Clone, Default, Deserialize)]
11pub struct MemoryPolicy {
12 #[serde(default)]
14 pub read_only: Vec<String>,
15 #[serde(default)]
17 pub ignored: Vec<String>,
18 #[serde(default)]
20 pub max_age_days: Option<u64>,
21}
22
23pub(crate) const POLICY_FILENAME: &str = "__policy__";
24pub(crate) const POLICY_FILE_BASENAME: &str = ".policy";
25pub(crate) const ARCHIVE_DIRNAME: &str = ".archive";
26
27impl MemoryPolicy {
28 pub fn load(memories_dir: &Path) -> Self {
30 let policy_path = memories_dir.join(POLICY_FILE_BASENAME);
31 if !policy_path.is_file() {
32 return Self::default();
33 }
34 let content = match std::fs::read_to_string(&policy_path) {
35 Ok(c) => c,
36 Err(_) => return Self::default(),
37 };
38 Self::parse(&content)
39 }
40
41 pub(crate) fn parse(content: &str) -> Self {
43 toml::from_str(content).unwrap_or_default()
44 }
45
46 pub fn is_read_only(&self, name: &str) -> bool {
47 matches_any_pattern(name, &self.read_only)
48 }
49
50 pub fn is_ignored(&self, name: &str) -> bool {
51 matches_any_pattern(name, &self.ignored)
52 }
53
54 pub fn is_stale(&self, _name: &str, modified_secs: u64) -> bool {
55 let Some(max_days) = self.max_age_days else {
56 return false;
57 };
58 let age_secs = now_secs().saturating_sub(modified_secs);
59 age_secs > max_days.saturating_mul(86400)
60 }
61}
62
63fn matches_any_pattern(name: &str, patterns: &[String]) -> bool {
64 patterns.iter().any(|pattern| glob_match(pattern, name))
65}
66
67pub(crate) fn glob_match(pattern: &str, name: &str) -> bool {
68 let pat_bytes = pattern.as_bytes();
69 let name_bytes = name.as_bytes();
70 let mut pi = 0usize;
71 let mut ni = 0usize;
72 let mut star_pi = usize::MAX;
73 let mut star_ni = usize::MAX;
74
75 while ni < name_bytes.len() {
76 if pi < pat_bytes.len() {
77 let pc = pat_bytes[pi];
78 if pc == b'*' {
79 star_pi = pi;
80 star_ni = ni;
81 pi += 1;
82 continue;
83 }
84 if pc == b'?' || pc == name_bytes[ni] {
85 pi += 1;
86 ni += 1;
87 continue;
88 }
89 }
90 if star_pi != usize::MAX {
91 pi = star_pi + 1;
92 star_ni += 1;
93 ni = star_ni;
94 continue;
95 }
96 return false;
97 }
98
99 while pi < pat_bytes.len() && pat_bytes[pi] == b'*' {
100 pi += 1;
101 }
102 pi == pat_bytes.len()
103}