1use std::{
2 any::Any,
3 cell::RefCell,
4 rc::{Rc, Weak},
5 sync::Arc,
6};
7
8use crate::{
9 Composer, LocalKey, RuntimeHandle, composer_context,
10 state::{MutationPolicy, OwnedMutableState},
11};
12
13fn provider_entry_source(key: &LocalKey, caller: crate::Key) -> crate::Key {
14 (key.entry_source() ^ caller).wrapping_mul(0x0000_0100_0000_01b3)
15}
16
17pub struct ProvidedValue {
20 key: LocalKey,
21 #[expect(clippy::type_complexity)]
22 apply: Box<dyn Fn(&Composer, crate::Key) -> (Rc<dyn Any>, bool)>,
23}
24
25impl ProvidedValue {
26 pub(crate) fn key(&self) -> &LocalKey {
27 &self.key
28 }
29
30 pub(crate) fn into_entry(
33 self,
34 composer: &Composer,
35 site: crate::Key,
36 ) -> (LocalKey, Rc<dyn Any>, bool) {
37 let ProvidedValue { key, apply } = self;
38 let (entry, static_changed) = apply(composer, site);
39 (key, entry, static_changed)
40 }
41}
42
43#[expect(non_snake_case)]
46#[track_caller]
47pub fn CompositionLocalProvider(
48 values: impl IntoIterator<Item = ProvidedValue>,
49 content: impl FnOnce(),
50) {
51 let site = crate::caller_location_key();
52 composer_context::with_composer(|composer| {
53 composer.with_composition_locals(values, site, |_composer| content());
54 });
55}
56
57pub(crate) struct LocalStateEntry<T: Clone + 'static> {
58 state: OwnedMutableState<T>,
59}
60
61type LocalEquivalentFn<T> = dyn Fn(&T, &T) -> bool + Send + Sync + 'static;
62
63struct LocalValuePolicy<T: Clone + 'static> {
64 equivalent: Arc<LocalEquivalentFn<T>>,
65}
66
67impl<T: Clone + 'static> MutationPolicy<T> for LocalValuePolicy<T> {
68 fn equivalent(&self, a: &T, b: &T) -> bool {
69 (self.equivalent)(a, b)
70 }
71}
72
73impl<T: Clone + 'static> LocalStateEntry<T> {
74 fn new(initial: T, runtime: RuntimeHandle, equivalent: Arc<LocalEquivalentFn<T>>) -> Self {
75 Self {
76 state: OwnedMutableState::with_runtime_and_policy(
77 initial,
78 runtime,
79 Rc::new(LocalValuePolicy { equivalent }),
80 ),
81 }
82 }
83
84 fn set(&self, value: T) {
85 self.state.replace(value);
86 }
87
88 pub(crate) fn value(&self) -> T {
89 self.state.value()
90 }
91}
92
93pub(crate) struct StaticLocalEntry<T: Clone + 'static> {
94 value: RefCell<T>,
95}
96
97impl<T: Clone + 'static> StaticLocalEntry<T> {
98 fn new(value: T) -> Self {
99 Self {
100 value: RefCell::new(value),
101 }
102 }
103
104 fn set(&self, value: T, equivalent: &LocalEquivalentFn<T>) -> bool {
106 let mut current = self.value.borrow_mut();
107 if equivalent(¤t, &value) {
108 return false;
109 }
110 *current = value;
111 true
112 }
113
114 pub(crate) fn value(&self) -> T {
115 self.value.borrow().clone()
116 }
117}
118
119type ComputeLocalFn<T> = dyn Fn(&CompositionLocalAccessorScope) -> T;
120
121pub(crate) struct ComputedLocalEntry<T: 'static> {
125 compute: OwnedMutableState<Rc<ComputeLocalFn<T>>>,
126}
127
128impl<T: 'static> ComputedLocalEntry<T> {
129 pub(crate) fn value(&self) -> T {
130 let compute = self.compute.value();
131 compute(&CompositionLocalAccessorScope { _private: () })
132 }
133}
134
135pub(crate) struct StaticComputedLocalEntry<T: 'static> {
139 compute: Rc<ComputeLocalFn<T>>,
140}
141
142impl<T: 'static> StaticComputedLocalEntry<T> {
143 pub(crate) fn value(&self) -> T {
144 (self.compute)(&CompositionLocalAccessorScope { _private: () })
145 }
146}
147
148pub struct CompositionLocalAccessorScope {
152 _private: (),
153}
154
155impl CompositionLocalAccessorScope {
156 pub fn current_value<T>(&self, local: &impl ReadableCompositionLocal<T>) -> T {
159 local.read_current()
160 }
161}
162
163pub trait ReadableCompositionLocal<T> {
166 fn read_current(&self) -> T;
168}
169
170impl<T: Clone + 'static> ReadableCompositionLocal<T> for CompositionLocal<T> {
171 fn read_current(&self) -> T {
172 self.current()
173 }
174}
175
176impl<T: Clone + 'static> ReadableCompositionLocal<T> for StaticCompositionLocal<T> {
177 fn read_current(&self) -> T {
178 self.current()
179 }
180}
181
182#[derive(Clone)]
186pub struct CompositionLocal<T: Clone + 'static> {
187 pub(crate) key: LocalKey,
188 default: Rc<dyn Fn() -> T>,
189 equivalent: Arc<LocalEquivalentFn<T>>,
190}
191
192impl<T: Clone + 'static> PartialEq for CompositionLocal<T> {
193 fn eq(&self, other: &Self) -> bool {
194 self.key == other.key
195 }
196}
197
198impl<T: Clone + 'static> Eq for CompositionLocal<T> {}
199
200impl<T: Clone + 'static> CompositionLocal<T> {
201 #[track_caller]
204 pub fn provides(&self, value: T) -> ProvidedValue {
205 let key = self.key.clone();
206 let entry_source = provider_entry_source(&key, crate::caller_location_key());
207 let equivalent = Arc::clone(&self.equivalent);
208 ProvidedValue {
209 key,
210 apply: Box::new(move |composer: &Composer, site: crate::Key| {
211 let runtime = composer.runtime_handle();
212 let source = (entry_source ^ site).wrapping_mul(0x0000_0100_0000_01b3);
213 let entry_ref = composer.remember_internal(source, || {
214 Rc::new(LocalStateEntry::new(
215 value.clone(),
216 runtime.clone(),
217 Arc::clone(&equivalent),
218 ))
219 });
220 entry_ref.update(|entry| entry.set(value.clone()));
221 entry_ref.with(|entry| (entry.clone() as Rc<dyn Any>, false))
222 }),
223 }
224 }
225
226 #[track_caller]
232 pub fn provides_computed(
233 &self,
234 compute: impl Fn(&CompositionLocalAccessorScope) -> T + 'static,
235 ) -> ProvidedValue {
236 let key = self.key.clone();
237 let entry_source = provider_entry_source(&key, crate::caller_location_key());
238 let compute: Rc<ComputeLocalFn<T>> = Rc::new(compute);
239 ProvidedValue {
240 key,
241 apply: Box::new(move |composer: &Composer, site: crate::Key| {
242 let runtime = composer.runtime_handle();
243 let source = (entry_source ^ site).wrapping_mul(0x0000_0100_0000_01b3);
244 let entry_ref = composer.remember_internal(source, || {
245 Rc::new(ComputedLocalEntry {
246 compute: OwnedMutableState::with_runtime_and_policy(
247 Rc::clone(&compute),
248 runtime.clone(),
249 Rc::new(LocalValuePolicy::<Rc<ComputeLocalFn<T>>> {
250 equivalent: Arc::new(Rc::ptr_eq),
251 }),
252 ),
253 })
254 });
255 entry_ref.update(|entry| entry.compute.replace(Rc::clone(&compute)));
256 entry_ref.with(|entry| (entry.clone() as Rc<dyn Any>, false))
257 }),
258 }
259 }
260
261 pub fn current(&self) -> T {
265 composer_context::with_composer(|composer| composer.read_composition_local(self))
266 }
267
268 pub fn default_value(&self) -> T {
270 (self.default)()
271 }
272}
273
274#[cfg(test)]
275fn malformed_provided_value_for_test(key: LocalKey, entry: Rc<dyn Any>) -> ProvidedValue {
276 ProvidedValue {
277 key,
278 apply: Box::new(move |_, _| (entry.clone(), false)),
279 }
280}
281
282#[cfg(test)]
283pub(crate) fn malformed_composition_local_for_test<T: Clone + 'static>(
284 local: &CompositionLocal<T>,
285 entry: Rc<dyn Any>,
286) -> ProvidedValue {
287 malformed_provided_value_for_test(local.key.clone(), entry)
288}
289
290#[expect(non_snake_case)]
293pub fn compositionLocalOf<T: Clone + PartialEq + 'static>(
294 default: impl Fn() -> T + 'static,
295) -> CompositionLocal<T> {
296 compositionLocalOfWithPolicy(default, |current, next| current == next)
297}
298
299#[expect(non_snake_case)]
302pub fn compositionLocalOfWithPolicy<T: Clone + 'static>(
303 default: impl Fn() -> T + 'static,
304 equivalent: impl Fn(&T, &T) -> bool + Send + Sync + 'static,
305) -> CompositionLocal<T> {
306 CompositionLocal {
307 key: LocalKey::new(),
308 default: Rc::new(default),
309 equivalent: Arc::new(equivalent),
310 }
311}
312
313#[derive(Clone)]
320pub struct StaticCompositionLocal<T: Clone + 'static> {
321 pub(crate) key: LocalKey,
322 default: Rc<dyn Fn() -> T>,
323 equivalent: Arc<LocalEquivalentFn<T>>,
324 last_read: Rc<RefCell<Option<StaticRead<T>>>>,
328}
329
330enum Provided<T: Clone + 'static> {
332 Value(Rc<StaticLocalEntry<T>>),
333 Computed(Rc<StaticComputedLocalEntry<T>>),
334 Default,
335}
336
337impl<T: Clone + 'static> Provided<T> {
338 fn downgrade(&self) -> StaticFound<T> {
339 match self {
340 Self::Value(entry) => StaticFound::Value(Rc::downgrade(entry)),
341 Self::Computed(entry) => StaticFound::Computed(Rc::downgrade(entry)),
342 Self::Default => StaticFound::Default,
343 }
344 }
345
346 fn read(self) -> Result<T, StaticDeferred<T>> {
349 match self {
350 Self::Value(entry) => Ok(entry.value()),
351 Self::Computed(entry) => Err(StaticDeferred::Computed(entry)),
352 Self::Default => Err(StaticDeferred::Default),
353 }
354 }
355}
356
357enum StaticFound<T: Clone + 'static> {
359 Value(Weak<StaticLocalEntry<T>>),
360 Computed(Weak<StaticComputedLocalEntry<T>>),
361 Default,
362}
363
364impl<T: Clone + 'static> StaticFound<T> {
365 fn upgrade(&self) -> Option<Provided<T>> {
366 match self {
367 Self::Value(entry) => entry.upgrade().map(Provided::Value),
368 Self::Computed(entry) => entry.upgrade().map(Provided::Computed),
369 Self::Default => Some(Provided::Default),
370 }
371 }
372}
373
374pub(crate) enum StaticDeferred<T: Clone + 'static> {
377 Computed(Rc<StaticComputedLocalEntry<T>>),
378 Default,
379}
380
381impl<T: Clone + 'static> StaticDeferred<T> {
382 pub(crate) fn value(self, local: &StaticCompositionLocal<T>) -> T {
383 match self {
384 Self::Computed(entry) => entry.value(),
385 Self::Default => local.default_value(),
386 }
387 }
388}
389
390struct StaticRead<T: Clone + 'static> {
393 frame: Option<Weak<crate::composer::LocalFrame>>,
394 found: StaticFound<T>,
395}
396
397fn is_top_frame(
400 frame: &Option<Weak<crate::composer::LocalFrame>>,
401 stack: &crate::LocalStackSnapshot,
402) -> bool {
403 match (frame, stack) {
404 (Some(frame), Some(top)) => std::ptr::eq(frame.as_ptr(), Rc::as_ptr(top)),
405 (None, None) => true,
406 _ => false,
407 }
408}
409
410impl<T: Clone + 'static> PartialEq for StaticCompositionLocal<T> {
411 fn eq(&self, other: &Self) -> bool {
412 self.key == other.key
413 }
414}
415
416impl<T: Clone + 'static> Eq for StaticCompositionLocal<T> {}
417
418impl<T: Clone + 'static> StaticCompositionLocal<T> {
419 pub(crate) fn provided_under(
423 &self,
424 stack: &crate::LocalStackSnapshot,
425 ) -> Result<T, StaticDeferred<T>> {
426 let mut last = self.last_read.borrow_mut();
427 if let Some(read) = &*last
428 && is_top_frame(&read.frame, stack)
429 && let Some(provided) = read.found.upgrade()
430 {
431 return provided.read();
432 }
433 let provided = self.find(stack);
434 *last = Some(StaticRead {
435 frame: stack.as_ref().map(Rc::downgrade),
436 found: provided.downgrade(),
437 });
438 provided.read()
439 }
440
441 fn find(&self, stack: &crate::LocalStackSnapshot) -> Provided<T> {
442 let Some(entry) = crate::composer::provided_entry(stack, &self.key) else {
443 return Provided::Default;
444 };
445 match entry.downcast::<StaticLocalEntry<T>>() {
446 Ok(entry) => Provided::Value(entry),
447 Err(entry) => match entry.downcast::<StaticComputedLocalEntry<T>>() {
448 Ok(entry) => Provided::Computed(entry),
449 Err(_) => {
450 log::error!(
451 "composition local entry type mismatch for key {:?}",
452 self.key
453 );
454 Provided::Default
455 }
456 },
457 }
458 }
459
460 #[track_caller]
463 pub fn provides(&self, value: T) -> ProvidedValue {
464 let key = self.key.clone();
465 let entry_source = provider_entry_source(&key, crate::caller_location_key());
466 let equivalent = Arc::clone(&self.equivalent);
467 ProvidedValue {
468 key,
469 apply: Box::new(move |composer: &Composer, site: crate::Key| {
470 let source = (entry_source ^ site).wrapping_mul(0x0000_0100_0000_01b3);
471 let entry_ref = composer
472 .remember_internal(source, || Rc::new(StaticLocalEntry::new(value.clone())));
473 let changed = entry_ref.update(|entry| entry.set(value.clone(), &*equivalent));
474 entry_ref.with(|entry| (entry.clone() as Rc<dyn Any>, changed))
475 }),
476 }
477 }
478
479 #[track_caller]
484 pub fn provides_computed(
485 &self,
486 compute: impl Fn(&CompositionLocalAccessorScope) -> T + 'static,
487 ) -> ProvidedValue {
488 let compute: Rc<ComputeLocalFn<T>> = Rc::new(compute);
489 ProvidedValue {
490 key: self.key.clone(),
491 apply: Box::new(move |_: &Composer, _: crate::Key| {
492 let entry = Rc::new(StaticComputedLocalEntry {
493 compute: Rc::clone(&compute),
494 });
495 (entry as Rc<dyn Any>, true)
496 }),
497 }
498 }
499
500 pub fn current(&self) -> T {
503 composer_context::with_composer(|composer| composer.read_static_composition_local(self))
504 }
505
506 pub fn default_value(&self) -> T {
508 (self.default)()
509 }
510}
511
512#[cfg(test)]
513pub(crate) fn malformed_static_composition_local_for_test<T: Clone + 'static>(
514 local: &StaticCompositionLocal<T>,
515 entry: Rc<dyn Any>,
516) -> ProvidedValue {
517 malformed_provided_value_for_test(local.key.clone(), entry)
518}
519
520#[expect(non_snake_case)]
522pub fn staticCompositionLocalOf<T: Clone + PartialEq + 'static>(
523 default: impl Fn() -> T + 'static,
524) -> StaticCompositionLocal<T> {
525 staticCompositionLocalOfWithPolicy(default, |current, next| current == next)
526}
527
528#[expect(non_snake_case)]
531pub fn staticCompositionLocalOfWithPolicy<T: Clone + 'static>(
532 default: impl Fn() -> T + 'static,
533 equivalent: impl Fn(&T, &T) -> bool + Send + Sync + 'static,
534) -> StaticCompositionLocal<T> {
535 StaticCompositionLocal {
536 key: LocalKey::new(),
537 default: Rc::new(default),
538 equivalent: Arc::new(equivalent),
539 last_read: Rc::default(),
540 }
541}
542
543#[expect(non_snake_case)]
549pub fn compositionLocalWithComputedDefaultOf<T: Clone + PartialEq + 'static>(
550 compute: impl Fn(&CompositionLocalAccessorScope) -> T + 'static,
551) -> CompositionLocal<T> {
552 compositionLocalOf(move || compute(&CompositionLocalAccessorScope { _private: () }))
553}
554
555#[expect(non_snake_case)]
559pub fn staticCompositionLocalWithComputedDefaultOf<T: Clone + PartialEq + 'static>(
560 compute: impl Fn(&CompositionLocalAccessorScope) -> T + 'static,
561) -> StaticCompositionLocal<T> {
562 staticCompositionLocalOf(move || compute(&CompositionLocalAccessorScope { _private: () }))
563}