use alloc::string::String;
use alloc::vec::Vec;
use zerodds_qos::{
DeadlineQosPolicy, DestinationOrderQosPolicy, DurabilityQosPolicy, DurabilityServiceQosPolicy,
EntityFactoryQosPolicy, GroupDataQosPolicy, HistoryQosPolicy, LatencyBudgetQosPolicy,
LifespanQosPolicy, LivelinessQosPolicy, OwnershipQosPolicy, OwnershipStrengthQosPolicy,
PartitionQosPolicy, PresentationQosPolicy, ReaderDataLifecycleQosPolicy, ReaderQos,
ReliabilityQosPolicy, ResourceLimitsQosPolicy, TimeBasedFilterQosPolicy, TopicDataQosPolicy,
TransportPriorityQosPolicy, UserDataQosPolicy, WriterDataLifecycleQosPolicy, WriterQos,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QosLibrary {
pub name: String,
pub profiles: Vec<QosProfile>,
}
impl QosLibrary {
#[must_use]
pub fn profile(&self, name: &str) -> Option<&QosProfile> {
self.profiles.iter().find(|p| p.name == name)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct QosProfile {
pub name: String,
pub base_name: Option<String>,
pub topic_filter: Option<String>,
pub datawriter_qos: Option<EntityQos>,
pub datareader_qos: Option<EntityQos>,
pub topic_qos: Option<EntityQos>,
pub publisher_qos: Option<EntityQos>,
pub subscriber_qos: Option<EntityQos>,
pub domainparticipant_qos: Option<EntityQos>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct EntityQos {
pub durability: Option<DurabilityQosPolicy>,
pub durability_service: Option<DurabilityServiceQosPolicy>,
pub presentation: Option<PresentationQosPolicy>,
pub deadline: Option<DeadlineQosPolicy>,
pub latency_budget: Option<LatencyBudgetQosPolicy>,
pub ownership: Option<OwnershipQosPolicy>,
pub ownership_strength: Option<OwnershipStrengthQosPolicy>,
pub liveliness: Option<LivelinessQosPolicy>,
pub time_based_filter: Option<TimeBasedFilterQosPolicy>,
pub partition: Option<PartitionQosPolicy>,
pub reliability: Option<ReliabilityQosPolicy>,
pub transport_priority: Option<TransportPriorityQosPolicy>,
pub lifespan: Option<LifespanQosPolicy>,
pub destination_order: Option<DestinationOrderQosPolicy>,
pub history: Option<HistoryQosPolicy>,
pub resource_limits: Option<ResourceLimitsQosPolicy>,
pub entity_factory: Option<EntityFactoryQosPolicy>,
pub writer_data_lifecycle: Option<WriterDataLifecycleQosPolicy>,
pub reader_data_lifecycle: Option<ReaderDataLifecycleQosPolicy>,
pub user_data: Option<UserDataQosPolicy>,
pub topic_data: Option<TopicDataQosPolicy>,
pub group_data: Option<GroupDataQosPolicy>,
}
impl EntityQos {
#[must_use]
pub fn merge(mut self, override_: &Self) -> Self {
macro_rules! merge_field {
($field:ident) => {
if let Some(v) = override_.$field.clone() {
self.$field = Some(v);
}
};
}
merge_field!(durability);
merge_field!(durability_service);
merge_field!(presentation);
merge_field!(deadline);
merge_field!(latency_budget);
merge_field!(ownership);
merge_field!(ownership_strength);
merge_field!(liveliness);
merge_field!(time_based_filter);
merge_field!(partition);
merge_field!(reliability);
merge_field!(transport_priority);
merge_field!(lifespan);
merge_field!(destination_order);
merge_field!(history);
merge_field!(resource_limits);
merge_field!(entity_factory);
merge_field!(writer_data_lifecycle);
merge_field!(reader_data_lifecycle);
merge_field!(user_data);
merge_field!(topic_data);
merge_field!(group_data);
self
}
#[must_use]
pub fn into_writer_qos(&self) -> WriterQos {
let mut q = WriterQos::default();
if let Some(p) = self.durability {
q.durability = p;
}
if let Some(p) = self.durability_service {
q.durability_service = p;
}
if let Some(p) = self.deadline {
q.deadline = p;
}
if let Some(p) = self.latency_budget {
q.latency_budget = p;
}
if let Some(p) = self.liveliness {
q.liveliness = p;
}
if let Some(p) = self.reliability {
q.reliability = p;
}
if let Some(p) = self.destination_order {
q.destination_order = p;
}
if let Some(p) = self.history {
q.history = p;
}
if let Some(p) = self.resource_limits {
q.resource_limits = p;
}
if let Some(p) = self.transport_priority {
q.transport_priority = p;
}
if let Some(p) = self.lifespan {
q.lifespan = p;
}
if let Some(p) = self.ownership {
q.ownership = p;
}
if let Some(p) = self.ownership_strength {
q.ownership_strength = p;
}
if let Some(p) = self.presentation {
q.presentation = p;
}
if let Some(p) = self.partition.clone() {
q.partition = p;
}
if let Some(p) = self.writer_data_lifecycle {
q.writer_data_lifecycle = p;
}
if let Some(p) = self.user_data.clone() {
q.user_data = p;
}
if let Some(p) = self.topic_data.clone() {
q.topic_data = p;
}
if let Some(p) = self.group_data.clone() {
q.group_data = p;
}
q
}
#[must_use]
pub fn into_reader_qos(&self) -> ReaderQos {
let mut q = ReaderQos::default();
if let Some(p) = self.durability {
q.durability = p;
}
if let Some(p) = self.deadline {
q.deadline = p;
}
if let Some(p) = self.latency_budget {
q.latency_budget = p;
}
if let Some(p) = self.liveliness {
q.liveliness = p;
}
if let Some(p) = self.reliability {
q.reliability = p;
}
if let Some(p) = self.destination_order {
q.destination_order = p;
}
if let Some(p) = self.history {
q.history = p;
}
if let Some(p) = self.resource_limits {
q.resource_limits = p;
}
if let Some(p) = self.ownership {
q.ownership = p;
}
if let Some(p) = self.time_based_filter {
q.time_based_filter = p;
}
if let Some(p) = self.presentation {
q.presentation = p;
}
if let Some(p) = self.partition.clone() {
q.partition = p;
}
if let Some(p) = self.reader_data_lifecycle {
q.reader_data_lifecycle = p;
}
if let Some(p) = self.user_data.clone() {
q.user_data = p;
}
if let Some(p) = self.topic_data.clone() {
q.topic_data = p;
}
if let Some(p) = self.group_data.clone() {
q.group_data = p;
}
q
}
}
#[must_use]
pub fn topic_filter_matches(filter: &str, topic_name: &str) -> bool {
let p: Vec<char> = filter.chars().collect();
let n: Vec<char> = topic_name.chars().collect();
let (m, k) = (n.len(), p.len());
let mut dp = alloc::vec![alloc::vec![false; k + 1]; m + 1];
dp[0][0] = true;
for j in 1..=k {
if p[j - 1] == '*' {
dp[0][j] = dp[0][j - 1];
}
}
for i in 1..=m {
for j in 1..=k {
let pc = p[j - 1];
dp[i][j] = if pc == '*' {
dp[i - 1][j] || dp[i][j - 1]
} else if pc == '?' || pc == n[i - 1] {
dp[i - 1][j - 1]
} else {
false
};
}
}
dp[m][k]
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
use zerodds_qos::{DurabilityKind, ReliabilityKind};
#[test]
fn entity_qos_default_is_all_none() {
let e = EntityQos::default();
assert!(e.durability.is_none());
assert!(e.reliability.is_none());
assert!(e.history.is_none());
assert!(e.partition.is_none());
assert!(e.user_data.is_none());
}
#[test]
fn entity_qos_into_writer_uses_defaults_for_unset() {
let e = EntityQos::default();
let wq = e.into_writer_qos();
assert_eq!(wq.reliability.kind, ReliabilityKind::Reliable);
}
#[test]
fn entity_qos_into_reader_uses_defaults_for_unset() {
let e = EntityQos::default();
let rq = e.into_reader_qos();
assert_eq!(rq.reliability.kind, ReliabilityKind::BestEffort);
}
#[test]
fn merge_override_replaces_field() {
let parent = EntityQos {
durability: Some(DurabilityQosPolicy {
kind: DurabilityKind::Volatile,
}),
history: Some(HistoryQosPolicy::default()),
..Default::default()
};
let child = EntityQos {
durability: Some(DurabilityQosPolicy {
kind: DurabilityKind::Persistent,
}),
..Default::default()
};
let merged = parent.merge(&child);
assert_eq!(
merged.durability.unwrap().kind,
DurabilityKind::Persistent,
"child override should win"
);
assert!(merged.history.is_some(), "parent's history should be kept");
}
#[test]
fn merge_none_does_not_clobber() {
let parent = EntityQos {
deadline: Some(DeadlineQosPolicy::default()),
..Default::default()
};
let child = EntityQos::default();
let merged = parent.merge(&child);
assert!(
merged.deadline.is_some(),
"child=None should not clobber parent"
);
}
#[test]
fn library_profile_lookup() {
let lib = QosLibrary {
name: "L".into(),
profiles: alloc::vec![
QosProfile {
name: "A".into(),
..Default::default()
},
QosProfile {
name: "B".into(),
..Default::default()
}
],
};
assert!(lib.profile("A").is_some());
assert!(lib.profile("B").is_some());
assert!(lib.profile("Missing").is_none());
}
#[test]
fn glob_star_matches_all() {
assert!(topic_filter_matches("*", "foo"));
assert!(topic_filter_matches("*", ""));
}
#[test]
fn glob_prefix() {
assert!(topic_filter_matches("foo_*", "foo_bar"));
assert!(topic_filter_matches("foo_*", "foo_"));
assert!(!topic_filter_matches("foo_*", "bar_foo"));
}
#[test]
fn glob_question_mark() {
assert!(topic_filter_matches("s?nsor", "sensor"));
assert!(!topic_filter_matches("s?nsor", "snsor"));
assert!(!topic_filter_matches("s?nsor", "sennsor"));
}
#[test]
fn glob_exact() {
assert!(topic_filter_matches("Chatter", "Chatter"));
assert!(!topic_filter_matches("Chatter", "ChatterX"));
}
}