zerodds_zenoh_bridge/
mapping.rs1extern crate alloc;
9use alloc::string::String;
10use alloc::vec::Vec;
11
12use zerodds_qos::{DurabilityKind, ReliabilityKind};
13
14#[allow(dead_code)] pub const DEFAULT_PREFIX: &str = "dds";
17
18#[must_use]
32pub fn key_expr_for_topic(prefix: &str, partition: &str, topic: &str) -> String {
33 let safe_topic = sanitize(topic);
34 if partition.is_empty() {
35 alloc::format!("{prefix}/{safe_topic}")
36 } else {
37 let safe_part = sanitize(partition);
38 alloc::format!("{prefix}/{safe_part}/{safe_topic}")
39 }
40}
41
42fn sanitize(s: &str) -> String {
43 s.chars()
44 .map(|c| match c {
45 '*' | '?' | '[' | ']' | '$' | '#' => '_',
46 other => other,
47 })
48 .collect()
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum ZenohReliability {
54 Reliable,
56 BestEffort,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum ZenohCongestion {
63 Block,
65 Drop,
67}
68
69#[must_use]
79pub fn dds_qos_to_zenoh(
80 reliability: ReliabilityKind,
81 durability: DurabilityKind,
82) -> (ZenohReliability, ZenohCongestion) {
83 let rel = match reliability {
84 ReliabilityKind::Reliable => ZenohReliability::Reliable,
85 ReliabilityKind::BestEffort => ZenohReliability::BestEffort,
86 };
87 let cong = match durability {
88 DurabilityKind::Volatile => ZenohCongestion::Drop,
89 _ => ZenohCongestion::Block,
90 };
91 (rel, cong)
92}
93
94#[derive(Debug, Default, Clone)]
96pub struct TopicMap {
97 entries: Vec<TopicMapEntry>,
98}
99
100#[derive(Debug, Clone)]
102pub struct TopicMapEntry {
103 pub topic: String,
105 pub type_name: String,
107 pub key_expr: String,
109 pub reliability: ReliabilityKind,
111 pub durability: DurabilityKind,
113}
114
115impl TopicMap {
116 #[must_use]
118 pub fn new() -> Self {
119 Self::default()
120 }
121
122 pub fn add(&mut self, entry: TopicMapEntry) {
124 self.entries.push(entry);
125 }
126
127 #[must_use]
129 pub fn by_topic(&self, topic: &str) -> Option<&TopicMapEntry> {
130 self.entries.iter().find(|e| e.topic == topic)
131 }
132
133 #[must_use]
135 pub fn by_key_expr(&self, key_expr: &str) -> Option<&TopicMapEntry> {
136 self.entries.iter().find(|e| e.key_expr == key_expr)
137 }
138
139 #[must_use]
141 pub fn entries(&self) -> &[TopicMapEntry] {
142 &self.entries
143 }
144}
145
146#[cfg(test)]
147#[allow(clippy::expect_used)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn key_expr_no_partition() {
153 assert_eq!(key_expr_for_topic("dds", "", "Chatter"), "dds/Chatter");
154 }
155
156 #[test]
157 fn key_expr_with_partition() {
158 assert_eq!(
159 key_expr_for_topic("dds", "robot1", "Sensor"),
160 "dds/robot1/Sensor"
161 );
162 }
163
164 #[test]
165 fn key_expr_sanitizes_wildcards() {
166 assert_eq!(key_expr_for_topic("dds", "", "Foo*Bar"), "dds/Foo_Bar");
169 assert_eq!(key_expr_for_topic("dds", "", "Topic[1]"), "dds/Topic_1_");
170 }
171
172 #[test]
173 fn reliability_mapping() {
174 let (r, c) = dds_qos_to_zenoh(ReliabilityKind::Reliable, DurabilityKind::Volatile);
175 assert_eq!(r, ZenohReliability::Reliable);
176 assert_eq!(c, ZenohCongestion::Drop);
177
178 let (r, c) = dds_qos_to_zenoh(ReliabilityKind::BestEffort, DurabilityKind::TransientLocal);
179 assert_eq!(r, ZenohReliability::BestEffort);
180 assert_eq!(c, ZenohCongestion::Block);
181 }
182
183 #[test]
184 fn topic_map_lookup() {
185 let mut m = TopicMap::new();
186 m.add(TopicMapEntry {
187 topic: "Chatter".into(),
188 type_name: "std_msgs::String".into(),
189 key_expr: "dds/Chatter".into(),
190 reliability: ReliabilityKind::Reliable,
191 durability: DurabilityKind::Volatile,
192 });
193 assert!(m.by_topic("Chatter").is_some());
194 assert!(m.by_key_expr("dds/Chatter").is_some());
195 assert!(m.by_topic("Unknown").is_none());
196 }
197}