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
114#[derive(Clone)]
118pub struct CompositionLocal<T: Clone + 'static> {
119 pub(crate) key: LocalKey,
120 default: Rc<dyn Fn() -> T>,
121 equivalent: Arc<LocalEquivalentFn<T>>,
122}
123
124impl<T: Clone + 'static> PartialEq for CompositionLocal<T> {
125 fn eq(&self, other: &Self) -> bool {
126 self.key == other.key
127 }
128}
129
130impl<T: Clone + 'static> Eq for CompositionLocal<T> {}
131
132impl<T: Clone + 'static> CompositionLocal<T> {
133 #[track_caller]
136 pub fn provides(&self, value: T) -> ProvidedValue {
137 let key = self.key.clone();
138 let entry_source = provider_entry_source(&key, crate::caller_location_key());
139 let equivalent = Arc::clone(&self.equivalent);
140 ProvidedValue {
141 key,
142 apply: Box::new(move |composer: &Composer, site: crate::Key| {
143 let runtime = composer.runtime_handle();
144 let source = (entry_source ^ site).wrapping_mul(0x0000_0100_0000_01b3);
145 let entry_ref = composer.remember_internal(source, || {
146 Rc::new(LocalStateEntry::new(
147 value.clone(),
148 runtime.clone(),
149 Arc::clone(&equivalent),
150 ))
151 });
152 entry_ref.update(|entry| entry.set(value.clone()));
153 entry_ref.with(|entry| (entry.clone() as Rc<dyn Any>, false))
154 }),
155 }
156 }
157
158 pub fn current(&self) -> T {
162 composer_context::with_composer(|composer| composer.read_composition_local(self))
163 }
164
165 pub fn default_value(&self) -> T {
167 (self.default)()
168 }
169}
170
171#[cfg(test)]
172fn malformed_provided_value_for_test(key: LocalKey, entry: Rc<dyn Any>) -> ProvidedValue {
173 ProvidedValue {
174 key,
175 apply: Box::new(move |_, _| (entry.clone(), false)),
176 }
177}
178
179#[cfg(test)]
180pub(crate) fn malformed_composition_local_for_test<T: Clone + 'static>(
181 local: &CompositionLocal<T>,
182 entry: Rc<dyn Any>,
183) -> ProvidedValue {
184 malformed_provided_value_for_test(local.key.clone(), entry)
185}
186
187#[expect(non_snake_case)]
190pub fn compositionLocalOf<T: Clone + PartialEq + 'static>(
191 default: impl Fn() -> T + 'static,
192) -> CompositionLocal<T> {
193 compositionLocalOfWithPolicy(default, |current, next| current == next)
194}
195
196#[expect(non_snake_case)]
199pub fn compositionLocalOfWithPolicy<T: Clone + 'static>(
200 default: impl Fn() -> T + 'static,
201 equivalent: impl Fn(&T, &T) -> bool + Send + Sync + 'static,
202) -> CompositionLocal<T> {
203 CompositionLocal {
204 key: LocalKey::new(),
205 default: Rc::new(default),
206 equivalent: Arc::new(equivalent),
207 }
208}
209
210#[derive(Clone)]
217pub struct StaticCompositionLocal<T: Clone + 'static> {
218 pub(crate) key: LocalKey,
219 default: Rc<dyn Fn() -> T>,
220 equivalent: Arc<LocalEquivalentFn<T>>,
221}
222
223impl<T: Clone + 'static> PartialEq for StaticCompositionLocal<T> {
224 fn eq(&self, other: &Self) -> bool {
225 self.key == other.key
226 }
227}
228
229impl<T: Clone + 'static> Eq for StaticCompositionLocal<T> {}
230
231impl<T: Clone + 'static> StaticCompositionLocal<T> {
232 #[track_caller]
235 pub fn provides(&self, value: T) -> ProvidedValue {
236 let key = self.key.clone();
237 let entry_source = provider_entry_source(&key, crate::caller_location_key());
238 let equivalent = Arc::clone(&self.equivalent);
239 ProvidedValue {
240 key,
241 apply: Box::new(move |composer: &Composer, site: crate::Key| {
242 let source = (entry_source ^ site).wrapping_mul(0x0000_0100_0000_01b3);
243 let entry_ref = composer
244 .remember_internal(source, || Rc::new(StaticLocalEntry::new(value.clone())));
245 let changed = entry_ref.update(|entry| entry.set(value.clone(), &*equivalent));
246 entry_ref.with(|entry| (entry.clone() as Rc<dyn Any>, changed))
247 }),
248 }
249 }
250
251 pub fn current(&self) -> T {
254 composer_context::with_composer(|composer| composer.read_static_composition_local(self))
255 }
256
257 pub fn default_value(&self) -> T {
259 (self.default)()
260 }
261}
262
263#[cfg(test)]
264pub(crate) fn malformed_static_composition_local_for_test<T: Clone + 'static>(
265 local: &StaticCompositionLocal<T>,
266 entry: Rc<dyn Any>,
267) -> ProvidedValue {
268 malformed_provided_value_for_test(local.key.clone(), entry)
269}
270
271#[expect(non_snake_case)]
273pub fn staticCompositionLocalOf<T: Clone + PartialEq + 'static>(
274 default: impl Fn() -> T + 'static,
275) -> StaticCompositionLocal<T> {
276 staticCompositionLocalOfWithPolicy(default, |current, next| current == next)
277}
278
279#[expect(non_snake_case)]
282pub fn staticCompositionLocalOfWithPolicy<T: Clone + 'static>(
283 default: impl Fn() -> T + 'static,
284 equivalent: impl Fn(&T, &T) -> bool + Send + Sync + 'static,
285) -> StaticCompositionLocal<T> {
286 StaticCompositionLocal {
287 key: LocalKey::new(),
288 default: Rc::new(default),
289 equivalent: Arc::new(equivalent),
290 }
291}