ndn_ipc/
registry.rs

1use std::collections::HashMap;
2
3use bytes::Bytes;
4
5/// A registered service entry in the local namespace.
6pub struct ServiceEntry {
7    /// Encoded capabilities blob (application-defined format).
8    pub capabilities: Bytes,
9}
10
11/// Service registry backed by the NDN namespace.
12///
13/// Services advertise under `/local/services/<name>/info` (capabilities)
14/// and `/local/services/<name>/alive` (heartbeat with short FreshnessPeriod).
15/// Discovery is a CanBePrefix Interest for `/local/services`.
16///
17/// This in-memory implementation is used for testing and single-process deployments.
18/// A production implementation would publish and fetch `Data` packets via the engine.
19pub struct ServiceRegistry {
20    services: HashMap<String, ServiceEntry>,
21}
22
23impl ServiceRegistry {
24    pub fn new() -> Self {
25        Self {
26            services: HashMap::new(),
27        }
28    }
29
30    /// Advertise `name` with the given `capabilities` blob.
31    pub fn register(&mut self, name: impl Into<String>, capabilities: Bytes) {
32        self.services
33            .insert(name.into(), ServiceEntry { capabilities });
34    }
35
36    /// Look up a registered service by name.
37    pub fn lookup(&self, name: &str) -> Option<&ServiceEntry> {
38        self.services.get(name)
39    }
40
41    /// Remove a service. Returns `true` if it was registered.
42    pub fn unregister(&mut self, name: &str) -> bool {
43        self.services.remove(name).is_some()
44    }
45
46    pub fn service_count(&self) -> usize {
47        self.services.len()
48    }
49}
50
51impl Default for ServiceRegistry {
52    fn default() -> Self {
53        Self::new()
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    #[test]
62    fn register_and_lookup() {
63        let mut reg = ServiceRegistry::new();
64        reg.register("foo", Bytes::from_static(b"caps"));
65        let entry = reg.lookup("foo").unwrap();
66        assert_eq!(entry.capabilities, Bytes::from_static(b"caps"));
67    }
68
69    #[test]
70    fn lookup_missing_returns_none() {
71        let reg = ServiceRegistry::new();
72        assert!(reg.lookup("missing").is_none());
73    }
74
75    #[test]
76    fn unregister_removes_entry() {
77        let mut reg = ServiceRegistry::new();
78        reg.register("bar", Bytes::new());
79        assert!(reg.unregister("bar"));
80        assert!(reg.lookup("bar").is_none());
81    }
82
83    #[test]
84    fn unregister_nonexistent_returns_false() {
85        let mut reg = ServiceRegistry::new();
86        assert!(!reg.unregister("nope"));
87    }
88
89    #[test]
90    fn service_count() {
91        let mut reg = ServiceRegistry::new();
92        assert_eq!(reg.service_count(), 0);
93        reg.register("a", Bytes::new());
94        reg.register("b", Bytes::new());
95        assert_eq!(reg.service_count(), 2);
96        reg.unregister("a");
97        assert_eq!(reg.service_count(), 1);
98    }
99}