oxihuman_core/
service_registry.rs1#![allow(dead_code)]
4
5#[derive(Clone, Debug)]
9pub struct ServiceInstance {
10 pub name: String,
11 pub host: String,
12 pub port: u16,
13 pub version: String,
14 pub healthy: bool,
15 pub tags: Vec<String>,
16}
17
18#[derive(Clone, Debug)]
20pub struct ServiceRegistryConfig {
21 pub max_instances_per_service: usize,
22}
23
24impl Default for ServiceRegistryConfig {
25 fn default() -> Self {
26 Self {
27 max_instances_per_service: 16,
28 }
29 }
30}
31
32pub struct ServiceRegistry {
34 pub config: ServiceRegistryConfig,
35 instances: Vec<ServiceInstance>,
36}
37
38pub fn new_registry(config: ServiceRegistryConfig) -> ServiceRegistry {
40 ServiceRegistry {
41 config,
42 instances: Vec::new(),
43 }
44}
45
46pub fn register_instance(reg: &mut ServiceRegistry, instance: ServiceInstance) -> bool {
48 let count = reg
49 .instances
50 .iter()
51 .filter(|i| i.name == instance.name)
52 .count();
53 if count >= reg.config.max_instances_per_service {
54 return false;
55 }
56 reg.instances.push(instance);
57 true
58}
59
60pub fn deregister_instance(reg: &mut ServiceRegistry, name: &str, host: &str, port: u16) -> bool {
62 let before = reg.instances.len();
63 reg.instances
64 .retain(|i| !(i.name == name && i.host == host && i.port == port));
65 reg.instances.len() < before
66}
67
68pub fn resolve_service<'a>(reg: &'a ServiceRegistry, name: &str) -> Vec<&'a ServiceInstance> {
70 reg.instances
71 .iter()
72 .filter(|i| i.name == name && i.healthy)
73 .collect()
74}
75
76pub fn set_service_health(reg: &mut ServiceRegistry, name: &str, healthy: bool) {
78 for inst in reg.instances.iter_mut().filter(|i| i.name == name) {
79 inst.healthy = healthy;
80 }
81}
82
83pub fn total_instance_count(reg: &ServiceRegistry) -> usize {
85 reg.instances.len()
86}
87
88impl ServiceRegistry {
89 pub fn new(config: ServiceRegistryConfig) -> Self {
91 new_registry(config)
92 }
93}
94
95fn make_instance(name: &str, host: &str, port: u16) -> ServiceInstance {
96 ServiceInstance {
97 name: name.into(),
98 host: host.into(),
99 port,
100 version: "1.0".into(),
101 healthy: true,
102 tags: vec![],
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 fn make_reg() -> ServiceRegistry {
111 new_registry(ServiceRegistryConfig::default())
112 }
113
114 #[test]
115 fn test_register_and_count() {
116 let mut reg = make_reg();
117 let ok = register_instance(&mut reg, make_instance("svc-a", "host1", 8080));
118 assert!(ok);
119 assert_eq!(total_instance_count(®), 1);
120 }
121
122 #[test]
123 fn test_resolve_healthy_instance() {
124 let mut reg = make_reg();
125 register_instance(&mut reg, make_instance("svc-b", "host2", 9000));
126 let found = resolve_service(®, "svc-b");
127 assert_eq!(found.len(), 1);
128 }
129
130 #[test]
131 fn test_unhealthy_instance_not_resolved() {
132 let mut reg = make_reg();
133 let mut inst = make_instance("svc-c", "h", 80);
134 inst.healthy = false;
135 register_instance(&mut reg, inst);
136 assert!(resolve_service(®, "svc-c").is_empty());
137 }
138
139 #[test]
140 fn test_deregister_removes_instance() {
141 let mut reg = make_reg();
142 register_instance(&mut reg, make_instance("svc-d", "h", 80));
143 let removed = deregister_instance(&mut reg, "svc-d", "h", 80);
144 assert!(removed);
145 assert_eq!(total_instance_count(®), 0);
146 }
147
148 #[test]
149 fn test_deregister_nonexistent_returns_false() {
150 let mut reg = make_reg();
151 assert!(!deregister_instance(&mut reg, "none", "h", 80));
152 }
153
154 #[test]
155 fn test_set_service_health_to_unhealthy() {
156 let mut reg = make_reg();
157 register_instance(&mut reg, make_instance("svc-e", "h", 80));
158 set_service_health(&mut reg, "svc-e", false);
159 assert!(resolve_service(®, "svc-e").is_empty());
160 }
161
162 #[test]
163 fn test_capacity_limit_enforced() {
164 let mut reg = new_registry(ServiceRegistryConfig {
165 max_instances_per_service: 2,
166 });
167 register_instance(&mut reg, make_instance("svc-f", "h1", 1));
168 register_instance(&mut reg, make_instance("svc-f", "h2", 2));
169 let ok = register_instance(&mut reg, make_instance("svc-f", "h3", 3));
170 assert!(!ok);
171 }
172
173 #[test]
174 fn test_multiple_services_independent() {
175 let mut reg = make_reg();
176 register_instance(&mut reg, make_instance("alpha", "h1", 80));
177 register_instance(&mut reg, make_instance("beta", "h2", 80));
178 assert_eq!(resolve_service(®, "alpha").len(), 1);
179 assert_eq!(resolve_service(®, "beta").len(), 1);
180 }
181
182 #[test]
183 fn test_resolve_returns_empty_for_unknown_service() {
184 let reg = make_reg();
185 assert!(resolve_service(®, "unknown").is_empty());
186 }
187}