extern crate alloc;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::sync::{Mutex, OnceLock};
use crate::error::{DdsError, Result};
use crate::participant::{DomainId, DomainParticipant};
use crate::qos::DomainParticipantQos;
use crate::runtime::RuntimeConfig;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DomainParticipantFactoryQos {
pub autoenable_created_entities: bool,
}
impl Default for DomainParticipantFactoryQos {
fn default() -> Self {
Self {
autoenable_created_entities: true,
}
}
}
#[cfg(feature = "std")]
#[derive(Debug)]
pub struct DomainParticipantFactory {
participants: Mutex<BTreeMap<DomainId, Vec<DomainParticipant>>>,
default_participant_qos: Mutex<DomainParticipantQos>,
factory_qos: Mutex<DomainParticipantFactoryQos>,
}
#[cfg(not(feature = "std"))]
#[derive(Debug, Default)]
pub struct DomainParticipantFactory {}
#[cfg(feature = "std")]
impl DomainParticipantFactory {
pub fn instance() -> &'static Self {
static INSTANCE: OnceLock<DomainParticipantFactory> = OnceLock::new();
INSTANCE.get_or_init(|| Self {
participants: Mutex::new(BTreeMap::new()),
default_participant_qos: Mutex::new(DomainParticipantQos::default()),
factory_qos: Mutex::new(DomainParticipantFactoryQos::default()),
})
}
fn track(&self, p: &DomainParticipant) {
if let Ok(mut reg) = self.participants.lock() {
reg.entry(p.domain_id()).or_default().push(p.clone());
}
}
pub fn create_participant(
&self,
domain_id: DomainId,
qos: DomainParticipantQos,
) -> Result<DomainParticipant> {
let config = RuntimeConfig {
user_data: qos.user_data.value.clone(),
..RuntimeConfig::default()
};
let p = DomainParticipant::new_with_runtime(domain_id, qos, config)?;
self.track(&p);
Ok(p)
}
pub fn create_participant_with_config(
&self,
domain_id: DomainId,
qos: DomainParticipantQos,
config: RuntimeConfig,
) -> Result<DomainParticipant> {
let p = DomainParticipant::new_with_runtime(domain_id, qos, config)?;
self.track(&p);
Ok(p)
}
#[must_use]
pub fn create_participant_offline(
&self,
domain_id: DomainId,
qos: DomainParticipantQos,
) -> DomainParticipant {
let p = DomainParticipant::new(domain_id, qos);
self.track(&p);
p
}
#[must_use]
pub fn lookup_participant(&self, domain_id: DomainId) -> Option<DomainParticipant> {
let reg = self.participants.lock().ok()?;
reg.get(&domain_id).and_then(|v| v.first().cloned())
}
pub fn delete_participant(&self, p: &DomainParticipant) -> Result<()> {
let mut reg = self
.participants
.lock()
.map_err(|_| DdsError::PreconditionNotMet {
reason: "factory participants poisoned",
})?;
let target_handle = p.instance_handle();
let mut found = false;
if let Some(vec) = reg.get_mut(&p.domain_id()) {
let before = vec.len();
vec.retain(|q| q.instance_handle() != target_handle);
found = vec.len() < before;
if vec.is_empty() {
reg.remove(&p.domain_id());
}
}
drop(reg);
if !found {
return Err(DdsError::PreconditionNotMet {
reason: "participant not registered with this factory",
});
}
p.delete_contained_entities()
}
pub fn set_default_participant_qos(&self, qos: DomainParticipantQos) -> Result<()> {
let mut current =
self.default_participant_qos
.lock()
.map_err(|_| DdsError::PreconditionNotMet {
reason: "default qos poisoned",
})?;
*current = qos;
Ok(())
}
#[must_use]
pub fn get_default_participant_qos(&self) -> DomainParticipantQos {
self.default_participant_qos
.lock()
.map(|q| q.clone())
.unwrap_or_default()
}
pub fn set_qos(&self, qos: DomainParticipantFactoryQos) -> Result<()> {
let mut current = self
.factory_qos
.lock()
.map_err(|_| DdsError::PreconditionNotMet {
reason: "factory qos poisoned",
})?;
*current = qos;
Ok(())
}
#[must_use]
pub fn get_qos(&self) -> DomainParticipantFactoryQos {
self.factory_qos.lock().map(|q| *q).unwrap_or_default()
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn factory_is_singleton() {
let a = DomainParticipantFactory::instance();
let b = DomainParticipantFactory::instance();
assert!(core::ptr::eq(a, b));
}
#[test]
fn factory_creates_participant_with_correct_domain_id() {
let p = DomainParticipantFactory::instance()
.create_participant_offline(7, DomainParticipantQos::default());
assert_eq!(p.domain_id(), 7);
}
#[test]
fn lookup_participant_finds_registered_offline_participant() {
let f = DomainParticipantFactory::instance();
let domain = 91;
let _p = f.create_participant_offline(domain, DomainParticipantQos::default());
let found = f.lookup_participant(domain);
assert!(found.is_some());
assert_eq!(found.unwrap().domain_id(), domain);
}
#[test]
fn lookup_participant_returns_none_for_unknown_domain() {
let f = DomainParticipantFactory::instance();
assert!(f.lookup_participant(60_001).is_none());
}
#[test]
fn delete_participant_removes_from_registry() {
let f = DomainParticipantFactory::instance();
let domain = 92;
let p = f.create_participant_offline(domain, DomainParticipantQos::default());
assert!(f.lookup_participant(domain).is_some());
f.delete_participant(&p).unwrap();
assert!(f.lookup_participant(domain).is_none());
}
#[test]
fn delete_participant_unknown_returns_precondition_not_met() {
let f = DomainParticipantFactory::instance();
let detached =
crate::participant::DomainParticipant::new(93, DomainParticipantQos::default());
let res = f.delete_participant(&detached);
assert!(matches!(
res,
Err(crate::error::DdsError::PreconditionNotMet { .. })
));
}
#[test]
fn default_participant_qos_roundtrips() {
let f = DomainParticipantFactory::instance();
let mut new_qos = f.get_default_participant_qos();
new_qos = new_qos.clone();
f.set_default_participant_qos(new_qos.clone()).unwrap();
let got = f.get_default_participant_qos();
assert_eq!(format!("{got:?}"), format!("{new_qos:?}"));
}
#[test]
fn factory_qos_default_is_autoenable_true() {
let q = DomainParticipantFactoryQos::default();
assert!(q.autoenable_created_entities);
}
#[test]
fn factory_set_get_qos_roundtrip() {
let f = DomainParticipantFactory::instance();
let q = DomainParticipantFactoryQos {
autoenable_created_entities: false,
};
f.set_qos(q).unwrap();
let got = f.get_qos();
assert!(!got.autoenable_created_entities);
f.set_qos(DomainParticipantFactoryQos::default()).unwrap();
}
}