1use std::{
8 any::{Any, TypeId},
9 cell::{Cell, RefCell},
10 collections::{HashMap, HashSet},
11 fmt,
12 rc::{Rc, Weak},
13 sync::atomic::{AtomicU64, Ordering},
14};
15
16static NEXT_CONSUMER: AtomicU64 = AtomicU64::new(1);
17static NEXT_ENVIRONMENT: AtomicU64 = AtomicU64::new(1);
18use crate::reactivity::{DependencySource, Subscription, TrackingGuard};
19
20fn next_identity(counter: &AtomicU64, label: &'static str) -> u64 {
21 counter
22 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| id.checked_add(1))
23 .unwrap_or_else(|_| panic!("{label} identity space exhausted"))
24}
25
26fn next_revision(revision: u64) -> u64 {
27 revision
28 .checked_add(1)
29 .expect("context dependency revision exhausted")
30}
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
33enum DependencyKey {
34 Environment { environment: u64, type_id: TypeId },
35}
36
37#[derive(Default)]
38struct TrackerInner {
39 dependencies: RefCell<HashMap<ConsumerId, HashMap<u64, Subscription>>>,
40 sources: RefCell<HashMap<DependencyKey, DependencySource>>,
41 source_keys: RefCell<HashMap<u64, DependencyKey>>,
44 dirty: RefCell<HashSet<ConsumerId>>,
45 owners: RefCell<HashMap<ConsumerId, Weak<ConsumerOwner>>>,
46}
47impl TrackerInner {
48 fn owner(self: &Rc<Self>, consumer: ConsumerId) -> Rc<ConsumerOwner> {
49 let mut owners = self.owners.borrow_mut();
50 if let Some(owner) = owners.get(&consumer).and_then(Weak::upgrade) {
51 return owner;
52 }
53 let owner = Rc::new(ConsumerOwner {
54 tracker: Rc::downgrade(self),
55 consumer,
56 });
57 owners.insert(consumer, Rc::downgrade(&owner));
58 owner
59 }
60
61 fn record(self: &Rc<Self>, consumer: ConsumerId, dependency: DependencyKey) {
62 let source = self
63 .sources
64 .borrow_mut()
65 .entry(dependency)
66 .or_insert_with(|| {
67 let source = DependencySource::default();
68 self.source_keys
69 .borrow_mut()
70 .insert(source.id(), dependency);
71 source
72 })
73 .clone();
74 self.record_source(consumer, source);
75 }
76 fn record_source(self: &Rc<Self>, consumer: ConsumerId, source: DependencySource) {
77 let mut dependencies = self.dependencies.borrow_mut();
78 dependencies
79 .entry(consumer)
80 .or_default()
81 .entry(source.id())
82 .or_insert_with(|| {
83 let weak = Rc::downgrade(self);
84 source.subscribe(consumer, move || {
85 if let Some(tracker) = weak.upgrade() {
86 tracker.dirty.borrow_mut().insert(consumer);
87 }
88 })
89 });
90 }
91 fn clear(&self, consumer: ConsumerId) {
92 self.dirty.borrow_mut().remove(&consumer);
93 let Some(subscriptions) = self.dependencies.borrow_mut().remove(&consumer) else {
94 return;
95 };
96 let ids: Vec<u64> = subscriptions.keys().copied().collect();
97 drop(subscriptions);
100 let mut sources = self.sources.borrow_mut();
101 let mut keys = self.source_keys.borrow_mut();
102 for id in ids {
103 if let Some(key) = keys.get(&id).copied()
104 && sources
105 .get(&key)
106 .is_some_and(|source| source.subscriber_count() == 0)
107 {
108 sources.remove(&key);
109 keys.remove(&id);
110 }
111 }
112 }
113 fn invalidate(&self, dependency: DependencyKey) {
114 let source = self.sources.borrow().get(&dependency).cloned();
115 if let Some(source) = source {
116 source.notify();
117 }
118 }
119 fn dependency_count(&self, consumer: ConsumerId) -> usize {
120 self.dependencies
121 .borrow()
122 .get(&consumer)
123 .map_or(0, HashMap::len)
124 }
125 fn is_dirty(&self, consumer: ConsumerId) -> bool {
126 self.dirty.borrow().contains(&consumer)
127 }
128 fn take_dirty(&self, consumer: ConsumerId) -> bool {
129 self.dirty.borrow_mut().remove(&consumer)
130 }
131 fn take_all_dirty(&self) -> Vec<ConsumerId> {
132 self.dirty.borrow_mut().drain().collect()
133 }
134}
135
136struct ConsumerOwner {
137 tracker: Weak<TrackerInner>,
138 consumer: ConsumerId,
139}
140impl Drop for ConsumerOwner {
141 fn drop(&mut self) {
142 if let Some(tracker) = self.tracker.upgrade() {
143 tracker.clear(self.consumer);
144 tracker.owners.borrow_mut().remove(&self.consumer);
145 }
146 }
147}
148
149struct Environment {
150 id: u64,
151 parent: Option<Rc<Environment>>,
152 values: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
153 revision: Cell<u64>,
154}
155
156impl Environment {
157 fn root() -> Rc<Self> {
158 Rc::new(Self {
159 id: next_identity(&NEXT_ENVIRONMENT, "environment"),
160 parent: None,
161 values: RefCell::new(HashMap::new()),
162 revision: Cell::new(0),
163 })
164 }
165
166 fn child(parent: &Rc<Self>) -> Rc<Self> {
167 Rc::new(Self {
168 id: next_identity(&NEXT_ENVIRONMENT, "environment"),
169 parent: Some(parent.clone()),
170 values: RefCell::new(HashMap::new()),
171 revision: Cell::new(0),
172 })
173 }
174
175 fn lookup(&self, type_id: TypeId) -> Option<(u64, Rc<dyn Any>)> {
176 if let Some(value) = self.values.borrow().get(&type_id) {
177 return Some((self.id, value.clone()));
178 }
179 self.parent.as_deref()?.lookup(type_id)
180 }
181}
182
183#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
184pub struct ConsumerId(u64);
185
186impl ConsumerId {
187 #[must_use]
188 pub fn new() -> Self {
189 Self(next_identity(&NEXT_CONSUMER, "context consumer"))
190 }
191
192 #[must_use]
193 pub const fn from_raw(value: u64) -> Self {
194 Self(value)
195 }
196
197 #[must_use]
198 pub const fn raw(self) -> u64 {
199 self.0
200 }
201}
202
203impl Default for ConsumerId {
204 fn default() -> Self {
205 Self::new()
206 }
207}
208
209#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
211pub struct DependencySnapshot {
212 count: usize,
213 dirty: bool,
214}
215
216impl DependencySnapshot {
217 #[must_use]
218 pub const fn count(self) -> usize {
219 self.count
220 }
221
222 #[must_use]
223 pub const fn is_empty(self) -> bool {
224 self.count == 0
225 }
226
227 #[must_use]
228 pub const fn is_dirty(self) -> bool {
229 self.dirty
230 }
231}
232
233struct ActiveScope {
234 tracker: Rc<TrackerInner>,
235 consumer: ConsumerId,
236}
237
238thread_local! {
239 static ACTIVE_SCOPES: RefCell<Vec<ActiveScope>> = const { RefCell::new(Vec::new()) };
240}
241
242#[derive(Clone)]
245pub struct BuildContext {
246 _owner: Rc<ConsumerOwner>,
247 environment: Rc<Environment>,
248 tracker: Rc<TrackerInner>,
249 consumer: ConsumerId,
250}
251
252impl fmt::Debug for BuildContext {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 f.debug_struct("BuildContext")
255 .field("consumer", &self.consumer)
256 .field("dependency_count", &self.dependency_count())
257 .field("dirty", &self.is_dirty())
258 .finish()
259 }
260}
261
262impl Default for BuildContext {
263 fn default() -> Self {
264 Self::new()
265 }
266}
267
268impl BuildContext {
269 #[must_use]
271 pub fn new() -> Self {
272 Self::for_consumer(ConsumerId::new())
273 }
274
275 #[must_use]
277 pub fn for_consumer(consumer: ConsumerId) -> Self {
278 let tracker = Rc::new(TrackerInner::default());
279 Self {
280 _owner: tracker.owner(consumer),
281 environment: Environment::root(),
282 tracker,
283 consumer,
284 }
285 }
286
287 #[must_use]
290 pub fn for_related_consumer(&self, consumer: ConsumerId) -> Self {
291 Self {
292 _owner: self.tracker.owner(consumer),
293 environment: self.environment.clone(),
294 tracker: self.tracker.clone(),
295 consumer,
296 }
297 }
298
299 #[must_use]
303 pub fn boundary_for_consumer(&self, consumer: ConsumerId) -> Self {
304 Self {
305 _owner: self.tracker.owner(consumer),
306 environment: Environment::root(),
307 tracker: self.tracker.clone(),
308 consumer,
309 }
310 }
311
312 #[must_use]
314 pub const fn consumer_id(&self) -> ConsumerId {
315 self.consumer
316 }
317
318 #[must_use]
321 pub fn child(&self) -> Self {
322 Self {
323 _owner: self._owner.clone(),
324 environment: Environment::child(&self.environment),
325 tracker: self.tracker.clone(),
326 consumer: self.consumer,
327 }
328 }
329
330 #[must_use]
332 pub fn provide<T: Any>(&self, value: T) -> Self {
333 let child = self.child();
334 child.insert(value);
335 child
336 }
337
338 #[doc(hidden)]
342 #[must_use]
343 pub fn provide_erased(&self, type_id: TypeId, value: Rc<dyn Any>) -> Self {
344 let child = self.child();
345 child.environment.values.borrow_mut().insert(type_id, value);
346 child
347 }
348
349 #[doc(hidden)]
352 pub fn set_erased(&self, type_id: TypeId, value: Rc<dyn Any>) {
353 self.environment.values.borrow_mut().insert(type_id, value);
354 self.environment
355 .revision
356 .set(next_revision(self.environment.revision.get()));
357 self.tracker.invalidate(DependencyKey::Environment {
358 environment: self.environment.id,
359 type_id,
360 });
361 }
362
363 pub fn insert<T: Any>(&self, value: T) -> bool {
366 let type_id = TypeId::of::<T>();
367 self.environment
368 .values
369 .borrow_mut()
370 .insert(type_id, Rc::new(value));
371 self.environment
372 .revision
373 .set(next_revision(self.environment.revision.get()));
374 self.tracker.invalidate(DependencyKey::Environment {
375 environment: self.environment.id,
376 type_id,
377 });
378 true
379 }
380
381 pub fn set<T: Any + PartialEq>(&self, value: T) -> bool {
383 let type_id = TypeId::of::<T>();
384 let changed = self
385 .environment
386 .values
387 .borrow()
388 .get(&type_id)
389 .and_then(|old| old.downcast_ref::<T>())
390 .is_none_or(|old| old != &value);
391 if changed {
392 self.insert(value);
393 }
394 changed
395 }
396
397 pub fn remove<T: Any>(&self) -> bool {
399 let type_id = TypeId::of::<T>();
400 let removed = self
401 .environment
402 .values
403 .borrow_mut()
404 .remove(&type_id)
405 .is_some();
406 if removed {
407 self.environment
408 .revision
409 .set(next_revision(self.environment.revision.get()));
410 self.tracker.invalidate(DependencyKey::Environment {
411 environment: self.environment.id,
412 type_id,
413 });
414 }
415 removed
416 }
417
418 #[must_use]
419 pub fn contains<T: Any>(&self) -> bool {
420 self.lookup_value(TypeId::of::<T>()).is_some()
421 }
422
423 #[must_use]
424 pub fn revision(&self) -> u64 {
425 self.environment.revision.get()
426 }
427
428 fn lookup_value(&self, type_id: TypeId) -> Option<(u64, Rc<dyn Any>)> {
429 self.environment.lookup(type_id)
430 }
431
432 #[must_use]
434 pub fn read<T: Any + Clone>(&self) -> Option<T> {
435 self.read_shared::<T>().map(|value| (*value).clone())
436 }
437
438 #[must_use]
440 pub fn get<T: Any + Clone>(&self) -> Option<T> {
441 self.read()
442 }
443
444 #[must_use]
447 pub fn read_shared<T: Any>(&self) -> Option<Rc<T>> {
448 self.lookup_value(TypeId::of::<T>())
449 .and_then(|(_, value)| value.downcast::<T>().ok())
450 }
451
452 fn record_environment(&self, type_id: TypeId) {
453 let environment = self
454 .lookup_value(type_id)
455 .map_or(self.environment.id, |(environment, _)| environment);
456 with_active_scope(|scope| {
457 scope.tracker.record(
458 scope.consumer,
459 DependencyKey::Environment {
460 environment,
461 type_id,
462 },
463 );
464 })
465 .unwrap_or_else(|| {
466 self.tracker.record(
467 self.consumer,
468 DependencyKey::Environment {
469 environment,
470 type_id,
471 },
472 )
473 });
474 }
475
476 #[must_use]
478 pub fn depend<T: Any + Clone>(&self) -> Option<T> {
479 self.record_environment(TypeId::of::<T>());
480 self.read()
481 }
482
483 #[must_use]
486 pub fn depend_shared<T: Any>(&self) -> Option<Rc<T>> {
487 self.record_environment(TypeId::of::<T>());
488 self.read_shared()
489 }
490
491 #[must_use]
493 pub fn watch<T: Any + Clone>(&self) -> Option<T> {
494 self.depend()
495 }
496
497 #[must_use]
500 pub fn enter(&self) -> ContextGuard {
501 ACTIVE_SCOPES.with(|scopes| {
502 scopes.borrow_mut().push(ActiveScope {
503 tracker: self.tracker.clone(),
504 consumer: self.consumer,
505 });
506 });
507 let tracker = self.tracker.clone();
508 let consumer = self.consumer;
509 ContextGuard {
510 active: true,
511 _owner: self._owner.clone(),
512 _read_only: crate::reactivity::ReadOnlyGuard::enter(),
513 _tracking: TrackingGuard::enter(move |source| tracker.record_source(consumer, source)),
514 }
515 }
516
517 #[must_use]
519 pub fn scope(&self) -> ContextGuard {
520 self.enter()
521 }
522
523 pub fn build<R>(&self, callback: impl FnOnce(&BuildContext) -> R) -> R {
526 self.clear_dependencies();
527 let _guard = self.enter();
528 callback(self)
529 }
530
531 pub fn run<R>(&self, callback: impl FnOnce() -> R) -> R {
534 self.clear_dependencies();
535 let _guard = self.enter();
536 callback()
537 }
538
539 pub fn with_context<R>(&self, callback: impl FnOnce(&BuildContext) -> R) -> R {
541 self.build(callback)
542 }
543
544 pub fn clear_dependencies(&self) {
545 self.tracker.clear(self.consumer);
546 }
547
548 #[must_use]
549 pub fn dependency_count(&self) -> usize {
550 self.tracker.dependency_count(self.consumer)
551 }
552
553 #[must_use]
554 pub fn is_dirty(&self) -> bool {
555 self.tracker.is_dirty(self.consumer)
556 }
557
558 pub fn take_dirty(&self) -> bool {
560 self.tracker.take_dirty(self.consumer)
561 }
562
563 #[doc(hidden)]
567 pub fn take_dirty_consumers(&self) -> Vec<ConsumerId> {
568 self.tracker.take_all_dirty()
569 }
570
571 #[doc(hidden)]
575 #[must_use]
576 pub fn has_dirty_consumers(&self) -> bool {
577 !self.tracker.dirty.borrow().is_empty()
578 }
579
580 #[must_use]
581 pub fn dependencies(&self) -> DependencySnapshot {
582 DependencySnapshot {
583 count: self.dependency_count(),
584 dirty: self.is_dirty(),
585 }
586 }
587}
588
589pub struct ContextGuard {
591 active: bool,
592 _owner: Rc<ConsumerOwner>,
593 _tracking: TrackingGuard,
594 _read_only: crate::reactivity::ReadOnlyGuard,
595}
596
597impl Drop for ContextGuard {
598 fn drop(&mut self) {
599 if self.active {
600 ACTIVE_SCOPES.with(|scopes| {
601 let _ = scopes.borrow_mut().pop();
602 });
603 self.active = false;
604 }
605 }
606}
607
608fn with_active_scope<R>(callback: impl FnOnce(&ActiveScope) -> R) -> Option<R> {
609 ACTIVE_SCOPES.with(|scopes| scopes.borrow().last().map(callback))
610}