1use alloc::string::String;
63use alloc::vec::Vec;
64
65use zerodds_qos::{
66 DeadlineQosPolicy, DestinationOrderQosPolicy, DurabilityQosPolicy, DurabilityServiceQosPolicy,
67 EntityFactoryQosPolicy, GroupDataQosPolicy, HistoryQosPolicy, LatencyBudgetQosPolicy,
68 LifespanQosPolicy, LivelinessQosPolicy, OwnershipQosPolicy, OwnershipStrengthQosPolicy,
69 PartitionQosPolicy, PresentationQosPolicy, ReaderDataLifecycleQosPolicy, ReaderQos,
70 ReliabilityQosPolicy, ResourceLimitsQosPolicy, TimeBasedFilterQosPolicy, TopicDataQosPolicy,
71 TransportPriorityQosPolicy, UserDataQosPolicy, WriterDataLifecycleQosPolicy, WriterQos,
72};
73
74#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct QosLibrary {
81 pub name: String,
83 pub profiles: Vec<QosProfile>,
85}
86
87impl QosLibrary {
88 #[must_use]
90 pub fn profile(&self, name: &str) -> Option<&QosProfile> {
91 self.profiles.iter().find(|p| p.name == name)
92 }
93}
94
95#[derive(Debug, Clone, Default, PartialEq, Eq)]
101pub struct QosProfile {
102 pub name: String,
104 pub base_name: Option<String>,
108 pub topic_filter: Option<String>,
113 pub datawriter_qos: Option<EntityQos>,
115 pub datareader_qos: Option<EntityQos>,
117 pub topic_qos: Option<EntityQos>,
119 pub publisher_qos: Option<EntityQos>,
121 pub subscriber_qos: Option<EntityQos>,
123 pub domainparticipant_qos: Option<EntityQos>,
125}
126
127#[derive(Debug, Clone, Default, PartialEq, Eq)]
137pub struct EntityQos {
138 pub durability: Option<DurabilityQosPolicy>,
140 pub durability_service: Option<DurabilityServiceQosPolicy>,
142 pub presentation: Option<PresentationQosPolicy>,
144 pub deadline: Option<DeadlineQosPolicy>,
146 pub latency_budget: Option<LatencyBudgetQosPolicy>,
148 pub ownership: Option<OwnershipQosPolicy>,
150 pub ownership_strength: Option<OwnershipStrengthQosPolicy>,
152 pub liveliness: Option<LivelinessQosPolicy>,
154 pub time_based_filter: Option<TimeBasedFilterQosPolicy>,
156 pub partition: Option<PartitionQosPolicy>,
158 pub reliability: Option<ReliabilityQosPolicy>,
160 pub transport_priority: Option<TransportPriorityQosPolicy>,
162 pub lifespan: Option<LifespanQosPolicy>,
164 pub destination_order: Option<DestinationOrderQosPolicy>,
166 pub history: Option<HistoryQosPolicy>,
168 pub resource_limits: Option<ResourceLimitsQosPolicy>,
170 pub entity_factory: Option<EntityFactoryQosPolicy>,
172 pub writer_data_lifecycle: Option<WriterDataLifecycleQosPolicy>,
174 pub reader_data_lifecycle: Option<ReaderDataLifecycleQosPolicy>,
176 pub user_data: Option<UserDataQosPolicy>,
178 pub topic_data: Option<TopicDataQosPolicy>,
180 pub group_data: Option<GroupDataQosPolicy>,
182}
183
184impl EntityQos {
185 #[must_use]
190 pub fn merge(mut self, override_: &Self) -> Self {
191 macro_rules! merge_field {
192 ($field:ident) => {
193 if let Some(v) = override_.$field.clone() {
194 self.$field = Some(v);
195 }
196 };
197 }
198 merge_field!(durability);
199 merge_field!(durability_service);
200 merge_field!(presentation);
201 merge_field!(deadline);
202 merge_field!(latency_budget);
203 merge_field!(ownership);
204 merge_field!(ownership_strength);
205 merge_field!(liveliness);
206 merge_field!(time_based_filter);
207 merge_field!(partition);
208 merge_field!(reliability);
209 merge_field!(transport_priority);
210 merge_field!(lifespan);
211 merge_field!(destination_order);
212 merge_field!(history);
213 merge_field!(resource_limits);
214 merge_field!(entity_factory);
215 merge_field!(writer_data_lifecycle);
216 merge_field!(reader_data_lifecycle);
217 merge_field!(user_data);
218 merge_field!(topic_data);
219 merge_field!(group_data);
220 self
221 }
222
223 #[must_use]
231 pub fn into_writer_qos(&self) -> WriterQos {
232 let mut q = WriterQos::default();
233 if let Some(p) = self.durability {
234 q.durability = p;
235 }
236 if let Some(p) = self.durability_service {
237 q.durability_service = p;
238 }
239 if let Some(p) = self.deadline {
240 q.deadline = p;
241 }
242 if let Some(p) = self.latency_budget {
243 q.latency_budget = p;
244 }
245 if let Some(p) = self.liveliness {
246 q.liveliness = p;
247 }
248 if let Some(p) = self.reliability {
249 q.reliability = p;
250 }
251 if let Some(p) = self.destination_order {
252 q.destination_order = p;
253 }
254 if let Some(p) = self.history {
255 q.history = p;
256 }
257 if let Some(p) = self.resource_limits {
258 q.resource_limits = p;
259 }
260 if let Some(p) = self.transport_priority {
261 q.transport_priority = p;
262 }
263 if let Some(p) = self.lifespan {
264 q.lifespan = p;
265 }
266 if let Some(p) = self.ownership {
267 q.ownership = p;
268 }
269 if let Some(p) = self.ownership_strength {
270 q.ownership_strength = p;
271 }
272 if let Some(p) = self.presentation {
273 q.presentation = p;
274 }
275 if let Some(p) = self.partition.clone() {
276 q.partition = p;
277 }
278 if let Some(p) = self.writer_data_lifecycle {
279 q.writer_data_lifecycle = p;
280 }
281 if let Some(p) = self.user_data.clone() {
282 q.user_data = p;
283 }
284 if let Some(p) = self.topic_data.clone() {
285 q.topic_data = p;
286 }
287 if let Some(p) = self.group_data.clone() {
288 q.group_data = p;
289 }
290 q
291 }
292
293 #[must_use]
295 pub fn into_reader_qos(&self) -> ReaderQos {
296 let mut q = ReaderQos::default();
297 if let Some(p) = self.durability {
298 q.durability = p;
299 }
300 if let Some(p) = self.deadline {
301 q.deadline = p;
302 }
303 if let Some(p) = self.latency_budget {
304 q.latency_budget = p;
305 }
306 if let Some(p) = self.liveliness {
307 q.liveliness = p;
308 }
309 if let Some(p) = self.reliability {
310 q.reliability = p;
311 }
312 if let Some(p) = self.destination_order {
313 q.destination_order = p;
314 }
315 if let Some(p) = self.history {
316 q.history = p;
317 }
318 if let Some(p) = self.resource_limits {
319 q.resource_limits = p;
320 }
321 if let Some(p) = self.ownership {
322 q.ownership = p;
323 }
324 if let Some(p) = self.time_based_filter {
325 q.time_based_filter = p;
326 }
327 if let Some(p) = self.presentation {
328 q.presentation = p;
329 }
330 if let Some(p) = self.partition.clone() {
331 q.partition = p;
332 }
333 if let Some(p) = self.reader_data_lifecycle {
334 q.reader_data_lifecycle = p;
335 }
336 if let Some(p) = self.user_data.clone() {
337 q.user_data = p;
338 }
339 if let Some(p) = self.topic_data.clone() {
340 q.topic_data = p;
341 }
342 if let Some(p) = self.group_data.clone() {
343 q.group_data = p;
344 }
345 q
346 }
347}
348
349#[must_use]
360pub fn topic_filter_matches(filter: &str, topic_name: &str) -> bool {
361 let p: Vec<char> = filter.chars().collect();
362 let n: Vec<char> = topic_name.chars().collect();
363 let (m, k) = (n.len(), p.len());
364 let mut dp = alloc::vec![alloc::vec![false; k + 1]; m + 1];
365 dp[0][0] = true;
366 for j in 1..=k {
367 if p[j - 1] == '*' {
368 dp[0][j] = dp[0][j - 1];
369 }
370 }
371 for i in 1..=m {
372 for j in 1..=k {
373 let pc = p[j - 1];
374 dp[i][j] = if pc == '*' {
375 dp[i - 1][j] || dp[i][j - 1]
376 } else if pc == '?' || pc == n[i - 1] {
377 dp[i - 1][j - 1]
378 } else {
379 false
380 };
381 }
382 }
383 dp[m][k]
384}
385
386#[cfg(test)]
387#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
388mod tests {
389 use super::*;
390 use zerodds_qos::{DurabilityKind, ReliabilityKind};
391
392 #[test]
393 fn entity_qos_default_is_all_none() {
394 let e = EntityQos::default();
395 assert!(e.durability.is_none());
396 assert!(e.reliability.is_none());
397 assert!(e.history.is_none());
398 assert!(e.partition.is_none());
399 assert!(e.user_data.is_none());
400 }
401
402 #[test]
403 fn entity_qos_into_writer_uses_defaults_for_unset() {
404 let e = EntityQos::default();
405 let wq = e.into_writer_qos();
406 assert_eq!(wq.reliability.kind, ReliabilityKind::Reliable);
408 }
409
410 #[test]
411 fn entity_qos_into_reader_uses_defaults_for_unset() {
412 let e = EntityQos::default();
413 let rq = e.into_reader_qos();
414 assert_eq!(rq.reliability.kind, ReliabilityKind::BestEffort);
416 }
417
418 #[test]
419 fn merge_override_replaces_field() {
420 let parent = EntityQos {
421 durability: Some(DurabilityQosPolicy {
422 kind: DurabilityKind::Volatile,
423 }),
424 history: Some(HistoryQosPolicy::default()),
425 ..Default::default()
426 };
427 let child = EntityQos {
428 durability: Some(DurabilityQosPolicy {
429 kind: DurabilityKind::Persistent,
430 }),
431 ..Default::default()
432 };
433
434 let merged = parent.merge(&child);
435 assert_eq!(
436 merged.durability.unwrap().kind,
437 DurabilityKind::Persistent,
438 "child override should win"
439 );
440 assert!(merged.history.is_some(), "parent's history should be kept");
441 }
442
443 #[test]
444 fn merge_none_does_not_clobber() {
445 let parent = EntityQos {
446 deadline: Some(DeadlineQosPolicy::default()),
447 ..Default::default()
448 };
449 let child = EntityQos::default();
450 let merged = parent.merge(&child);
451 assert!(
452 merged.deadline.is_some(),
453 "child=None should not clobber parent"
454 );
455 }
456
457 #[test]
458 fn library_profile_lookup() {
459 let lib = QosLibrary {
460 name: "L".into(),
461 profiles: alloc::vec![
462 QosProfile {
463 name: "A".into(),
464 ..Default::default()
465 },
466 QosProfile {
467 name: "B".into(),
468 ..Default::default()
469 }
470 ],
471 };
472 assert!(lib.profile("A").is_some());
473 assert!(lib.profile("B").is_some());
474 assert!(lib.profile("Missing").is_none());
475 }
476
477 #[test]
480 fn glob_star_matches_all() {
481 assert!(topic_filter_matches("*", "foo"));
482 assert!(topic_filter_matches("*", ""));
483 }
484
485 #[test]
486 fn glob_prefix() {
487 assert!(topic_filter_matches("foo_*", "foo_bar"));
488 assert!(topic_filter_matches("foo_*", "foo_"));
489 assert!(!topic_filter_matches("foo_*", "bar_foo"));
490 }
491
492 #[test]
493 fn glob_question_mark() {
494 assert!(topic_filter_matches("s?nsor", "sensor"));
495 assert!(!topic_filter_matches("s?nsor", "snsor"));
496 assert!(!topic_filter_matches("s?nsor", "sennsor"));
497 }
498
499 #[test]
500 fn glob_exact() {
501 assert!(topic_filter_matches("Chatter", "Chatter"));
502 assert!(!topic_filter_matches("Chatter", "ChatterX"));
503 }
504}