use std::sync::RwLock;
use std::sync::atomic::{AtomicU64, Ordering};
use serde_json::Value;
use super::bus::{EventBus, Limits, PublishError, check_event_size};
use super::event::{ExecutionId, Topic};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Generation(u64);
struct Installed {
generation: Generation,
bus: EventBus,
}
pub(super) struct CurrentBus {
slot: RwLock<Option<Installed>>,
next_generation: AtomicU64,
}
impl CurrentBus {
pub(super) const fn new() -> Self {
Self {
slot: RwLock::new(None),
next_generation: AtomicU64::new(1),
}
}
pub(super) fn install(&self, bus: EventBus) -> Generation {
let generation = Generation(self.next_generation.fetch_add(1, Ordering::Relaxed));
*self
.slot
.write()
.unwrap_or_else(|poisoned| poisoned.into_inner()) = Some(Installed { generation, bus });
generation
}
pub(super) fn clear(&self, generation: Generation) -> bool {
let mut slot = self
.slot
.write()
.unwrap_or_else(|poisoned| poisoned.into_inner());
if slot
.as_ref()
.is_some_and(|installed| installed.generation == generation)
{
*slot = None;
true
} else {
false
}
}
pub(super) fn publish(
&self,
topic: Topic,
data: Value,
execution: Option<ExecutionId>,
) -> Result<u64, PublishError> {
let slot = self
.slot
.read()
.unwrap_or_else(|poisoned| poisoned.into_inner());
match slot.as_ref() {
Some(installed) => installed.bus.publish(topic, data, execution),
None => {
check_event_size(&topic, &data, Limits::default().max_event_bytes)?;
Err(PublishError::Closed)
}
}
}
}
static CURRENT: CurrentBus = CurrentBus::new();
pub(crate) fn install_current_bus(bus: EventBus) -> Generation {
CURRENT.install(bus)
}
pub(crate) fn clear_current_bus(generation: Generation) -> bool {
CURRENT.clear(generation)
}
pub(crate) fn publish_event(
topic: Topic,
data: Value,
execution: Option<ExecutionId>,
) -> Result<u64, PublishError> {
CURRENT.publish(topic, data, execution)
}