use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Direction {
CorbaToDds,
DdsToCorba,
Bidirectional,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct TopicQosRef {
pub reliability: Option<String>,
pub durability: Option<String>,
pub profile_name: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OperationMapping {
pub operation: String,
pub request_topic: String,
pub reply_topic: String,
pub qos: TopicQosRef,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BridgeRoute {
pub repository_id: String,
pub object_key: Vec<u8>,
pub direction: Direction,
pub operations: Vec<OperationMapping>,
}
impl BridgeRoute {
#[must_use]
pub fn operation(&self, name: &str) -> Option<&OperationMapping> {
self.operations.iter().find(|o| o.operation == name)
}
}
#[derive(Debug, Clone, Default)]
pub struct BridgeMapping {
routes: BTreeMap<RouteKey, BridgeRoute>,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct RouteKey {
repository_id: String,
object_key: Vec<u8>,
}
impl BridgeMapping {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn add_route(&mut self, route: BridgeRoute) {
let key = RouteKey {
repository_id: route.repository_id.clone(),
object_key: route.object_key.clone(),
};
self.routes.insert(key, route);
}
#[must_use]
pub fn lookup(&self, repository_id: &str, object_key: &[u8]) -> Option<&BridgeRoute> {
let key = RouteKey {
repository_id: repository_id.into(),
object_key: object_key.to_vec(),
};
self.routes.get(&key)
}
#[must_use]
pub fn len(&self) -> usize {
self.routes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.routes.is_empty()
}
#[must_use]
pub fn all_routes(&self) -> Vec<&BridgeRoute> {
self.routes.values().collect()
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
fn echo_route() -> BridgeRoute {
BridgeRoute {
repository_id: "IDL:demo/Echo:1.0".into(),
object_key: alloc::vec![0xab],
direction: Direction::Bidirectional,
operations: alloc::vec![OperationMapping {
operation: "ping".into(),
request_topic: "demo/Echo/ping/Request".into(),
reply_topic: "demo/Echo/ping/Reply".into(),
qos: TopicQosRef {
reliability: Some("RELIABLE".into()),
durability: Some("VOLATILE".into()),
profile_name: None,
},
}],
}
}
#[test]
fn add_and_lookup_route() {
let mut m = BridgeMapping::new();
m.add_route(echo_route());
let r = m.lookup("IDL:demo/Echo:1.0", &[0xab]).unwrap();
assert_eq!(r.direction, Direction::Bidirectional);
assert_eq!(m.len(), 1);
}
#[test]
fn lookup_unknown_yields_none() {
let m = BridgeMapping::new();
assert!(m.lookup("IDL:demo/Echo:1.0", &[0xff]).is_none());
}
#[test]
fn operation_mapping_by_name() {
let r = echo_route();
let op = r.operation("ping").unwrap();
assert_eq!(op.request_topic, "demo/Echo/ping/Request");
assert_eq!(op.reply_topic, "demo/Echo/ping/Reply");
assert!(r.operation("nope").is_none());
}
#[test]
fn add_route_replaces_existing() {
let mut m = BridgeMapping::new();
m.add_route(echo_route());
let mut r2 = echo_route();
r2.direction = Direction::CorbaToDds;
m.add_route(r2);
assert_eq!(m.len(), 1);
assert_eq!(
m.lookup("IDL:demo/Echo:1.0", &[0xab]).unwrap().direction,
Direction::CorbaToDds
);
}
#[test]
fn all_routes_listing() {
let mut m = BridgeMapping::new();
m.add_route(echo_route());
let mut other = echo_route();
other.repository_id = "IDL:demo/Other:1.0".into();
m.add_route(other);
assert_eq!(m.all_routes().len(), 2);
}
#[test]
fn direction_variants_distinct() {
assert_ne!(Direction::CorbaToDds, Direction::DdsToCorba);
assert_ne!(Direction::CorbaToDds, Direction::Bidirectional);
}
}