use std::collections::BTreeMap;
use yog_sync::RwLock;
use yog_data::Shape;
use yog_event::bus::{AddressKind, Describe, Descriptor, Find, List};
use yog_hot_reload::ModuleGeneration;
use super::duties::provide;
static DESCRIPTORS: RwLock<BTreeMap<String, (Descriptor, Option<ModuleGeneration>)>> =
RwLock::new(BTreeMap::new());
fn doc(shape: &Shape) -> String {
match shape {
Shape::Struct(s) => s.doc.to_owned(),
_ => String::new(),
}
}
fn declare(d: Descriptor, owner: Option<ModuleGeneration>) {
DESCRIPTORS.write().insert(d.address.clone(), (d, owner));
}
pub(crate) fn purge(generation: ModuleGeneration) {
DESCRIPTORS
.write()
.retain(|_, (_, owner)| *owner != Some(generation));
}
fn describe(address: &str) -> Option<Descriptor> {
let all = DESCRIPTORS.read();
let mut at = address;
loop {
if let Some((d, _)) = all.get(at) {
return Some(d.clone());
}
at = &at[..at.rfind(yog_ids::sep::PATH)?];
}
}
fn declare_vocabulary() {
for e in yog_event::all() {
let shape = e.shape.describe();
for address in std::iter::once(e.address).chain(e.pre) {
declare(
Descriptor {
address: address.to_owned(),
kind: AddressKind::Event,
doc: doc(&e.shape),
shape: shape.clone(),
reply: None,
interfaces: Vec::new(),
},
None,
);
}
}
for s in yog_event::services() {
declare(
Descriptor {
address: s.address.to_owned(),
kind: AddressKind::Service,
doc: doc(&s.shape),
shape: s.shape.describe(),
reply: Some(s.reply.describe()),
interfaces: Vec::new(),
},
None,
);
}
}
pub(crate) fn register() {
declare_vocabulary();
provide::<Descriptor>(|d| {
declare(d.clone(), super::owner().map(|m| m.generation()));
true
});
provide::<Describe>(|d| describe(&d.address));
provide::<List>(|l| {
DESCRIPTORS
.read()
.keys()
.filter(|a| a.starts_with(&l.prefix))
.cloned()
.collect()
});
provide::<Find>(|f| {
DESCRIPTORS
.read()
.values()
.filter(|(d, _)| d.interfaces.contains(&f.interface))
.map(|(d, _)| d.address.clone())
.collect()
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_keyed_branch_is_described_by_its_event() {
declare_vocabulary();
let tick = <yog_event::entity::game::Tick as yog_event::Event>::ADDRESS;
let d = describe(&yog_event::keyed(tick, "mymod:golem")).expect("its event's descriptor");
assert_eq!((d.address.as_str(), d.kind), (tick, AddressKind::Event));
assert!(describe("nobody:nothing").is_none());
}
#[test]
fn a_reloaded_mod_s_descriptors_go() {
let generation = yog_hot_reload::GenerationAllocator::new().next();
let d = Descriptor {
address: "test:gone".into(),
kind: AddressKind::Service,
doc: String::new(),
shape: yog_data::Value::Null,
reply: None,
interfaces: Vec::new(),
};
declare(d, Some(generation));
assert!(describe("test:gone").is_some());
purge(generation);
assert!(describe("test:gone").is_none());
}
}