use core::net::SocketAddrV4;
use heapless::index_map::FnvIndexMap;
pub const SERVICE_REGISTRY_CAP: usize = 32;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ServiceInstanceId {
pub service_id: u16,
pub instance_id: u16,
}
#[derive(Clone, Debug)]
pub struct ServiceEndpointInfo {
pub addr: SocketAddrV4,
pub local_port: u16,
#[allow(dead_code)]
pub major_version: u8,
#[allow(dead_code)]
pub minor_version: u32,
}
#[derive(Debug, Default)]
pub struct ServiceRegistry {
endpoints: FnvIndexMap<ServiceInstanceId, ServiceEndpointInfo, SERVICE_REGISTRY_CAP>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ServiceRegistryFull;
impl ServiceRegistry {
pub fn insert(
&mut self,
id: ServiceInstanceId,
info: ServiceEndpointInfo,
) -> Result<(), ServiceRegistryFull> {
self.endpoints
.insert(id, info)
.map(|_| ())
.map_err(|_| ServiceRegistryFull)
}
pub fn remove(&mut self, id: ServiceInstanceId) -> Option<ServiceEndpointInfo> {
self.endpoints.swap_remove(&id)
}
pub fn get(&self, id: ServiceInstanceId) -> Option<&ServiceEndpointInfo> {
self.endpoints.get(&id)
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::net::Ipv4Addr;
fn test_id(service: u16, instance: u16) -> ServiceInstanceId {
ServiceInstanceId {
service_id: service,
instance_id: instance,
}
}
fn test_info(port: u16) -> ServiceEndpointInfo {
ServiceEndpointInfo {
addr: SocketAddrV4::new(Ipv4Addr::new(192, 168, 1, 1), port),
local_port: 0,
major_version: 1,
minor_version: 0,
}
}
#[test]
fn insert_and_get() {
let mut reg = ServiceRegistry::default();
let id = test_id(0x1234, 0x0001);
reg.insert(id, test_info(30000)).unwrap();
let info = reg.get(id).unwrap();
assert_eq!(info.addr.port(), 30000);
assert_eq!(info.major_version, 1);
}
#[test]
fn remove_returns_info() {
let mut reg = ServiceRegistry::default();
let id = test_id(0x1234, 0x0001);
reg.insert(id, test_info(30000)).unwrap();
let removed = reg.remove(id).unwrap();
assert_eq!(removed.addr.port(), 30000);
assert!(reg.get(id).is_none());
}
#[test]
fn overwrite_replaces_info() {
let mut reg = ServiceRegistry::default();
let id = test_id(0x1234, 0x0001);
reg.insert(id, test_info(30000)).unwrap();
reg.insert(id, test_info(40000)).unwrap();
assert_eq!(reg.get(id).unwrap().addr.port(), 40000);
}
#[test]
fn get_missing_returns_none() {
let reg = ServiceRegistry::default();
assert!(reg.get(test_id(0xFFFF, 0xFFFF)).is_none());
}
#[test]
fn remove_missing_returns_none() {
let mut reg = ServiceRegistry::default();
assert!(reg.remove(test_id(0xFFFF, 0xFFFF)).is_none());
}
#[test]
fn insert_returns_full_at_cap() {
let mut reg = ServiceRegistry::default();
for i in 0..SERVICE_REGISTRY_CAP {
#[allow(clippy::cast_possible_truncation)]
let id = test_id(i as u16, 0);
assert!(reg.insert(id, test_info(0)).is_ok());
}
let overflow_id = test_id(0xFFFF, 0xFFFF);
assert_eq!(
reg.insert(overflow_id, test_info(0)),
Err(ServiceRegistryFull),
);
}
#[test]
fn insert_at_cap_for_existing_key_succeeds() {
let mut reg = ServiceRegistry::default();
for i in 0..SERVICE_REGISTRY_CAP {
#[allow(clippy::cast_possible_truncation)]
let id = test_id(i as u16, 0);
assert!(reg.insert(id, test_info(0)).is_ok());
}
let existing = test_id(0, 0);
assert!(reg.insert(existing, test_info(9999)).is_ok());
assert_eq!(reg.get(existing).unwrap().addr.port(), 9999);
}
}