extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use zerodds_qos::{DurabilityKind, ReliabilityKind};
#[allow(dead_code)] pub const DEFAULT_PREFIX: &str = "dds";
#[must_use]
pub fn key_expr_for_topic(prefix: &str, partition: &str, topic: &str) -> String {
let safe_topic = sanitize(topic);
if partition.is_empty() {
alloc::format!("{prefix}/{safe_topic}")
} else {
let safe_part = sanitize(partition);
alloc::format!("{prefix}/{safe_part}/{safe_topic}")
}
}
fn sanitize(s: &str) -> String {
s.chars()
.map(|c| match c {
'*' | '?' | '[' | ']' | '$' | '#' => '_',
other => other,
})
.collect()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ZenohReliability {
Reliable,
BestEffort,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ZenohCongestion {
Block,
Drop,
}
#[must_use]
pub fn dds_qos_to_zenoh(
reliability: ReliabilityKind,
durability: DurabilityKind,
) -> (ZenohReliability, ZenohCongestion) {
let rel = match reliability {
ReliabilityKind::Reliable => ZenohReliability::Reliable,
ReliabilityKind::BestEffort => ZenohReliability::BestEffort,
};
let cong = match durability {
DurabilityKind::Volatile => ZenohCongestion::Drop,
_ => ZenohCongestion::Block,
};
(rel, cong)
}
#[derive(Debug, Default, Clone)]
pub struct TopicMap {
entries: Vec<TopicMapEntry>,
}
#[derive(Debug, Clone)]
pub struct TopicMapEntry {
pub topic: String,
pub type_name: String,
pub key_expr: String,
pub reliability: ReliabilityKind,
pub durability: DurabilityKind,
}
impl TopicMap {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, entry: TopicMapEntry) {
self.entries.push(entry);
}
#[must_use]
pub fn by_topic(&self, topic: &str) -> Option<&TopicMapEntry> {
self.entries.iter().find(|e| e.topic == topic)
}
#[must_use]
pub fn by_key_expr(&self, key_expr: &str) -> Option<&TopicMapEntry> {
self.entries.iter().find(|e| e.key_expr == key_expr)
}
#[must_use]
pub fn entries(&self) -> &[TopicMapEntry] {
&self.entries
}
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn key_expr_no_partition() {
assert_eq!(key_expr_for_topic("dds", "", "Chatter"), "dds/Chatter");
}
#[test]
fn key_expr_with_partition() {
assert_eq!(
key_expr_for_topic("dds", "robot1", "Sensor"),
"dds/robot1/Sensor"
);
}
#[test]
fn key_expr_sanitizes_wildcards() {
assert_eq!(key_expr_for_topic("dds", "", "Foo*Bar"), "dds/Foo_Bar");
assert_eq!(key_expr_for_topic("dds", "", "Topic[1]"), "dds/Topic_1_");
}
#[test]
fn reliability_mapping() {
let (r, c) = dds_qos_to_zenoh(ReliabilityKind::Reliable, DurabilityKind::Volatile);
assert_eq!(r, ZenohReliability::Reliable);
assert_eq!(c, ZenohCongestion::Drop);
let (r, c) = dds_qos_to_zenoh(ReliabilityKind::BestEffort, DurabilityKind::TransientLocal);
assert_eq!(r, ZenohReliability::BestEffort);
assert_eq!(c, ZenohCongestion::Block);
}
#[test]
fn topic_map_lookup() {
let mut m = TopicMap::new();
m.add(TopicMapEntry {
topic: "Chatter".into(),
type_name: "std_msgs::String".into(),
key_expr: "dds/Chatter".into(),
reliability: ReliabilityKind::Reliable,
durability: DurabilityKind::Volatile,
});
assert!(m.by_topic("Chatter").is_some());
assert!(m.by_key_expr("dds/Chatter").is_some());
assert!(m.by_topic("Unknown").is_none());
}
}