zng_var/var_impl/
context_var.rs1use std::{any::TypeId, fmt, ops, sync::Arc};
4
5use smallbox::SmallBox;
6use zng_app_context::{AppLocalId, ContextLocal, ContextLocalKeyProvider};
7
8use crate::{
9 AnyVar, AnyVarHookArgs, AnyVarModify, AnyVarValue, BoxAnyVarValue, ContextInitHandle, DynAnyVar, DynWeakAnyVar, IntoVar, Var,
10 VarCapability, VarHandle, VarImpl, VarInstanceTag, VarUpdateId, VarValue, WeakVarImpl,
11};
12
13#[macro_export]
57macro_rules! context_var {
58 ($(
59 $(#[$attr:meta])*
60 $vis:vis static $NAME:ident: $Type:ty = $default:expr;
61 )+) => {$(
62 $(#[$attr])*
63 $vis static $NAME: $crate::ContextVar<$Type> = {
64 $crate::__context_var_local! {
65 static CTX: $crate::AnyVar = $crate::context_var_init::<$Type>($default);
66 }
67 static VAR: std::sync::OnceLock<$crate::Var<$Type>> = std::sync::OnceLock::new();
68 $crate::ContextVar::new(&CTX, &VAR)
69 };
70 )+}
71}
72
73#[doc(hidden)]
74pub use zng_app_context::context_local as __context_var_local;
75
76#[doc(hidden)]
77pub fn context_var_init<T: VarValue>(init: impl IntoVar<T>) -> AnyVar {
78 init.into_var().into()
79}
80
81impl<T: VarValue> ContextLocalKeyProvider for ContextVar<T> {
82 fn context_local_key(&'static self) -> AppLocalId {
83 self.ctx.context_local_key()
84 }
85}
86
87pub struct ContextVar<T: VarValue> {
97 ctx: &'static ContextLocal<AnyVar>,
98 var: &'static std::sync::OnceLock<Var<T>>,
99}
100impl<T: VarValue> Copy for ContextVar<T> {}
101impl<T: VarValue> Clone for ContextVar<T> {
102 fn clone(&self) -> Self {
103 *self
104 }
105}
106
107impl<T: VarValue> ContextVar<T> {
108 #[doc(hidden)]
109 pub const fn new(ctx: &'static ContextLocal<AnyVar>, var: &'static std::sync::OnceLock<Var<T>>) -> Self {
110 Self { ctx, var }
111 }
112
113 pub fn as_var(&self) -> &Var<T> {
124 self.var
125 .get_or_init(|| Var::new_any(AnyVar(DynAnyVar::Context(ContextVarImpl(self.ctx)))))
126 }
127
128 pub fn with_context<R>(self, id: ContextInitHandle, var: &mut Option<Arc<AnyVar>>, action: impl FnOnce() -> R) -> R {
139 #[cfg(debug_assertions)]
140 {
141 let var = var.as_ref().expect("context `var` not set");
142 assert!(var.value_is::<T>(), "context `var` not of the expected value type `T`");
143 assert!(!var.capabilities().is_contextual(), "context `var` must be current_context");
144 }
145 self.ctx.with_context_var(var, move || id.with_context(action))
146 }
147
148 pub fn with_context_var<R>(self, id: ContextInitHandle, var: impl IntoVar<T>, action: impl FnOnce() -> R) -> R {
159 let mut var = Some(Arc::new(var.into_var().as_any().current_context()));
160 self.with_context(id, &mut var, action)
161 }
162}
163impl<T: VarValue> ops::Deref for ContextVar<T> {
164 type Target = Var<T>;
165
166 fn deref(&self) -> &Self::Target {
167 self.as_var()
168 }
169}
170impl<T: VarValue> IntoVar<T> for ContextVar<T> {
171 fn into_var(self) -> Var<T> {
172 self.as_var().clone()
173 }
174}
175impl<T: VarValue> From<ContextVar<T>> for Var<T> {
176 fn from(v: ContextVar<T>) -> Self {
177 v.as_var().clone()
178 }
179}
180impl<T: VarValue> From<ContextVar<T>> for AnyVar {
181 fn from(v: ContextVar<T>) -> Self {
182 v.as_any().clone()
183 }
184}
185pub(crate) struct ContextVarImpl(&'static ContextLocal<AnyVar>);
186impl fmt::Debug for ContextVarImpl {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 f.debug_tuple("ContextVar").finish_non_exhaustive() }
190}
191impl PartialEq for ContextVarImpl {
192 fn eq(&self, other: &Self) -> bool {
193 std::ptr::eq(self.0, other.0)
194 }
195}
196impl VarImpl for ContextVarImpl {
197 fn clone_dyn(&self) -> DynAnyVar {
198 DynAnyVar::Context(Self(self.0))
199 }
200
201 fn value_type(&self) -> TypeId {
202 self.0.get_clone().0.value_type()
203 }
204
205 #[cfg(feature = "type_names")]
206 fn value_type_name(&self) -> &'static str {
207 self.0.get().0.value_type_name()
208 }
209
210 fn strong_count(&self) -> usize {
211 1
212 }
213
214 fn var_eq(&self, other: &DynAnyVar) -> bool {
215 match other {
216 DynAnyVar::Context(b) => self == b,
217 _ => false,
218 }
219 }
220
221 fn var_instance_tag(&self) -> VarInstanceTag {
222 self.0.get().0.var_instance_tag()
223 }
224
225 fn downgrade(&self) -> DynWeakAnyVar {
226 DynWeakAnyVar::Context(Self(self.0))
227 }
228
229 fn capabilities(&self) -> VarCapability {
230 self.0.get().0.capabilities() | VarCapability::CONTEXT | VarCapability::MODIFY_CHANGES
231 }
232
233 fn with(&self, visitor: &mut dyn FnMut(&dyn AnyVarValue)) {
234 self.0.get().0.with(visitor);
235 }
236
237 fn get(&self) -> BoxAnyVarValue {
238 self.0.get().0.get()
239 }
240
241 fn set(&self, new_value: BoxAnyVarValue) -> bool {
242 self.0.get().0.set(new_value)
243 }
244
245 fn update(&self) -> bool {
246 self.0.get().0.update()
247 }
248
249 fn modify(&self, modify: SmallBox<dyn FnMut(&mut AnyVarModify) + Send + 'static, smallbox::space::S4>) -> bool {
250 self.0.get().0.modify(modify)
251 }
252
253 fn hook(&self, on_new: SmallBox<dyn FnMut(&AnyVarHookArgs) -> bool + Send + 'static, smallbox::space::S4>) -> VarHandle {
254 self.0.get().0.hook(on_new)
255 }
256
257 fn last_update(&self) -> VarUpdateId {
258 self.0.get().0.last_update()
259 }
260
261 fn modify_info(&self) -> crate::animation::ModifyInfo {
262 self.0.get().0.modify_info()
263 }
264
265 fn modify_importance(&self) -> usize {
266 self.0.get().0.modify_importance()
267 }
268
269 fn is_animating(&self) -> bool {
270 self.0.get().0.is_animating()
271 }
272
273 fn hook_animation_stop(&self, handler: crate::animation::AnimationStopFn) -> VarHandle {
274 self.0.get().0.hook_animation_stop(handler)
275 }
276
277 fn current_context(&self) -> DynAnyVar {
278 self.0.get().0.current_context()
280 }
281}
282impl WeakVarImpl for ContextVarImpl {
283 fn clone_dyn(&self) -> DynWeakAnyVar {
284 DynWeakAnyVar::Context(Self(self.0))
285 }
286
287 fn strong_count(&self) -> usize {
288 1
289 }
290
291 fn upgrade(&self) -> Option<DynAnyVar> {
292 Some(DynAnyVar::Context(Self(self.0)))
293 }
294}