nt_memory/coordination/
namespace.rs1use std::collections::HashSet;
6
7pub struct Namespace;
9
10impl Namespace {
11 pub fn parse(key: &str) -> Result<(String, String, String), String> {
13 let parts: Vec<&str> = key.split('/').collect();
14
15 if parts.len() != 3 {
16 return Err(format!("Invalid namespace format: {}", key));
17 }
18
19 if parts[0] != "swarm" {
20 return Err(format!("Namespace must start with 'swarm': {}", key));
21 }
22
23 Ok((
24 parts[0].to_string(), parts[1].to_string(), parts[2].to_string(), ))
28 }
29
30 pub fn build(agent_id: &str, key: &str) -> String {
32 format!("swarm/{}/{}", agent_id, key)
33 }
34
35 pub fn validate(key: &str) -> bool {
37 Self::parse(key).is_ok()
38 }
39
40 pub fn extract_agent_id(key: &str) -> Option<String> {
42 Self::parse(key).ok().map(|(_, agent_id, _)| agent_id)
43 }
44
45 pub fn extract_key(key: &str) -> Option<String> {
47 Self::parse(key).ok().map(|(_, _, key)| key)
48 }
49
50 pub fn belongs_to_agent(key: &str, agent_id: &str) -> bool {
52 Self::extract_agent_id(key)
53 .map(|id| id == agent_id)
54 .unwrap_or(false)
55 }
56
57 pub fn get_agent_ids(keys: &[String]) -> HashSet<String> {
59 keys.iter()
60 .filter_map(|k| Self::extract_agent_id(k))
61 .collect()
62 }
63
64 pub fn filter_by_agent(keys: &[String], agent_id: &str) -> Vec<String> {
66 keys.iter()
67 .filter(|k| Self::belongs_to_agent(k, agent_id))
68 .cloned()
69 .collect()
70 }
71
72 pub fn agent_prefix(agent_id: &str) -> String {
74 format!("swarm/{}/", agent_id)
75 }
76
77 pub fn has_prefix(key: &str, prefix: &str) -> bool {
79 key.starts_with(prefix)
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_parse_valid() {
89 let key = "swarm/agent_1/position";
90 let (ns, agent_id, k) = Namespace::parse(key).unwrap();
91
92 assert_eq!(ns, "swarm");
93 assert_eq!(agent_id, "agent_1");
94 assert_eq!(k, "position");
95 }
96
97 #[test]
98 fn test_parse_invalid() {
99 assert!(Namespace::parse("invalid/format").is_err());
100 assert!(Namespace::parse("wrong/agent/key").is_err());
101 assert!(Namespace::parse("swarm/only_two").is_err());
102 }
103
104 #[test]
105 fn test_build() {
106 let key = Namespace::build("agent_1", "position");
107 assert_eq!(key, "swarm/agent_1/position");
108 }
109
110 #[test]
111 fn test_validate() {
112 assert!(Namespace::validate("swarm/agent_1/key"));
113 assert!(!Namespace::validate("invalid/format"));
114 }
115
116 #[test]
117 fn test_extract_agent_id() {
118 let agent_id = Namespace::extract_agent_id("swarm/agent_1/key");
119 assert_eq!(agent_id, Some("agent_1".to_string()));
120
121 let invalid = Namespace::extract_agent_id("invalid");
122 assert_eq!(invalid, None);
123 }
124
125 #[test]
126 fn test_extract_key() {
127 let key = Namespace::extract_key("swarm/agent_1/position");
128 assert_eq!(key, Some("position".to_string()));
129 }
130
131 #[test]
132 fn test_belongs_to_agent() {
133 assert!(Namespace::belongs_to_agent("swarm/agent_1/key", "agent_1"));
134 assert!(!Namespace::belongs_to_agent("swarm/agent_2/key", "agent_1"));
135 }
136
137 #[test]
138 fn test_get_agent_ids() {
139 let keys = vec![
140 "swarm/agent_1/key1".to_string(),
141 "swarm/agent_2/key2".to_string(),
142 "swarm/agent_1/key3".to_string(),
143 ];
144
145 let agent_ids = Namespace::get_agent_ids(&keys);
146 assert_eq!(agent_ids.len(), 2);
147 assert!(agent_ids.contains("agent_1"));
148 assert!(agent_ids.contains("agent_2"));
149 }
150
151 #[test]
152 fn test_filter_by_agent() {
153 let keys = vec![
154 "swarm/agent_1/key1".to_string(),
155 "swarm/agent_2/key2".to_string(),
156 "swarm/agent_1/key3".to_string(),
157 ];
158
159 let filtered = Namespace::filter_by_agent(&keys, "agent_1");
160 assert_eq!(filtered.len(), 2);
161 }
162
163 #[test]
164 fn test_agent_prefix() {
165 let prefix = Namespace::agent_prefix("agent_1");
166 assert_eq!(prefix, "swarm/agent_1/");
167 }
168
169 #[test]
170 fn test_has_prefix() {
171 let key = "swarm/agent_1/position";
172 let prefix = Namespace::agent_prefix("agent_1");
173
174 assert!(Namespace::has_prefix(key, &prefix));
175 assert!(!Namespace::has_prefix(key, "swarm/agent_2/"));
176 }
177}