guinea_core/scope/
features.rs1use std::any::{Any, TypeId};
2use std::collections::HashMap;
3use std::rc::Rc;
4
5use crate::actor::shape::Declared;
6
7use super::{Scope, ScopeData};
8
9#[derive(Clone, Debug, PartialEq)]
11pub struct Listener {
12 pub event: &'static str,
13 pub actor: Option<&'static str>,
15 pub bus: crate::trace::Bus,
16 pub feature: Option<&'static str>,
17}
18
19#[derive(Clone, Copy, Debug, PartialEq)]
21pub struct Installed {
22 pub name: &'static str,
23 pub declared: Option<Declared>,
25}
26
27impl Scope {
28 pub fn mark_feature_installed<F: 'static>(&self) {
34 let data = self.installing("installing a feature");
35 let newly_inserted = data.installed_features.borrow_mut().insert(TypeId::of::<F>());
36 assert!(
37 newly_inserted,
38 "feature already installed in this scope - install() called twice for the same feature"
39 );
40 }
41
42 pub fn claims<R: 'static>(&self) -> bool {
46 self.data()
47 .is_some_and(|data| data.owners.borrow().contains_key(&TypeId::of::<R>()))
48 }
49
50 pub fn note_reducer_owner<R: 'static>(&self) {
51 let data = self.installing("claiming a reducer");
52 data.installed_features.borrow_mut().insert(TypeId::of::<R>());
53 let section = data.current_section();
54 data.owners.borrow_mut().entry(TypeId::of::<R>()).or_insert(section);
55 }
56
57 pub fn note_reducer_declared<R: 'static>(&self, declared: Declared) {
59 let data = self.installing("claiming a reducer");
60 data.declarations.borrow_mut().entry(TypeId::of::<R>()).or_insert(declared);
61 }
62
63 pub fn current_crate_dir(&self) -> Option<&'static str> {
66 let data = self.data()?;
67 let section = data.current_section();
68 let declarations = data.section_declarations.borrow();
69 declarations.get(section).copied().flatten().map(|declared| declared.crate_dir)
70 }
71
72 pub fn open_section(&self, name: Option<&'static str>, declared: Option<Declared>) -> usize {
75 let data = self.installing("installing a feature");
76 let mut sections = data.sections.borrow_mut();
77 if sections.is_empty() {
78 sections.push(HashMap::new());
79 }
80 sections.push(HashMap::new());
81 let index = sections.len() - 1;
82 let mut names = data.section_names.borrow_mut();
83 names.resize(index + 1, None);
84 names[index] = name;
85
86 let mut declarations = data.section_declarations.borrow_mut();
87 declarations.resize(index + 1, None);
88 declarations[index] = declared;
89
90 data.installing.borrow_mut().push(index);
91 index
92 }
93
94 pub fn section_name(&self, section: usize) -> Option<&'static str> {
96 self.data()?.section_name(section)
97 }
98
99 pub fn features(&self) -> Vec<Installed> {
101 let Some(data) = self.data() else {
102 return Vec::new();
103 };
104 let declarations = data.section_declarations.borrow();
105
106 data.section_names
107 .borrow()
108 .iter()
109 .enumerate()
110 .filter_map(|(section, name)| {
111 Some(Installed {
112 name: (*name)?,
113 declared: declarations.get(section).copied().flatten(),
114 })
115 })
116 .collect()
117 }
118
119 pub fn current_feature(&self) -> Option<&'static str> {
121 let data = self.data()?;
122 data.section_name(data.current_section())
123 }
124
125 pub fn note_listener(
128 &self,
129 event: &'static str,
130 actor: Option<&'static str>,
131 bus: crate::trace::Bus,
132 ) {
133 let Some(data) = self.data() else { return };
134 let feature = data.section_name(data.current_section());
135 data.listeners.borrow_mut().push(Listener {
136 event,
137 actor,
138 bus,
139 feature,
140 });
141 }
142
143 pub fn listeners(&self) -> Vec<Listener> {
144 self.data()
145 .map(|data| data.listeners.borrow().clone())
146 .unwrap_or_default()
147 }
148
149 pub fn close_section(&self) {
150 if let Some(data) = self.data() {
151 data.installing.borrow_mut().pop();
152 }
153 }
154
155 pub fn current_section(&self) -> usize {
157 self.data().map_or(0, |data| data.current_section())
158 }
159
160 pub fn section_of<R: 'static>(&self) -> usize {
163 self.data()
164 .and_then(|data| data.owners.borrow().get(&TypeId::of::<R>()).copied())
165 .unwrap_or(0)
166 }
167
168 pub fn has_feature<F: 'static>(&self) -> bool {
170 self.data()
171 .is_some_and(|data| data.installed_features.borrow().contains(&TypeId::of::<F>()))
172 }
173
174 pub fn note_export<R: 'static>(&self) {
181 let data = self.installing("exporting a reducer");
182 data.exports.borrow_mut().insert(TypeId::of::<R>());
183 }
184
185 pub fn exports<R: 'static>(&self) -> bool {
187 self.data()
188 .is_some_and(|data| data.exports.borrow().contains(&TypeId::of::<R>()))
189 }
190
191 pub fn answers<M: 'static>(&self, answer: impl Fn(M) + 'static) {
198 let data = self.installing("answering an action");
199 let answer: Rc<dyn Fn(M)> = Rc::new(answer);
200 let section = data.current_section();
201
202 let mut sections = data.sections.borrow_mut();
203 while sections.len() <= section {
204 sections.push(HashMap::new());
205 }
206 sections[section].insert(TypeId::of::<M>(), Rc::new(answer) as Rc<dyn Any>);
207 }
208
209 pub fn answerer<M: 'static>(&self, section: usize) -> Option<Rc<dyn Fn(M)>> {
211 self.data()?.answerer::<M>(section)
212 }
213
214 pub fn first_answerer<M: 'static>(&self) -> Option<Rc<dyn Fn(M)>> {
218 let data = self.data()?;
219 let sections = data.sections.borrow().len();
220 (0..sections).find_map(|section| data.answerer::<M>(section))
221 }
222}
223
224impl ScopeData {
225 fn answerer<M: 'static>(&self, section: usize) -> Option<Rc<dyn Fn(M)>> {
226 let sections = self.sections.borrow();
227 let answer = sections.get(section)?.get(&TypeId::of::<M>())?.clone();
228 answer.downcast::<Rc<dyn Fn(M)>>().ok().map(|a| (*a).clone())
229 }
230}