use std::collections::HashMap;
use crate::observation::Value;
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct ContextId(pub String);
impl ContextId {
#[must_use]
pub fn background() -> Self {
Self("background".into())
}
#[must_use]
pub fn content_script(tab_id: u32) -> Self {
Self(format!("content:{tab_id}"))
}
#[must_use]
pub fn popup() -> Self {
Self("popup".into())
}
#[must_use]
pub fn options() -> Self {
Self("options".into())
}
#[must_use]
pub fn service_worker() -> Self {
Self("service_worker".into())
}
#[must_use]
pub fn custom(name: impl Into<String>) -> Self {
Self(name.into())
}
}
impl std::fmt::Display for ContextId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ContextMessage {
pub from: ContextId,
pub to: ContextId,
pub payload: Value,
pub channel: Option<String>,
}
const MAX_MESSAGE_HISTORY: usize = 50_000;
const MAX_QUEUE_PER_CONTEXT: usize = 1_000;
#[derive(Debug, Default)]
pub struct MessageBus {
queues: HashMap<ContextId, Vec<ContextMessage>>,
history: Vec<ContextMessage>,
}
impl MessageBus {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn send(&mut self, message: ContextMessage) {
if self.history.len() < MAX_MESSAGE_HISTORY {
self.history.push(message.clone());
}
let queue = self.queues.entry(message.to.clone()).or_default();
if queue.len() < MAX_QUEUE_PER_CONTEXT {
queue.push(message);
}
}
pub fn receive(&mut self, context: &ContextId) -> Vec<ContextMessage> {
self.queues.remove(context).unwrap_or_default()
}
#[must_use]
pub fn history(&self) -> &[ContextMessage] {
&self.history
}
#[must_use]
pub fn has_pending(&self) -> bool {
self.queues.values().any(|q| !q.is_empty())
}
#[must_use]
pub fn history_len(&self) -> usize {
self.history.len()
}
}