zeroclaw_config/
field_visibility.rs1use crate::schema::Config;
15
16pub fn memory_backend_excludes(backend: &str) -> Vec<&'static str> {
24 let mut out = Vec::new();
25 if backend != "sqlite" {
26 out.push("sqlite-open-timeout-secs");
27 out.push("conversation-retention-days");
28 }
29 if backend != "qdrant" {
30 out.push("qdrant.");
31 }
32 if backend != "postgres" {
33 out.push("postgres.");
34 }
35 out
36}
37
38pub fn excluded_paths(cfg: &Config, prefix: &str) -> Vec<String> {
47 if prefix == "memory" || prefix.is_empty() {
48 let backend = if cfg.memory.backend.is_empty() {
49 "sqlite"
50 } else {
51 cfg.memory.backend.as_str()
52 };
53 return memory_backend_excludes(backend)
54 .into_iter()
55 .map(|leaf| format!("memory.{leaf}"))
56 .collect();
57 }
58
59 Vec::new()
60}
61
62pub fn is_excluded(path: &str, excludes: &[String]) -> bool {
66 excludes
67 .iter()
68 .any(|e| path == e || (e.ends_with('.') && path.starts_with(e)))
69}
70
71pub fn path_matches_prefix(path: &str, prefix: &str) -> bool {
75 match path.strip_prefix(prefix) {
76 Some(rest) => {
77 prefix.is_empty() || rest.is_empty() || rest.starts_with('.') || prefix.ends_with('.')
78 }
79 None => false,
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn memory_excludes_hide_inactive_backends() {
89 let ex = memory_backend_excludes("sqlite");
92 assert!(ex.contains(&"qdrant."));
93 assert!(ex.contains(&"postgres."));
94 assert!(!ex.contains(&"sqlite-open-timeout-secs"));
95 assert!(!ex.contains(&"conversation-retention-days"));
96
97 let ex = memory_backend_excludes("qdrant");
99 assert!(!ex.contains(&"qdrant."));
100 assert!(ex.contains(&"postgres."));
101 assert!(ex.contains(&"sqlite-open-timeout-secs"));
102 assert!(ex.contains(&"conversation-retention-days"));
103 }
104
105 #[test]
106 fn excluded_paths_for_memory_uses_active_backend() {
107 let mut cfg = Config::default();
108 cfg.memory.backend = "sqlite".into();
109 let paths = excluded_paths(&cfg, "memory");
110 assert!(paths.iter().any(|p| p == "memory.qdrant."));
111 assert!(paths.iter().any(|p| p == "memory.postgres."));
112 }
113
114 #[test]
115 fn is_excluded_handles_sub_table_marker() {
116 let excludes = vec!["memory.qdrant.".to_string(), "memory.foo".to_string()];
117 assert!(is_excluded("memory.qdrant.url", &excludes));
119 assert!(is_excluded("memory.qdrant.api-key", &excludes));
120 assert!(is_excluded("memory.foo", &excludes));
122 assert!(!is_excluded("memory.postgres.url", &excludes));
124 assert!(!is_excluded("memory.foobar", &excludes));
125 }
126
127 #[test]
128 fn path_matches_prefix_requires_segment_boundary() {
129 assert!(path_matches_prefix("agents.aaa", "agents.aaa"));
131 assert!(path_matches_prefix("agents.aaa.workspace", "agents.aaa"));
132 assert!(path_matches_prefix("agents.aaa.memory.limit", "agents.aaa"));
133 assert!(!path_matches_prefix(
135 "agents.aaalore.workspace",
136 "agents.aaa"
137 ));
138 assert!(!path_matches_prefix(
139 "agents.aaatools.identity",
140 "agents.aaa"
141 ));
142 assert!(!path_matches_prefix("agents.aaalore", "agents.aaa"));
143 assert!(path_matches_prefix("agents.aaa.workspace", "agents.aaa."));
145 assert!(!path_matches_prefix("agents.aab.workspace", "agents.aaa."));
146 assert!(path_matches_prefix("memory.backend", "memory"));
148 assert!(!path_matches_prefix("memory.backend", "mem"));
149 assert!(!path_matches_prefix("unrelated", "agents.aaa"));
150 assert!(path_matches_prefix("anything.at.all", ""));
153 assert!(path_matches_prefix("", ""));
154 }
155}