cranpose_core/
composition_locals.rs1use crate::composer_context;
2use crate::state::{MutationPolicy, OwnedMutableState};
3use crate::{Composer, LocalKey, RuntimeHandle};
4use std::any::Any;
5use std::cell::RefCell;
6use std::rc::Rc;
7use std::sync::Arc;
8
9pub struct ProvidedValue {
10 key: LocalKey,
11 #[allow(clippy::type_complexity)] apply: Box<dyn Fn(&Composer) -> Rc<dyn Any>>,
13}
14
15impl ProvidedValue {
16 pub(crate) fn into_entry(self, composer: &Composer) -> (LocalKey, Rc<dyn Any>) {
17 let ProvidedValue { key, apply } = self;
18 let entry = apply(composer);
19 (key, entry)
20 }
21}
22
23#[allow(non_snake_case)]
24pub fn CompositionLocalProvider(
25 values: impl IntoIterator<Item = ProvidedValue>,
26 content: impl FnOnce(),
27) {
28 composer_context::with_composer(|composer| {
29 let provided: Vec<ProvidedValue> = values.into_iter().collect();
30 composer.with_composition_locals(provided, |_composer| content());
31 })
32}
33
34pub(crate) struct LocalStateEntry<T: Clone + 'static> {
35 state: OwnedMutableState<T>,
36}
37
38type LocalEquivalentFn<T> = dyn Fn(&T, &T) -> bool + Send + Sync + 'static;
39
40struct LocalValuePolicy<T: Clone + 'static> {
41 equivalent: Arc<LocalEquivalentFn<T>>,
42}
43
44impl<T: Clone + 'static> MutationPolicy<T> for LocalValuePolicy<T> {
45 fn equivalent(&self, a: &T, b: &T) -> bool {
46 (self.equivalent)(a, b)
47 }
48}
49
50impl<T: Clone + 'static> LocalStateEntry<T> {
51 fn new(initial: T, runtime: RuntimeHandle, equivalent: Arc<LocalEquivalentFn<T>>) -> Self {
52 Self {
53 state: OwnedMutableState::with_runtime_and_policy(
54 initial,
55 runtime,
56 Arc::new(LocalValuePolicy { equivalent }),
57 ),
58 }
59 }
60
61 fn set(&self, value: T) {
62 self.state.replace(value);
63 }
64
65 pub(crate) fn value(&self) -> T {
66 self.state.value()
67 }
68}
69
70pub(crate) struct StaticLocalEntry<T: Clone + 'static> {
71 value: RefCell<T>,
72}
73
74impl<T: Clone + 'static> StaticLocalEntry<T> {
75 fn new(value: T) -> Self {
76 Self {
77 value: RefCell::new(value),
78 }
79 }
80
81 fn set(&self, value: T) {
82 *self.value.borrow_mut() = value;
83 }
84
85 pub(crate) fn value(&self) -> T {
86 self.value.borrow().clone()
87 }
88}
89
90#[derive(Clone)]
91pub struct CompositionLocal<T: Clone + 'static> {
92 pub(crate) key: LocalKey,
93 default: Rc<dyn Fn() -> T>,
94 equivalent: Arc<LocalEquivalentFn<T>>,
95}
96
97impl<T: Clone + 'static> PartialEq for CompositionLocal<T> {
98 fn eq(&self, other: &Self) -> bool {
99 self.key == other.key
100 }
101}
102
103impl<T: Clone + 'static> Eq for CompositionLocal<T> {}
104
105impl<T: Clone + 'static> CompositionLocal<T> {
106 pub fn provides(&self, value: T) -> ProvidedValue {
107 let key = self.key.clone();
108 let equivalent = Arc::clone(&self.equivalent);
109 ProvidedValue {
110 key,
111 apply: Box::new(move |composer: &Composer| {
112 let runtime = composer.runtime_handle();
113 let entry_ref = composer.remember_internal(|| {
114 Rc::new(LocalStateEntry::new(
115 value.clone(),
116 runtime.clone(),
117 Arc::clone(&equivalent),
118 ))
119 });
120 entry_ref.update(|entry| entry.set(value.clone()));
121 entry_ref.with(|entry| entry.clone() as Rc<dyn Any>)
122 }),
123 }
124 }
125
126 pub fn current(&self) -> T {
127 composer_context::with_composer(|composer| composer.read_composition_local(self))
128 }
129
130 pub fn default_value(&self) -> T {
131 (self.default)()
132 }
133}
134
135#[cfg(test)]
136pub(crate) fn malformed_composition_local_for_test<T: Clone + 'static>(
137 local: &CompositionLocal<T>,
138 entry: Rc<dyn Any>,
139) -> ProvidedValue {
140 let key = local.key.clone();
141 ProvidedValue {
142 key,
143 apply: Box::new(move |_| entry.clone()),
144 }
145}
146
147#[allow(non_snake_case)]
148pub fn compositionLocalOf<T: Clone + PartialEq + 'static>(
149 default: impl Fn() -> T + 'static,
150) -> CompositionLocal<T> {
151 compositionLocalOfWithPolicy(default, |current, next| current == next)
152}
153
154#[allow(non_snake_case)]
155pub fn compositionLocalOfWithPolicy<T: Clone + 'static>(
156 default: impl Fn() -> T + 'static,
157 equivalent: impl Fn(&T, &T) -> bool + Send + Sync + 'static,
158) -> CompositionLocal<T> {
159 CompositionLocal {
160 key: LocalKey::new(),
161 default: Rc::new(default),
162 equivalent: Arc::new(equivalent),
163 }
164}
165
166#[derive(Clone)]
177pub struct StaticCompositionLocal<T: Clone + 'static> {
178 pub(crate) key: LocalKey,
179 default: Rc<dyn Fn() -> T>,
180}
181
182impl<T: Clone + 'static> PartialEq for StaticCompositionLocal<T> {
183 fn eq(&self, other: &Self) -> bool {
184 self.key == other.key
185 }
186}
187
188impl<T: Clone + 'static> Eq for StaticCompositionLocal<T> {}
189
190impl<T: Clone + 'static> StaticCompositionLocal<T> {
191 pub fn provides(&self, value: T) -> ProvidedValue {
192 let key = self.key.clone();
193 ProvidedValue {
194 key,
195 apply: Box::new(move |composer: &Composer| {
196 let entry_ref =
197 composer.remember_internal(|| Rc::new(StaticLocalEntry::new(value.clone())));
198 entry_ref.update(|entry| entry.set(value.clone()));
199 entry_ref.with(|entry| entry.clone() as Rc<dyn Any>)
200 }),
201 }
202 }
203
204 pub fn current(&self) -> T {
205 composer_context::with_composer(|composer| composer.read_static_composition_local(self))
206 }
207
208 pub fn default_value(&self) -> T {
209 (self.default)()
210 }
211}
212
213#[cfg(test)]
214pub(crate) fn malformed_static_composition_local_for_test<T: Clone + 'static>(
215 local: &StaticCompositionLocal<T>,
216 entry: Rc<dyn Any>,
217) -> ProvidedValue {
218 let key = local.key.clone();
219 ProvidedValue {
220 key,
221 apply: Box::new(move |_| entry.clone()),
222 }
223}
224
225#[allow(non_snake_case)]
226pub fn staticCompositionLocalOf<T: Clone + 'static>(
227 default: impl Fn() -> T + 'static,
228) -> StaticCompositionLocal<T> {
229 StaticCompositionLocal {
230 key: LocalKey::new(),
231 default: Rc::new(default),
232 }
233}