1use std::collections::HashMap;
4
5use agent_uri::{CapabilityPath, PathSegment};
6
7#[derive(Debug, Clone)]
42pub struct PathTrie<V> {
43 children: HashMap<String, PathTrie<V>>,
45 values: Vec<V>,
47}
48
49impl<V> Default for PathTrie<V> {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55impl<V> PathTrie<V> {
56 #[must_use]
58 pub fn new() -> Self {
59 Self {
60 children: HashMap::new(),
61 values: Vec::new(),
62 }
63 }
64
65 #[must_use]
67 pub fn len(&self) -> usize {
68 self.values.len()
69 }
70
71 #[must_use]
73 pub fn is_empty(&self) -> bool {
74 self.values.is_empty()
75 }
76
77 #[must_use]
79 pub fn total_count(&self) -> usize {
80 let mut count = self.values.len();
81 for child in self.children.values() {
82 count += child.total_count();
83 }
84 count
85 }
86
87 #[must_use]
89 pub fn unique_paths(&self) -> usize {
90 let mut count = usize::from(!self.values.is_empty());
91 for child in self.children.values() {
92 count += child.unique_paths();
93 }
94 count
95 }
96
97 #[must_use]
99 pub fn has_children(&self) -> bool {
100 !self.children.is_empty()
101 }
102}
103
104impl<V: Clone> PathTrie<V> {
105 pub fn insert(&mut self, path: &CapabilityPath, value: V) {
109 let segments = path.segments();
110 self.insert_at_segments(segments, 0, value);
111 }
112
113 fn insert_at_segments(&mut self, segments: &[PathSegment], index: usize, value: V) {
114 if index >= segments.len() {
115 self.values.push(value);
116 return;
117 }
118
119 let segment = segments[index].as_str().to_string();
120 let child = self.children.entry(segment).or_default();
121 child.insert_at_segments(segments, index + 1, value);
122 }
123
124 #[must_use]
126 pub fn get_exact(&self, path: &CapabilityPath) -> Vec<&V> {
127 let segments = path.segments();
128 self.get_exact_at_segments(segments, 0)
129 }
130
131 fn get_exact_at_segments(&self, segments: &[PathSegment], index: usize) -> Vec<&V> {
132 if index >= segments.len() {
133 return self.values.iter().collect();
134 }
135
136 let segment = segments[index].as_str();
137 match self.children.get(segment) {
138 Some(child) => child.get_exact_at_segments(segments, index + 1),
139 None => Vec::new(),
140 }
141 }
142
143 #[must_use]
145 pub fn get_prefix(&self, path: &CapabilityPath) -> Vec<&V> {
146 let segments = path.segments();
147 self.get_prefix_at_segments(segments, 0)
148 }
149
150 fn get_prefix_at_segments(&self, segments: &[PathSegment], index: usize) -> Vec<&V> {
151 if index >= segments.len() {
152 return self.collect_all();
154 }
155
156 let segment = segments[index].as_str();
157 match self.children.get(segment) {
158 Some(child) => child.get_prefix_at_segments(segments, index + 1),
159 None => Vec::new(),
160 }
161 }
162
163 fn collect_all(&self) -> Vec<&V> {
165 let mut result: Vec<&V> = self.values.iter().collect();
166 for child in self.children.values() {
167 result.extend(child.collect_all());
168 }
169 result
170 }
171
172 pub fn remove<F>(&mut self, path: &CapabilityPath, predicate: F) -> usize
176 where
177 F: Fn(&V) -> bool,
178 {
179 let segments = path.segments();
180 self.remove_at_segments(segments, 0, &predicate)
181 }
182
183 fn remove_at_segments<F>(
184 &mut self,
185 segments: &[PathSegment],
186 index: usize,
187 predicate: &F,
188 ) -> usize
189 where
190 F: Fn(&V) -> bool,
191 {
192 if index >= segments.len() {
193 let before = self.values.len();
194 self.values.retain(|v| !predicate(v));
195 return before - self.values.len();
196 }
197
198 let segment = segments[index].as_str().to_string();
199 if let Some(child) = self.children.get_mut(&segment) {
200 let removed = child.remove_at_segments(segments, index + 1, predicate);
201 if child.is_empty() && !child.has_children() {
203 self.children.remove(&segment);
204 }
205 removed
206 } else {
207 0
208 }
209 }
210
211 pub fn clear(&mut self) {
213 self.values.clear();
214 self.children.clear();
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn new_trie_is_empty() {
224 let trie: PathTrie<String> = PathTrie::new();
225 assert!(trie.is_empty());
226 assert_eq!(trie.len(), 0);
227 assert_eq!(trie.total_count(), 0);
228 assert_eq!(trie.unique_paths(), 0);
229 }
230
231 #[test]
232 fn insert_and_get_exact() {
233 let mut trie: PathTrie<String> = PathTrie::new();
234 let path = CapabilityPath::parse("assistant/chat").unwrap();
235
236 trie.insert(&path, "test".to_string());
237
238 let results = trie.get_exact(&path);
239 assert_eq!(results.len(), 1);
240 assert_eq!(*results[0], "test");
241 }
242
243 #[test]
244 fn multiple_values_at_same_path() {
245 let mut trie: PathTrie<String> = PathTrie::new();
246 let path = CapabilityPath::parse("assistant/chat").unwrap();
247
248 trie.insert(&path, "agent1".to_string());
249 trie.insert(&path, "agent2".to_string());
250
251 let results = trie.get_exact(&path);
252 assert_eq!(results.len(), 2);
253 }
254
255 #[test]
256 fn get_exact_returns_empty_for_nonexistent_path() {
257 let trie: PathTrie<String> = PathTrie::new();
258 let path = CapabilityPath::parse("assistant/chat").unwrap();
259
260 let results = trie.get_exact(&path);
261 assert!(results.is_empty());
262 }
263
264 #[test]
265 fn get_exact_does_not_include_descendants() {
266 let mut trie: PathTrie<String> = PathTrie::new();
267
268 let path1 = CapabilityPath::parse("assistant").unwrap();
269 let path2 = CapabilityPath::parse("assistant/chat").unwrap();
270
271 trie.insert(&path1, "assistant".to_string());
272 trie.insert(&path2, "chat".to_string());
273
274 let results = trie.get_exact(&path1);
275 assert_eq!(results.len(), 1);
276 assert_eq!(*results[0], "assistant");
277 }
278
279 #[test]
280 fn get_prefix_includes_descendants() {
281 let mut trie: PathTrie<String> = PathTrie::new();
282
283 let path1 = CapabilityPath::parse("assistant").unwrap();
284 let path2 = CapabilityPath::parse("assistant/chat").unwrap();
285 let path3 = CapabilityPath::parse("assistant/code").unwrap();
286
287 trie.insert(&path1, "assistant".to_string());
288 trie.insert(&path2, "chat".to_string());
289 trie.insert(&path3, "code".to_string());
290
291 let results = trie.get_prefix(&path1);
292 assert_eq!(results.len(), 3);
293 }
294
295 #[test]
296 fn get_prefix_at_leaf_returns_only_that_node() {
297 let mut trie: PathTrie<String> = PathTrie::new();
298
299 let path1 = CapabilityPath::parse("assistant").unwrap();
300 let path2 = CapabilityPath::parse("assistant/chat").unwrap();
301
302 trie.insert(&path1, "assistant".to_string());
303 trie.insert(&path2, "chat".to_string());
304
305 let results = trie.get_prefix(&path2);
306 assert_eq!(results.len(), 1);
307 assert_eq!(*results[0], "chat");
308 }
309
310 #[test]
311 fn get_prefix_returns_empty_for_nonexistent_path() {
312 let trie: PathTrie<String> = PathTrie::new();
313 let path = CapabilityPath::parse("nonexistent").unwrap();
314
315 let results = trie.get_prefix(&path);
316 assert!(results.is_empty());
317 }
318
319 #[test]
320 fn remove_matching_values() {
321 let mut trie: PathTrie<String> = PathTrie::new();
322 let path = CapabilityPath::parse("assistant/chat").unwrap();
323
324 trie.insert(&path, "agent1".to_string());
325 trie.insert(&path, "agent2".to_string());
326
327 let removed = trie.remove(&path, |v| v == "agent1");
328 assert_eq!(removed, 1);
329
330 let results = trie.get_exact(&path);
331 assert_eq!(results.len(), 1);
332 assert_eq!(*results[0], "agent2");
333 }
334
335 #[test]
336 fn remove_all_values_cleans_up_node() {
337 let mut trie: PathTrie<String> = PathTrie::new();
338 let path = CapabilityPath::parse("assistant/chat").unwrap();
339
340 trie.insert(&path, "agent1".to_string());
341
342 let removed = trie.remove(&path, |_| true);
343 assert_eq!(removed, 1);
344 assert_eq!(trie.total_count(), 0);
345 }
346
347 #[test]
348 fn remove_from_nonexistent_path_returns_zero() {
349 let mut trie: PathTrie<String> = PathTrie::new();
350 let path = CapabilityPath::parse("nonexistent").unwrap();
351
352 let removed = trie.remove(&path, |_| true);
353 assert_eq!(removed, 0);
354 }
355
356 #[test]
357 fn total_count_includes_all_descendants() {
358 let mut trie: PathTrie<String> = PathTrie::new();
359
360 let path1 = CapabilityPath::parse("assistant").unwrap();
361 let path2 = CapabilityPath::parse("assistant/chat").unwrap();
362 let path3 = CapabilityPath::parse("assistant/code").unwrap();
363 let path4 = CapabilityPath::parse("workflow").unwrap();
364
365 trie.insert(&path1, "a".to_string());
366 trie.insert(&path2, "b".to_string());
367 trie.insert(&path3, "c".to_string());
368 trie.insert(&path4, "d".to_string());
369
370 assert_eq!(trie.total_count(), 4);
371 }
372
373 #[test]
374 fn unique_paths_counts_nodes_with_values() {
375 let mut trie: PathTrie<String> = PathTrie::new();
376
377 let path1 = CapabilityPath::parse("assistant").unwrap();
378 let path2 = CapabilityPath::parse("assistant/chat").unwrap();
379
380 trie.insert(&path1, "a".to_string());
381 trie.insert(&path2, "b".to_string());
382 trie.insert(&path2, "c".to_string()); assert_eq!(trie.unique_paths(), 2);
385 }
386
387 #[test]
388 fn clear_removes_everything() {
389 let mut trie: PathTrie<String> = PathTrie::new();
390
391 let path = CapabilityPath::parse("assistant/chat").unwrap();
392 trie.insert(&path, "test".to_string());
393
394 trie.clear();
395
396 assert!(trie.is_empty());
397 assert_eq!(trie.total_count(), 0);
398 assert!(!trie.has_children());
399 }
400
401 #[test]
402 fn default_creates_empty_trie() {
403 let trie: PathTrie<String> = PathTrie::default();
404 assert!(trie.is_empty());
405 }
406
407 #[test]
408 fn deep_hierarchy() {
409 let mut trie: PathTrie<String> = PathTrie::new();
410
411 let path1 = CapabilityPath::parse("a/b/c/d").unwrap();
412 let path2 = CapabilityPath::parse("a/b/c").unwrap();
413 let path3 = CapabilityPath::parse("a/b").unwrap();
414 let path4 = CapabilityPath::parse("a").unwrap();
415
416 trie.insert(&path1, "deep".to_string());
417 trie.insert(&path2, "mid".to_string());
418 trie.insert(&path3, "shallow".to_string());
419 trie.insert(&path4, "root".to_string());
420
421 let results = trie.get_prefix(&path4);
423 assert_eq!(results.len(), 4);
424
425 let results = trie.get_prefix(&path2);
427 assert_eq!(results.len(), 2);
428 }
429}