use std::{
collections::HashMap,
sync::{
mpsc::{self, Receiver, Sender},
Arc, Mutex,
},
};
pub trait HasFields {
type Fields;
fn fields() -> Self::Fields;
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct FieldName(String);
impl FieldName {
pub fn as_str(&self) -> &str {
&self.0
}
pub fn static_lit(lit: &'static str) -> Self {
FieldName(lit.into())
}
pub fn from_string(s: String) -> Self {
FieldName(s)
}
pub fn join(prefix: &str, key: &'static str) -> Self {
if prefix.is_empty() {
FieldName::static_lit(key)
} else if key.is_empty() {
FieldName::from_string(prefix.to_owned())
} else {
FieldName::from_string(format!("{prefix}.{key}"))
}
}
}
pub trait AsField {
fn as_field(&self) -> FieldName;
}
impl AsField for FieldName {
fn as_field(&self) -> FieldName {
self.clone()
}
}
impl AsField for &str {
fn as_field(&self) -> FieldName {
FieldName::from_string(self.to_string())
}
}
impl AsField for String {
fn as_field(&self) -> FieldName {
FieldName::from_string(self.clone())
}
}
#[derive(Default, Clone)]
pub struct ChangeEventBus {
inner: Arc<Mutex<Registry>>,
}
#[derive(Default)]
struct Registry {
subs: HashMap<String, Vec<Sender<String>>>,
}
impl ChangeEventBus {
pub fn new() -> Self {
Self::default()
}
pub fn subscribe(&mut self, field: FieldName) -> Receiver<String> {
let path = field.0.clone(); let (tx, rx) = mpsc::channel();
self.inner
.lock()
.unwrap()
.subs
.entry(path)
.or_default()
.push(tx);
rx
}
pub fn publish(&self, path: &str, new_value: String) {
if let Some(list) = self.inner.lock().unwrap().subs.get_mut(path) {
list.retain(|tx| tx.send(new_value.clone()).is_ok());
}
}
}