1use std::{any::Any, cell::RefCell, rc::Rc, sync::Arc};
2
3use crate::{
4 Composer, LocalKey, RuntimeHandle, composer_context,
5 state::{MutationPolicy, OwnedMutableState},
6};
7
8fn provider_entry_source(key: &LocalKey, caller: crate::Key) -> crate::Key {
9 (key.entry_source() ^ caller).wrapping_mul(0x0000_0100_0000_01b3)
10}
11
12pub struct ProvidedValue {
15 key: LocalKey,
16 #[expect(clippy::type_complexity)]
17 apply: Box<dyn Fn(&Composer, crate::Key) -> (Rc<dyn Any>, bool)>,
18}
19
20impl ProvidedValue {
21 pub(crate) fn key(&self) -> &LocalKey {
22 &self.key
23 }
24
25 pub(crate) fn into_entry(
28 self,
29 composer: &Composer,
30 site: crate::Key,
31 ) -> (LocalKey, Rc<dyn Any>, bool) {
32 let ProvidedValue { key, apply } = self;
33 let (entry, static_changed) = apply(composer, site);
34 (key, entry, static_changed)
35 }
36}
37
38#[expect(non_snake_case)]
41#[track_caller]
42pub fn CompositionLocalProvider(
43 values: impl IntoIterator<Item = ProvidedValue>,
44 content: impl FnOnce(),
45) {
46 let site = crate::caller_location_key();
47 composer_context::with_composer(|composer| {
48 composer.with_composition_locals(values, site, |_composer| content());
49 });
50}
51
52pub(crate) struct LocalStateEntry<T: Clone + 'static> {
53 state: OwnedMutableState<T>,
54}
55
56type LocalEquivalentFn<T> = dyn Fn(&T, &T) -> bool + Send + Sync + 'static;
57
58struct LocalValuePolicy<T: Clone + 'static> {
59 equivalent: Arc<LocalEquivalentFn<T>>,
60}
61
62impl<T: Clone + 'static> MutationPolicy<T> for LocalValuePolicy<T> {
63 fn equivalent(&self, a: &T, b: &T) -> bool {
64 (self.equivalent)(a, b)
65 }
66}
67
68impl<T: Clone + 'static> LocalStateEntry<T> {
69 fn new(initial: T, runtime: RuntimeHandle, equivalent: Arc<LocalEquivalentFn<T>>) -> Self {
70 Self {
71 state: OwnedMutableState::with_runtime_and_policy(
72 initial,
73 runtime,
74 Rc::new(LocalValuePolicy { equivalent }),
75 ),
76 }
77 }
78
79 fn set(&self, value: T) {
80 self.state.replace(value);
81 }
82
83 pub(crate) fn value(&self) -> T {
84 self.state.value()
85 }
86}
87
88pub(crate) struct StaticLocalEntry<T: Clone + 'static> {
89 value: RefCell<T>,
90}
91
92impl<T: Clone + 'static> StaticLocalEntry<T> {
93 fn new(value: T) -> Self {
94 Self {
95 value: RefCell::new(value),
96 }
97 }
98
99 fn set(&self, value: T, equivalent: &LocalEquivalentFn<T>) -> bool {
101 let mut current = self.value.borrow_mut();
102 if equivalent(¤t, &value) {
103 return false;
104 }
105 *current = value;
106 true
107 }
108
109 pub(crate) fn value(&self) -> T {
110 self.value.borrow().clone()
111 }
112}
113
114type ComputeLocalFn<T> = dyn Fn(&CompositionLocalAccessorScope) -> T;
115
116pub(crate) struct ComputedLocalEntry<T: 'static> {
120 compute: OwnedMutableState<Rc<ComputeLocalFn<T>>>,
121}
122
123impl<T: 'static> ComputedLocalEntry<T> {
124 pub(crate) fn value(&self) -> T {
125 let compute = self.compute.value();
126 compute(&CompositionLocalAccessorScope { _private: () })
127 }
128}
129
130pub(crate) struct StaticComputedLocalEntry<T: 'static> {
134 compute: Rc<ComputeLocalFn<T>>,
135}
136
137impl<T: 'static> StaticComputedLocalEntry<T> {
138 pub(crate) fn value(&self) -> T {
139 (self.compute)(&CompositionLocalAccessorScope { _private: () })
140 }
141}
142
143pub struct CompositionLocalAccessorScope {
147 _private: (),
148}
149
150impl CompositionLocalAccessorScope {
151 pub fn current_value<T>(&self, local: &impl ReadableCompositionLocal<T>) -> T {
154 local.read_current()
155 }
156}
157
158pub trait ReadableCompositionLocal<T> {
161 fn read_current(&self) -> T;
163}
164
165impl<T: Clone + 'static> ReadableCompositionLocal<T> for CompositionLocal<T> {
166 fn read_current(&self) -> T {
167 self.current()
168 }
169}
170
171impl<T: Clone + 'static> ReadableCompositionLocal<T> for StaticCompositionLocal<T> {
172 fn read_current(&self) -> T {
173 self.current()
174 }
175}
176
177#[derive(Clone)]
181pub struct CompositionLocal<T: Clone + 'static> {
182 pub(crate) key: LocalKey,
183 default: Rc<dyn Fn() -> T>,
184 equivalent: Arc<LocalEquivalentFn<T>>,
185}
186
187impl<T: Clone + 'static> PartialEq for CompositionLocal<T> {
188 fn eq(&self, other: &Self) -> bool {
189 self.key == other.key
190 }
191}
192
193impl<T: Clone + 'static> Eq for CompositionLocal<T> {}
194
195impl<T: Clone + 'static> CompositionLocal<T> {
196 #[track_caller]
199 pub fn provides(&self, value: T) -> ProvidedValue {
200 let key = self.key.clone();
201 let entry_source = provider_entry_source(&key, crate::caller_location_key());
202 let equivalent = Arc::clone(&self.equivalent);
203 ProvidedValue {
204 key,
205 apply: Box::new(move |composer: &Composer, site: crate::Key| {
206 let runtime = composer.runtime_handle();
207 let source = (entry_source ^ site).wrapping_mul(0x0000_0100_0000_01b3);
208 let entry_ref = composer.remember_internal(source, || {
209 Rc::new(LocalStateEntry::new(
210 value.clone(),
211 runtime.clone(),
212 Arc::clone(&equivalent),
213 ))
214 });
215 entry_ref.update(|entry| entry.set(value.clone()));
216 entry_ref.with(|entry| (entry.clone() as Rc<dyn Any>, false))
217 }),
218 }
219 }
220
221 #[track_caller]
227 pub fn provides_computed(
228 &self,
229 compute: impl Fn(&CompositionLocalAccessorScope) -> T + 'static,
230 ) -> ProvidedValue {
231 let key = self.key.clone();
232 let entry_source = provider_entry_source(&key, crate::caller_location_key());
233 let compute: Rc<ComputeLocalFn<T>> = Rc::new(compute);
234 ProvidedValue {
235 key,
236 apply: Box::new(move |composer: &Composer, site: crate::Key| {
237 let runtime = composer.runtime_handle();
238 let source = (entry_source ^ site).wrapping_mul(0x0000_0100_0000_01b3);
239 let entry_ref = composer.remember_internal(source, || {
240 Rc::new(ComputedLocalEntry {
241 compute: OwnedMutableState::with_runtime_and_policy(
242 Rc::clone(&compute),
243 runtime.clone(),
244 Rc::new(LocalValuePolicy::<Rc<ComputeLocalFn<T>>> {
245 equivalent: Arc::new(Rc::ptr_eq),
246 }),
247 ),
248 })
249 });
250 entry_ref.update(|entry| entry.compute.replace(Rc::clone(&compute)));
251 entry_ref.with(|entry| (entry.clone() as Rc<dyn Any>, false))
252 }),
253 }
254 }
255
256 pub fn current(&self) -> T {
260 composer_context::with_composer(|composer| composer.read_composition_local(self))
261 }
262
263 pub fn default_value(&self) -> T {
265 (self.default)()
266 }
267}
268
269#[cfg(test)]
270fn malformed_provided_value_for_test(key: LocalKey, entry: Rc<dyn Any>) -> ProvidedValue {
271 ProvidedValue {
272 key,
273 apply: Box::new(move |_, _| (entry.clone(), false)),
274 }
275}
276
277#[cfg(test)]
278pub(crate) fn malformed_composition_local_for_test<T: Clone + 'static>(
279 local: &CompositionLocal<T>,
280 entry: Rc<dyn Any>,
281) -> ProvidedValue {
282 malformed_provided_value_for_test(local.key.clone(), entry)
283}
284
285#[expect(non_snake_case)]
288pub fn compositionLocalOf<T: Clone + PartialEq + 'static>(
289 default: impl Fn() -> T + 'static,
290) -> CompositionLocal<T> {
291 compositionLocalOfWithPolicy(default, |current, next| current == next)
292}
293
294#[expect(non_snake_case)]
297pub fn compositionLocalOfWithPolicy<T: Clone + 'static>(
298 default: impl Fn() -> T + 'static,
299 equivalent: impl Fn(&T, &T) -> bool + Send + Sync + 'static,
300) -> CompositionLocal<T> {
301 CompositionLocal {
302 key: LocalKey::new(),
303 default: Rc::new(default),
304 equivalent: Arc::new(equivalent),
305 }
306}
307
308#[derive(Clone)]
315pub struct StaticCompositionLocal<T: Clone + 'static> {
316 pub(crate) key: LocalKey,
317 default: Rc<dyn Fn() -> T>,
318 equivalent: Arc<LocalEquivalentFn<T>>,
319}
320
321impl<T: Clone + 'static> PartialEq for StaticCompositionLocal<T> {
322 fn eq(&self, other: &Self) -> bool {
323 self.key == other.key
324 }
325}
326
327impl<T: Clone + 'static> Eq for StaticCompositionLocal<T> {}
328
329impl<T: Clone + 'static> StaticCompositionLocal<T> {
330 #[track_caller]
333 pub fn provides(&self, value: T) -> ProvidedValue {
334 let key = self.key.clone();
335 let entry_source = provider_entry_source(&key, crate::caller_location_key());
336 let equivalent = Arc::clone(&self.equivalent);
337 ProvidedValue {
338 key,
339 apply: Box::new(move |composer: &Composer, site: crate::Key| {
340 let source = (entry_source ^ site).wrapping_mul(0x0000_0100_0000_01b3);
341 let entry_ref = composer
342 .remember_internal(source, || Rc::new(StaticLocalEntry::new(value.clone())));
343 let changed = entry_ref.update(|entry| entry.set(value.clone(), &*equivalent));
344 entry_ref.with(|entry| (entry.clone() as Rc<dyn Any>, changed))
345 }),
346 }
347 }
348
349 #[track_caller]
354 pub fn provides_computed(
355 &self,
356 compute: impl Fn(&CompositionLocalAccessorScope) -> T + 'static,
357 ) -> ProvidedValue {
358 let compute: Rc<ComputeLocalFn<T>> = Rc::new(compute);
359 ProvidedValue {
360 key: self.key.clone(),
361 apply: Box::new(move |_: &Composer, _: crate::Key| {
362 let entry = Rc::new(StaticComputedLocalEntry {
363 compute: Rc::clone(&compute),
364 });
365 (entry as Rc<dyn Any>, true)
366 }),
367 }
368 }
369
370 pub fn current(&self) -> T {
373 composer_context::with_composer(|composer| composer.read_static_composition_local(self))
374 }
375
376 pub fn default_value(&self) -> T {
378 (self.default)()
379 }
380}
381
382#[cfg(test)]
383pub(crate) fn malformed_static_composition_local_for_test<T: Clone + 'static>(
384 local: &StaticCompositionLocal<T>,
385 entry: Rc<dyn Any>,
386) -> ProvidedValue {
387 malformed_provided_value_for_test(local.key.clone(), entry)
388}
389
390#[expect(non_snake_case)]
392pub fn staticCompositionLocalOf<T: Clone + PartialEq + 'static>(
393 default: impl Fn() -> T + 'static,
394) -> StaticCompositionLocal<T> {
395 staticCompositionLocalOfWithPolicy(default, |current, next| current == next)
396}
397
398#[expect(non_snake_case)]
401pub fn staticCompositionLocalOfWithPolicy<T: Clone + 'static>(
402 default: impl Fn() -> T + 'static,
403 equivalent: impl Fn(&T, &T) -> bool + Send + Sync + 'static,
404) -> StaticCompositionLocal<T> {
405 StaticCompositionLocal {
406 key: LocalKey::new(),
407 default: Rc::new(default),
408 equivalent: Arc::new(equivalent),
409 }
410}
411
412#[expect(non_snake_case)]
418pub fn compositionLocalWithComputedDefaultOf<T: Clone + PartialEq + 'static>(
419 compute: impl Fn(&CompositionLocalAccessorScope) -> T + 'static,
420) -> CompositionLocal<T> {
421 compositionLocalOf(move || compute(&CompositionLocalAccessorScope { _private: () }))
422}
423
424#[expect(non_snake_case)]
428pub fn staticCompositionLocalWithComputedDefaultOf<T: Clone + PartialEq + 'static>(
429 compute: impl Fn(&CompositionLocalAccessorScope) -> T + 'static,
430) -> StaticCompositionLocal<T> {
431 staticCompositionLocalOf(move || compute(&CompositionLocalAccessorScope { _private: () }))
432}