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