extern crate alloc;
use alloc::sync::Arc;
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use crate::error::{DdsError, Result};
use crate::instance_handle::{InstanceHandle, InstanceHandleAllocator};
static ENTITY_HANDLE_ALLOCATOR: InstanceHandleAllocator = InstanceHandleAllocator::new();
pub type StatusMask = u32;
#[derive(Debug)]
pub struct EntityState {
enabled: AtomicBool,
deleted: AtomicBool,
instance_handle: InstanceHandle,
status_changes: AtomicU32,
listener_mask: AtomicU32,
}
impl EntityState {
#[must_use]
pub fn new() -> Arc<Self> {
Arc::new(Self {
enabled: AtomicBool::new(false),
deleted: AtomicBool::new(false),
instance_handle: ENTITY_HANDLE_ALLOCATOR.allocate(),
status_changes: AtomicU32::new(0),
listener_mask: AtomicU32::new(0),
})
}
#[must_use]
pub fn new_enabled() -> Arc<Self> {
Arc::new(Self {
enabled: AtomicBool::new(true),
deleted: AtomicBool::new(false),
instance_handle: ENTITY_HANDLE_ALLOCATOR.allocate(),
status_changes: AtomicU32::new(0),
listener_mask: AtomicU32::new(0),
})
}
#[must_use]
pub fn is_enabled(&self) -> bool {
self.enabled.load(Ordering::Acquire)
}
pub fn enable(&self) -> bool {
!self.enabled.swap(true, Ordering::AcqRel)
}
#[must_use]
pub fn instance_handle(&self) -> InstanceHandle {
self.instance_handle
}
#[must_use]
pub fn status_changes(&self) -> StatusMask {
self.status_changes.load(Ordering::Acquire)
}
pub fn set_status_bits(&self, bits: StatusMask) {
self.status_changes.fetch_or(bits, Ordering::AcqRel);
}
pub fn clear_status_changes(&self, bits: StatusMask) {
self.status_changes.fetch_and(!bits, Ordering::AcqRel);
}
pub fn set_listener_mask(&self, mask: StatusMask) {
self.listener_mask.store(mask, Ordering::Release);
}
#[must_use]
pub fn listener_mask(&self) -> StatusMask {
self.listener_mask.load(Ordering::Acquire)
}
#[must_use]
pub fn is_deleted(&self) -> bool {
self.deleted.load(Ordering::Acquire)
}
pub fn mark_deleted(&self) -> bool {
!self.deleted.swap(true, Ordering::AcqRel)
}
pub fn check_not_deleted(&self) -> crate::error::Result<()> {
if self.is_deleted() {
Err(crate::error::DdsError::AlreadyDeleted)
} else {
Ok(())
}
}
pub fn check_enabled(&self) -> crate::error::Result<()> {
if !self.is_enabled() {
Err(crate::error::DdsError::NotEnabled)
} else {
Ok(())
}
}
}
#[derive(Debug, Clone)]
pub struct StatusCondition {
state: Arc<EntityState>,
enabled_statuses: Arc<AtomicU32>,
}
impl StatusCondition {
#[must_use]
pub fn new(state: Arc<EntityState>) -> Self {
Self {
state,
enabled_statuses: Arc::new(AtomicU32::new(crate::psm_constants::status::ANY)),
}
}
pub fn set_enabled_statuses(&self, mask: StatusMask) {
self.enabled_statuses.store(mask, Ordering::Release);
}
#[must_use]
pub fn enabled_statuses(&self) -> StatusMask {
self.enabled_statuses.load(Ordering::Acquire)
}
#[must_use]
pub fn trigger_value(&self) -> bool {
let enabled = self.enabled_statuses.load(Ordering::Acquire);
let changes = self.state.status_changes();
(enabled & changes) != 0
}
#[must_use]
pub fn get_entity_handle(&self) -> InstanceHandle {
self.state.instance_handle()
}
#[must_use]
pub fn entity_state(&self) -> &Arc<EntityState> {
&self.state
}
}
pub trait Entity {
type Qos: Clone;
fn get_qos(&self) -> Self::Qos;
fn set_qos(&self, qos: Self::Qos) -> Result<()>;
fn enable(&self) -> Result<()>;
fn is_enabled(&self) -> bool {
self.entity_state().is_enabled()
}
fn get_status_condition(&self) -> StatusCondition {
StatusCondition::new(self.entity_state())
}
fn get_status_changes(&self) -> StatusMask {
self.entity_state().status_changes()
}
fn get_instance_handle(&self) -> InstanceHandle {
self.entity_state().instance_handle()
}
fn entity_state(&self) -> Arc<EntityState>;
}
#[must_use]
pub fn immutable_if_enabled(policy_name: &'static str) -> DdsError {
DdsError::ImmutablePolicy {
policy: policy_name,
}
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn entity_state_starts_disabled() {
let s = EntityState::new();
assert!(!s.is_enabled());
}
#[test]
fn entity_state_factory_starts_enabled() {
let s = EntityState::new_enabled();
assert!(s.is_enabled());
}
#[test]
fn enable_is_idempotent_and_reports_first_transition() {
let s = EntityState::new();
assert!(s.enable(), "first enable returns true");
assert!(!s.enable(), "second enable returns false");
assert!(s.is_enabled());
}
#[test]
fn instance_handles_are_unique_per_entity() {
let a = EntityState::new();
let b = EntityState::new();
assert_ne!(a.instance_handle(), b.instance_handle());
}
#[test]
fn status_bits_or_in_and_clear() {
let s = EntityState::new();
s.set_status_bits(0b0011);
s.set_status_bits(0b1100);
assert_eq!(s.status_changes(), 0b1111);
s.clear_status_changes(0b0101);
assert_eq!(s.status_changes(), 0b1010);
}
#[test]
fn status_condition_trigger_value() {
let s = EntityState::new();
let cond = StatusCondition::new(s.clone());
cond.set_enabled_statuses(0b1010);
assert!(!cond.trigger_value());
s.set_status_bits(0b0001);
assert!(!cond.trigger_value());
s.set_status_bits(0b0010);
assert!(cond.trigger_value());
}
#[test]
fn listener_mask_is_round_tripped() {
let s = EntityState::new();
s.set_listener_mask(0xABCD);
assert_eq!(s.listener_mask(), 0xABCD);
}
#[test]
fn immutable_if_enabled_returns_correct_error() {
let e = immutable_if_enabled("DURABILITY");
assert!(matches!(
e,
DdsError::ImmutablePolicy {
policy: "DURABILITY"
}
));
}
#[test]
fn check_not_deleted_passes_for_fresh_entity() {
let s = EntityState::new();
assert!(s.check_not_deleted().is_ok());
assert!(!s.is_deleted());
}
#[test]
fn check_not_deleted_returns_already_deleted_after_mark() {
let s = EntityState::new();
let first = s.mark_deleted();
assert!(first, "first mark_deleted should return true");
assert!(s.is_deleted());
let res = s.check_not_deleted();
assert!(matches!(res, Err(DdsError::AlreadyDeleted)));
}
#[test]
fn mark_deleted_is_idempotent() {
let s = EntityState::new();
assert!(s.mark_deleted());
assert!(!s.mark_deleted());
assert!(s.is_deleted());
}
#[test]
fn check_enabled_returns_not_enabled_for_disabled_entity() {
let s = EntityState::new();
assert!(!s.is_enabled());
let res = s.check_enabled();
assert!(matches!(res, Err(DdsError::NotEnabled)));
}
#[test]
fn check_enabled_passes_after_enable() {
let s = EntityState::new();
let _ = s.enable();
assert!(s.check_enabled().is_ok());
}
#[test]
fn check_enabled_passes_for_factory_entity() {
let s = EntityState::new_enabled();
assert!(s.check_enabled().is_ok());
}
#[test]
fn status_condition_get_entity_handle_matches_owner_state() {
let state = EntityState::new();
let cond = StatusCondition::new(state.clone());
assert_eq!(cond.get_entity_handle(), state.instance_handle());
}
#[test]
fn status_condition_get_entity_handle_unique_per_entity() {
let s1 = EntityState::new();
let s2 = EntityState::new();
let c1 = StatusCondition::new(s1);
let c2 = StatusCondition::new(s2);
assert_ne!(c1.get_entity_handle(), c2.get_entity_handle());
}
#[test]
fn status_condition_entity_state_returns_same_arc() {
let state = EntityState::new();
let cond = StatusCondition::new(state.clone());
assert!(Arc::ptr_eq(&state, cond.entity_state()));
}
#[test]
fn status_condition_entity_state_reflects_lifecycle_changes() {
let state = EntityState::new();
let cond = StatusCondition::new(state.clone());
assert!(!cond.entity_state().is_enabled());
let _ = state.enable();
assert!(cond.entity_state().is_enabled());
let _ = state.mark_deleted();
assert!(cond.entity_state().is_deleted());
}
}