use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::rc::Rc;
use crate::actor::shape::Declared;
use super::{Scope, ScopeData};
#[derive(Clone, Debug, PartialEq)]
pub struct Listener {
pub event: &'static str,
pub actor: Option<&'static str>,
pub bus: crate::trace::Bus,
pub feature: Option<&'static str>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Installed {
pub name: &'static str,
pub declared: Option<Declared>,
}
impl Scope {
pub fn mark_feature_installed<F: 'static>(&self) {
let data = self.installing("installing a feature");
let newly_inserted = data.installed_features.borrow_mut().insert(TypeId::of::<F>());
assert!(
newly_inserted,
"feature already installed in this scope - install() called twice for the same feature"
);
}
pub fn claims<R: 'static>(&self) -> bool {
self.data()
.is_some_and(|data| data.owners.borrow().contains_key(&TypeId::of::<R>()))
}
pub fn note_reducer_owner<R: 'static>(&self) {
let data = self.installing("claiming a reducer");
data.installed_features.borrow_mut().insert(TypeId::of::<R>());
let section = data.current_section();
data.owners.borrow_mut().entry(TypeId::of::<R>()).or_insert(section);
}
pub fn note_reducer_declared<R: 'static>(&self, declared: Declared) {
let data = self.installing("claiming a reducer");
data.declarations.borrow_mut().entry(TypeId::of::<R>()).or_insert(declared);
}
pub fn current_crate_dir(&self) -> Option<&'static str> {
let data = self.data()?;
let section = data.current_section();
let declarations = data.section_declarations.borrow();
declarations.get(section).copied().flatten().map(|declared| declared.crate_dir)
}
pub fn open_section(&self, name: Option<&'static str>, declared: Option<Declared>) -> usize {
let data = self.installing("installing a feature");
let mut sections = data.sections.borrow_mut();
if sections.is_empty() {
sections.push(HashMap::new());
}
sections.push(HashMap::new());
let index = sections.len() - 1;
let mut names = data.section_names.borrow_mut();
names.resize(index + 1, None);
names[index] = name;
let mut declarations = data.section_declarations.borrow_mut();
declarations.resize(index + 1, None);
declarations[index] = declared;
data.installing.borrow_mut().push(index);
index
}
pub fn section_name(&self, section: usize) -> Option<&'static str> {
self.data()?.section_name(section)
}
pub fn features(&self) -> Vec<Installed> {
let Some(data) = self.data() else {
return Vec::new();
};
let declarations = data.section_declarations.borrow();
data.section_names
.borrow()
.iter()
.enumerate()
.filter_map(|(section, name)| {
Some(Installed {
name: (*name)?,
declared: declarations.get(section).copied().flatten(),
})
})
.collect()
}
pub fn current_feature(&self) -> Option<&'static str> {
let data = self.data()?;
data.section_name(data.current_section())
}
pub fn note_listener(
&self,
event: &'static str,
actor: Option<&'static str>,
bus: crate::trace::Bus,
) {
let Some(data) = self.data() else { return };
let feature = data.section_name(data.current_section());
data.listeners.borrow_mut().push(Listener {
event,
actor,
bus,
feature,
});
}
pub fn listeners(&self) -> Vec<Listener> {
self.data()
.map(|data| data.listeners.borrow().clone())
.unwrap_or_default()
}
pub fn close_section(&self) {
if let Some(data) = self.data() {
data.installing.borrow_mut().pop();
}
}
pub fn current_section(&self) -> usize {
self.data().map_or(0, |data| data.current_section())
}
pub fn section_of<R: 'static>(&self) -> usize {
self.data()
.and_then(|data| data.owners.borrow().get(&TypeId::of::<R>()).copied())
.unwrap_or(0)
}
pub fn has_feature<F: 'static>(&self) -> bool {
self.data()
.is_some_and(|data| data.installed_features.borrow().contains(&TypeId::of::<F>()))
}
pub fn note_export<R: 'static>(&self) {
let data = self.installing("exporting a reducer");
data.exports.borrow_mut().insert(TypeId::of::<R>());
}
pub fn exports<R: 'static>(&self) -> bool {
self.data()
.is_some_and(|data| data.exports.borrow().contains(&TypeId::of::<R>()))
}
pub fn answers<M: 'static>(&self, answer: impl Fn(M) + 'static) {
let data = self.installing("answering an action");
let answer: Rc<dyn Fn(M)> = Rc::new(answer);
let section = data.current_section();
let mut sections = data.sections.borrow_mut();
while sections.len() <= section {
sections.push(HashMap::new());
}
sections[section].insert(TypeId::of::<M>(), Rc::new(answer) as Rc<dyn Any>);
}
pub fn answerer<M: 'static>(&self, section: usize) -> Option<Rc<dyn Fn(M)>> {
self.data()?.answerer::<M>(section)
}
pub fn first_answerer<M: 'static>(&self) -> Option<Rc<dyn Fn(M)>> {
let data = self.data()?;
let sections = data.sections.borrow().len();
(0..sections).find_map(|section| data.answerer::<M>(section))
}
}
impl ScopeData {
fn answerer<M: 'static>(&self, section: usize) -> Option<Rc<dyn Fn(M)>> {
let sections = self.sections.borrow();
let answer = sections.get(section)?.get(&TypeId::of::<M>())?.clone();
answer.downcast::<Rc<dyn Fn(M)>>().ok().map(|a| (*a).clone())
}
}