1use std::{
2 any::{Any, TypeId},
3 fmt,
4 hash::{Hash, Hasher},
5 rc::Rc,
6};
7
8use cranpose_core::collections::map::{HashMap, HashSet};
9use cranpose_foundation::{
10 DelegatableNode, InvalidationKind, ModifierInvalidation, ModifierInvalidations, ModifierNode,
11 ModifierNodeChain, ModifierNodeElement, NodeCapabilities, NodeState,
12};
13
14#[derive(Clone)]
15struct ModifierLocalId(Rc<()>);
16
17impl ModifierLocalId {
18 fn new() -> Self {
19 Self(Rc::new(()))
20 }
21}
22
23impl fmt::Debug for ModifierLocalId {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 f.debug_tuple("ModifierLocalId")
26 .field(&(Rc::as_ptr(&self.0) as usize))
27 .finish()
28 }
29}
30
31impl PartialEq for ModifierLocalId {
32 fn eq(&self, other: &Self) -> bool {
33 Rc::ptr_eq(&self.0, &other.0)
34 }
35}
36
37impl Eq for ModifierLocalId {}
38
39impl Hash for ModifierLocalId {
40 fn hash<H: Hasher>(&self, state: &mut H) {
41 Rc::as_ptr(&self.0).hash(state);
42 }
43}
44
45#[derive(Clone, Debug, PartialEq, Eq, Hash)]
46pub(crate) struct ModifierLocalToken {
47 id: ModifierLocalId,
48 type_id: TypeId,
49}
50
51impl ModifierLocalToken {
52 fn new(type_id: TypeId) -> Self {
53 let id = ModifierLocalId::new();
54 Self { id, type_id }
55 }
56
57 fn id(&self) -> ModifierLocalId {
58 self.id.clone()
59 }
60}
61
62#[derive(Clone)]
64pub struct ModifierLocalKey<T: 'static> {
65 token: ModifierLocalToken,
66 default: Rc<dyn Fn() -> T>,
67}
68
69impl<T: 'static> ModifierLocalKey<T> {
70 pub fn new(factory: impl Fn() -> T + 'static) -> Self {
73 Self {
74 token: ModifierLocalToken::new(TypeId::of::<T>()),
75 default: Rc::new(factory),
76 }
77 }
78
79 pub(crate) fn token(&self) -> ModifierLocalToken {
80 self.token.clone()
81 }
82
83 pub(crate) fn default_value(&self) -> T {
84 (self.default)()
85 }
86}
87
88impl<T: 'static> PartialEq for ModifierLocalKey<T> {
89 fn eq(&self, other: &Self) -> bool {
90 self.token == other.token
91 }
92}
93
94impl<T: 'static> Eq for ModifierLocalKey<T> {}
95
96impl<T: 'static> Hash for ModifierLocalKey<T> {
97 fn hash<H: Hasher>(&self, state: &mut H) {
98 self.token.hash(state);
99 }
100}
101
102pub struct ModifierLocalProviderNode {
103 token: ModifierLocalToken,
104 value_factory: Rc<dyn Fn() -> Box<dyn Any>>,
105 value: Rc<dyn Any>,
106 version: u64,
107 state: NodeState,
108}
109
110impl ModifierLocalProviderNode {
111 fn new(token: ModifierLocalToken, factory: Rc<dyn Fn() -> Box<dyn Any>>) -> Self {
112 Self {
113 token,
114 value: Self::create_value(&factory),
115 value_factory: factory,
116 version: 0,
117 state: NodeState::new(),
118 }
119 }
120
121 fn update_value(&mut self) {
122 self.value = Self::create_value(&self.value_factory);
123 self.version = self.version.wrapping_add(1);
124 }
125
126 fn set_factory(&mut self, factory: Rc<dyn Fn() -> Box<dyn Any>>) {
127 self.value_factory = factory;
128 self.update_value();
129 }
130
131 fn token(&self) -> ModifierLocalToken {
132 self.token.clone()
133 }
134
135 fn value(&self) -> Rc<dyn Any> {
136 self.value.clone()
137 }
138
139 fn version(&self) -> u64 {
140 self.version
141 }
142
143 fn create_value(factory: &Rc<dyn Fn() -> Box<dyn Any>>) -> Rc<dyn Any> {
144 Rc::from(factory())
145 }
146}
147
148impl DelegatableNode for ModifierLocalProviderNode {
149 fn node_state(&self) -> &NodeState {
150 &self.state
151 }
152}
153
154impl ModifierNode for ModifierLocalProviderNode {}
155
156pub struct ModifierLocalConsumerNode {
157 callback: Rc<dyn for<'a> Fn(&mut ModifierLocalReadScope<'a>)>,
158 state: NodeState,
159}
160
161impl ModifierLocalConsumerNode {
162 fn new(callback: Rc<dyn for<'a> Fn(&mut ModifierLocalReadScope<'a>)>) -> Self {
163 Self {
164 callback,
165 state: NodeState::new(),
166 }
167 }
168
169 fn notify(&self, scope: &mut ModifierLocalReadScope<'_>) {
170 (self.callback)(scope);
171 }
172
173 fn id(&self) -> usize {
174 self as *const Self as usize
175 }
176}
177
178#[derive(Clone)]
179pub(crate) struct ResolvedModifierLocal {
180 value: Rc<dyn Any>,
181 version: u64,
182 source: ModifierLocalSource,
183}
184
185impl ResolvedModifierLocal {
186 fn new(value: Rc<dyn Any>, version: u64, source: ModifierLocalSource) -> Self {
187 Self {
188 value,
189 version,
190 source,
191 }
192 }
193
194 pub(crate) fn value(&self) -> Rc<dyn Any> {
195 self.value.clone()
196 }
197
198 pub(crate) fn version(&self) -> u64 {
199 self.version
200 }
201
202 pub(crate) fn with_source(mut self, source: ModifierLocalSource) -> Self {
203 self.source = source;
204 self
205 }
206}
207
208#[derive(Clone, Copy, Debug, PartialEq, Eq)]
209pub(crate) enum ModifierLocalSource {
210 Chain,
211 Ancestor,
212}
213
214pub(crate) type ModifierLocalAncestorResolver<'a> =
215 dyn FnMut(&ModifierLocalToken) -> Option<ResolvedModifierLocal> + 'a;
216
217#[derive(Clone)]
218struct ProviderRecord {
219 value: Rc<dyn Any>,
220 version: u64,
221}
222
223impl ProviderRecord {
224 fn new(value: Rc<dyn Any>, version: u64) -> Self {
225 Self { value, version }
226 }
227
228 fn version(&self) -> u64 {
229 self.version
230 }
231
232 fn value(&self) -> &Rc<dyn Any> {
233 &self.value
234 }
235}
236
237#[derive(Clone)]
238struct DependencyRecord {
239 token: ModifierLocalToken,
240 source: DependencySource,
241 version: u64,
242}
243
244impl DependencyRecord {
245 fn from_chain(token: ModifierLocalToken, version: u64) -> Self {
246 Self {
247 token,
248 source: DependencySource::Chain,
249 version,
250 }
251 }
252
253 fn from_ancestor(token: ModifierLocalToken, version: u64) -> Self {
254 Self {
255 token,
256 source: DependencySource::Ancestor,
257 version,
258 }
259 }
260
261 fn from_default(token: ModifierLocalToken) -> Self {
262 Self {
263 token,
264 source: DependencySource::Default,
265 version: 0,
266 }
267 }
268
269 fn is_dirty(
270 &self,
271 providers: &HashMap<ModifierLocalId, ProviderRecord>,
272 ancestor_lookup: &mut ModifierLocalAncestorResolver<'_>,
273 ) -> bool {
274 match self.source {
275 DependencySource::Chain => match providers.get(&self.token.id()) {
276 Some(record) => record.version() != self.version,
277 None => true,
278 },
279 DependencySource::Ancestor => {
280 if providers.contains_key(&self.token.id()) {
281 return true;
282 }
283 ancestor_lookup(&self.token)
284 .is_none_or(|resolved| resolved.version() != self.version)
285 }
286 DependencySource::Default => {
287 providers.contains_key(&self.token.id()) || ancestor_lookup(&self.token).is_some()
288 }
289 }
290 }
291}
292
293#[derive(Clone, Copy)]
294enum DependencySource {
295 Chain,
296 Ancestor,
297 Default,
298}
299
300struct ConsumerState {
301 dependencies: Vec<DependencyRecord>,
302}
303
304impl ConsumerState {
305 fn new(dependencies: Vec<DependencyRecord>) -> Self {
306 Self { dependencies }
307 }
308
309 fn needs_update(
310 &self,
311 providers: &HashMap<ModifierLocalId, ProviderRecord>,
312 ancestor_lookup: &mut ModifierLocalAncestorResolver<'_>,
313 ) -> bool {
314 if self.dependencies.is_empty() {
315 return true;
316 }
317 self.dependencies
318 .iter()
319 .any(|dependency| dependency.is_dirty(providers, ancestor_lookup))
320 }
321}
322
323impl DelegatableNode for ModifierLocalConsumerNode {
324 fn node_state(&self) -> &NodeState {
325 &self.state
326 }
327}
328
329impl ModifierNode for ModifierLocalConsumerNode {}
330
331#[derive(Clone)]
332pub struct ModifierLocalProviderElement {
333 token: ModifierLocalToken,
334 factory: Rc<dyn Fn() -> Box<dyn Any>>,
335}
336
337impl ModifierLocalProviderElement {
338 pub fn new<T, F>(key: ModifierLocalKey<T>, factory: F) -> Self
339 where
340 T: 'static,
341 F: Fn() -> T + 'static,
342 {
343 let erased = Rc::new(move || -> Box<dyn Any> { Box::new(factory()) });
344 Self {
345 token: key.token(),
346 factory: erased,
347 }
348 }
349}
350
351impl fmt::Debug for ModifierLocalProviderElement {
352 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353 f.debug_struct("ModifierLocalProviderElement")
354 .field("id", &self.token.id())
355 .finish()
356 }
357}
358
359impl PartialEq for ModifierLocalProviderElement {
360 fn eq(&self, other: &Self) -> bool {
361 self.token == other.token
362 }
363}
364
365impl Eq for ModifierLocalProviderElement {}
366
367impl Hash for ModifierLocalProviderElement {
368 fn hash<H: Hasher>(&self, state: &mut H) {
369 "modifier_local_provider".hash(state);
370 self.token.hash(state);
371 }
372}
373
374impl ModifierNodeElement for ModifierLocalProviderElement {
375 type Node = ModifierLocalProviderNode;
376
377 fn create(&self) -> Self::Node {
378 ModifierLocalProviderNode::new(self.token.clone(), self.factory.clone())
379 }
380
381 fn update(&self, node: &mut Self::Node) {
382 node.set_factory(self.factory.clone());
383 }
384
385 fn capabilities(&self) -> NodeCapabilities {
386 NodeCapabilities::MODIFIER_LOCALS
387 }
388
389 fn always_update(&self) -> bool {
390 true
391 }
392}
393
394#[derive(Clone)]
395pub struct ModifierLocalConsumerElement {
396 callback: Rc<dyn for<'a> Fn(&mut ModifierLocalReadScope<'a>)>,
397}
398
399impl ModifierLocalConsumerElement {
400 pub fn new<F>(callback: F) -> Self
401 where
402 F: for<'a> Fn(&mut ModifierLocalReadScope<'a>) + 'static,
403 {
404 Self {
405 callback: Rc::new(callback),
406 }
407 }
408}
409
410impl fmt::Debug for ModifierLocalConsumerElement {
411 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
412 f.write_str("ModifierLocalConsumerElement")
413 }
414}
415
416impl PartialEq for ModifierLocalConsumerElement {
417 fn eq(&self, _other: &Self) -> bool {
418 true
419 }
420}
421
422impl Eq for ModifierLocalConsumerElement {}
423
424impl Hash for ModifierLocalConsumerElement {
425 fn hash<H: Hasher>(&self, state: &mut H) {
426 "modifier_local_consumer".hash(state);
427 }
428}
429
430impl ModifierNodeElement for ModifierLocalConsumerElement {
431 type Node = ModifierLocalConsumerNode;
432
433 fn create(&self) -> Self::Node {
434 ModifierLocalConsumerNode::new(self.callback.clone())
435 }
436
437 fn update(&self, node: &mut Self::Node) {
438 node.callback = self.callback.clone();
439 }
440
441 fn capabilities(&self) -> NodeCapabilities {
442 NodeCapabilities::MODIFIER_LOCALS
443 }
444
445 fn always_update(&self) -> bool {
446 true
447 }
448}
449
450pub struct ModifierLocalReadScope<'a> {
452 providers: &'a HashMap<ModifierLocalId, ProviderRecord>,
453 ancestor_lookup: &'a mut ModifierLocalAncestorResolver<'a>,
454 dependencies: &'a mut Vec<DependencyRecord>,
455 fallbacks: HashMap<ModifierLocalId, Rc<dyn Any>>,
456}
457
458impl<'a> ModifierLocalReadScope<'a> {
459 fn new(
460 providers: &'a HashMap<ModifierLocalId, ProviderRecord>,
461 ancestor_lookup: &'a mut ModifierLocalAncestorResolver<'a>,
462 dependencies: &'a mut Vec<DependencyRecord>,
463 ) -> Self {
464 Self {
465 providers,
466 ancestor_lookup,
467 dependencies,
468 fallbacks: HashMap::default(),
469 }
470 }
471
472 pub fn get<T: 'static>(&mut self, key: &ModifierLocalKey<T>) -> &T {
475 let token = key.token();
476 if let Some(record) = self.providers.get(&token.id()) {
477 self.dependencies
478 .push(DependencyRecord::from_chain(token, record.version()));
479 if let Some(value) = record.value().downcast_ref::<T>() {
480 return value;
481 }
482 return self.default_value_for(key);
483 }
484
485 if let Some(resolved) = (self.ancestor_lookup)(&token) {
486 self.dependencies.push(DependencyRecord::from_ancestor(
487 token.clone(),
488 resolved.version(),
489 ));
490 let resolved_value = resolved.value();
491 if resolved_value.downcast_ref::<T>().is_some() {
492 self.fallbacks.insert(token.id(), resolved_value);
493 return self.fallback_value_for(key);
494 }
495 return self.default_value_for(key);
496 }
497
498 self.dependencies
499 .push(DependencyRecord::from_default(token));
500 self.default_value_for(key)
501 }
502
503 fn fallback_value_for<T: 'static>(&mut self, key: &ModifierLocalKey<T>) -> &T {
504 let id = key.token().id();
505 if self
506 .fallbacks
507 .get(&id)
508 .and_then(|value| value.downcast_ref::<T>())
509 .is_none()
510 {
511 self.fallbacks
512 .insert(id.clone(), Rc::new(key.default_value()) as Rc<dyn Any>);
513 }
514
515 match self
516 .fallbacks
517 .get(&id)
518 .and_then(|value| value.downcast_ref::<T>())
519 {
520 Some(value) => value,
521 None => Box::leak(Box::new(key.default_value())),
522 }
523 }
524
525 fn default_value_for<T: 'static>(&mut self, key: &ModifierLocalKey<T>) -> &T {
526 self.fallback_value_for(key)
527 }
528}
529
530#[cfg(test)]
531#[path = "tests/local_tests.rs"]
532mod tests;
533
534#[derive(Default)]
535pub struct ModifierLocalManager {
536 providers: HashMap<ModifierLocalId, ProviderRecord>,
537 consumers: HashMap<usize, ConsumerState>,
538}
539
540impl ModifierLocalManager {
541 pub fn new() -> Self {
542 Self::default()
543 }
544
545 #[expect(private_interfaces)]
546 pub fn sync(
547 &mut self,
548 chain: &ModifierNodeChain,
549 ancestor_lookup: &mut ModifierLocalAncestorResolver<'_>,
550 ) -> ModifierInvalidations {
551 if !chain.has_capability(NodeCapabilities::MODIFIER_LOCALS) {
552 self.providers.clear();
553 self.consumers.clear();
554 return ModifierInvalidations::new();
555 }
556
557 let mut providers: HashMap<ModifierLocalId, ProviderRecord> = HashMap::default();
558 let mut seen_consumers: HashSet<usize> = HashSet::default();
559 let mut invalidations = ModifierInvalidations::new();
560
561 chain.for_each_node_with_capability(NodeCapabilities::MODIFIER_LOCALS, |_ref, node| {
562 if let Some(provider) = node.as_any().downcast_ref::<ModifierLocalProviderNode>() {
563 providers.insert(
564 provider.token().id(),
565 ProviderRecord::new(provider.value(), provider.version()),
566 );
567 return;
568 }
569
570 if let Some(consumer) = node.as_any().downcast_ref::<ModifierLocalConsumerNode>() {
571 let id = consumer.id();
572 seen_consumers.insert(id);
573 let needs_update = self
574 .consumers
575 .get(&id)
576 .is_none_or(|state| state.needs_update(&providers, ancestor_lookup));
577 if !needs_update {
578 return;
579 }
580
581 let mut dependencies = Vec::new();
582 {
583 let mut scope =
584 ModifierLocalReadScope::new(&providers, ancestor_lookup, &mut dependencies);
585 consumer.notify(&mut scope);
586 }
587 self.consumers.insert(id, ConsumerState::new(dependencies));
588 if !invalidations
589 .iter()
590 .any(|entry: &ModifierInvalidation| entry.kind() == InvalidationKind::Layout)
591 {
592 invalidations.push(ModifierInvalidation::new(
593 InvalidationKind::Layout,
594 NodeCapabilities::LAYOUT | NodeCapabilities::MODIFIER_LOCALS,
595 ));
596 }
597 }
598 });
599
600 self.providers = providers;
601 self.consumers.retain(|id, _| seen_consumers.contains(id));
602
603 invalidations
604 }
605
606 pub(crate) fn resolve(&self, token: &ModifierLocalToken) -> Option<ResolvedModifierLocal> {
607 self.providers.get(&token.id()).map(|record| {
608 ResolvedModifierLocal::new(
609 record.value().clone(),
610 record.version(),
611 ModifierLocalSource::Chain,
612 )
613 })
614 }
615}