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 {
13 key: LocalKey,
14 #[expect(clippy::type_complexity)]
15 apply: Box<dyn Fn(&Composer, crate::Key) -> (Rc<dyn Any>, bool)>,
16}
17
18impl ProvidedValue {
19 pub(crate) fn key(&self) -> &LocalKey {
20 &self.key
21 }
22
23 pub(crate) fn into_entry(
26 self,
27 composer: &Composer,
28 site: crate::Key,
29 ) -> (LocalKey, Rc<dyn Any>, bool) {
30 let ProvidedValue { key, apply } = self;
31 let (entry, static_changed) = apply(composer, site);
32 (key, entry, static_changed)
33 }
34}
35
36#[expect(non_snake_case)]
37#[track_caller]
38pub fn CompositionLocalProvider(
39 values: impl IntoIterator<Item = ProvidedValue>,
40 content: impl FnOnce(),
41) {
42 let site = crate::caller_location_key();
43 composer_context::with_composer(|composer| {
44 composer.with_composition_locals(values, site, |_composer| content());
45 });
46}
47
48pub(crate) struct LocalStateEntry<T: Clone + 'static> {
49 state: OwnedMutableState<T>,
50}
51
52type LocalEquivalentFn<T> = dyn Fn(&T, &T) -> bool + Send + Sync + 'static;
53
54struct LocalValuePolicy<T: Clone + 'static> {
55 equivalent: Arc<LocalEquivalentFn<T>>,
56}
57
58impl<T: Clone + 'static> MutationPolicy<T> for LocalValuePolicy<T> {
59 fn equivalent(&self, a: &T, b: &T) -> bool {
60 (self.equivalent)(a, b)
61 }
62}
63
64impl<T: Clone + 'static> LocalStateEntry<T> {
65 fn new(initial: T, runtime: RuntimeHandle, equivalent: Arc<LocalEquivalentFn<T>>) -> Self {
66 Self {
67 state: OwnedMutableState::with_runtime_and_policy(
68 initial,
69 runtime,
70 Rc::new(LocalValuePolicy { equivalent }),
71 ),
72 }
73 }
74
75 fn set(&self, value: T) {
76 self.state.replace(value);
77 }
78
79 pub(crate) fn value(&self) -> T {
80 self.state.value()
81 }
82}
83
84pub(crate) struct StaticLocalEntry<T: Clone + 'static> {
85 value: RefCell<T>,
86}
87
88impl<T: Clone + 'static> StaticLocalEntry<T> {
89 fn new(value: T) -> Self {
90 Self {
91 value: RefCell::new(value),
92 }
93 }
94
95 fn set(&self, value: T, equivalent: &LocalEquivalentFn<T>) -> bool {
97 let mut current = self.value.borrow_mut();
98 if equivalent(¤t, &value) {
99 return false;
100 }
101 *current = value;
102 true
103 }
104
105 pub(crate) fn value(&self) -> T {
106 self.value.borrow().clone()
107 }
108}
109
110#[derive(Clone)]
111pub struct CompositionLocal<T: Clone + 'static> {
112 pub(crate) key: LocalKey,
113 default: Rc<dyn Fn() -> T>,
114 equivalent: Arc<LocalEquivalentFn<T>>,
115}
116
117impl<T: Clone + 'static> PartialEq for CompositionLocal<T> {
118 fn eq(&self, other: &Self) -> bool {
119 self.key == other.key
120 }
121}
122
123impl<T: Clone + 'static> Eq for CompositionLocal<T> {}
124
125impl<T: Clone + 'static> CompositionLocal<T> {
126 #[track_caller]
127 pub fn provides(&self, value: T) -> ProvidedValue {
128 let key = self.key.clone();
129 let entry_source = provider_entry_source(&key, crate::caller_location_key());
130 let equivalent = Arc::clone(&self.equivalent);
131 ProvidedValue {
132 key,
133 apply: Box::new(move |composer: &Composer, site: crate::Key| {
134 let runtime = composer.runtime_handle();
135 let source = (entry_source ^ site).wrapping_mul(0x0000_0100_0000_01b3);
136 let entry_ref = composer.remember_internal(source, || {
137 Rc::new(LocalStateEntry::new(
138 value.clone(),
139 runtime.clone(),
140 Arc::clone(&equivalent),
141 ))
142 });
143 entry_ref.update(|entry| entry.set(value.clone()));
144 entry_ref.with(|entry| (entry.clone() as Rc<dyn Any>, false))
145 }),
146 }
147 }
148
149 pub fn current(&self) -> T {
150 composer_context::with_composer(|composer| composer.read_composition_local(self))
151 }
152
153 pub fn default_value(&self) -> T {
154 (self.default)()
155 }
156}
157
158#[cfg(test)]
159fn malformed_provided_value_for_test(key: LocalKey, entry: Rc<dyn Any>) -> ProvidedValue {
160 ProvidedValue {
161 key,
162 apply: Box::new(move |_, _| (entry.clone(), false)),
163 }
164}
165
166#[cfg(test)]
167pub(crate) fn malformed_composition_local_for_test<T: Clone + 'static>(
168 local: &CompositionLocal<T>,
169 entry: Rc<dyn Any>,
170) -> ProvidedValue {
171 malformed_provided_value_for_test(local.key.clone(), entry)
172}
173
174#[expect(non_snake_case)]
175pub fn compositionLocalOf<T: Clone + PartialEq + 'static>(
176 default: impl Fn() -> T + 'static,
177) -> CompositionLocal<T> {
178 compositionLocalOfWithPolicy(default, |current, next| current == next)
179}
180
181#[expect(non_snake_case)]
182pub fn compositionLocalOfWithPolicy<T: Clone + 'static>(
183 default: impl Fn() -> T + 'static,
184 equivalent: impl Fn(&T, &T) -> bool + Send + Sync + 'static,
185) -> CompositionLocal<T> {
186 CompositionLocal {
187 key: LocalKey::new(),
188 default: Rc::new(default),
189 equivalent: Arc::new(equivalent),
190 }
191}
192
193#[derive(Clone)]
200pub struct StaticCompositionLocal<T: Clone + 'static> {
201 pub(crate) key: LocalKey,
202 default: Rc<dyn Fn() -> T>,
203 equivalent: Arc<LocalEquivalentFn<T>>,
204}
205
206impl<T: Clone + 'static> PartialEq for StaticCompositionLocal<T> {
207 fn eq(&self, other: &Self) -> bool {
208 self.key == other.key
209 }
210}
211
212impl<T: Clone + 'static> Eq for StaticCompositionLocal<T> {}
213
214impl<T: Clone + 'static> StaticCompositionLocal<T> {
215 #[track_caller]
216 pub fn provides(&self, value: T) -> ProvidedValue {
217 let key = self.key.clone();
218 let entry_source = provider_entry_source(&key, crate::caller_location_key());
219 let equivalent = Arc::clone(&self.equivalent);
220 ProvidedValue {
221 key,
222 apply: Box::new(move |composer: &Composer, site: crate::Key| {
223 let source = (entry_source ^ site).wrapping_mul(0x0000_0100_0000_01b3);
224 let entry_ref = composer
225 .remember_internal(source, || Rc::new(StaticLocalEntry::new(value.clone())));
226 let changed = entry_ref.update(|entry| entry.set(value.clone(), &*equivalent));
227 entry_ref.with(|entry| (entry.clone() as Rc<dyn Any>, changed))
228 }),
229 }
230 }
231
232 pub fn current(&self) -> T {
233 composer_context::with_composer(|composer| composer.read_static_composition_local(self))
234 }
235
236 pub fn default_value(&self) -> T {
237 (self.default)()
238 }
239}
240
241#[cfg(test)]
242pub(crate) fn malformed_static_composition_local_for_test<T: Clone + 'static>(
243 local: &StaticCompositionLocal<T>,
244 entry: Rc<dyn Any>,
245) -> ProvidedValue {
246 malformed_provided_value_for_test(local.key.clone(), entry)
247}
248
249#[expect(non_snake_case)]
251pub fn staticCompositionLocalOf<T: Clone + PartialEq + 'static>(
252 default: impl Fn() -> T + 'static,
253) -> StaticCompositionLocal<T> {
254 staticCompositionLocalOfWithPolicy(default, |current, next| current == next)
255}
256
257#[expect(non_snake_case)]
260pub fn staticCompositionLocalOfWithPolicy<T: Clone + 'static>(
261 default: impl Fn() -> T + 'static,
262 equivalent: impl Fn(&T, &T) -> bool + Send + Sync + 'static,
263) -> StaticCompositionLocal<T> {
264 StaticCompositionLocal {
265 key: LocalKey::new(),
266 default: Rc::new(default),
267 equivalent: Arc::new(equivalent),
268 }
269}