Skip to main content

freya_core/lifecycle/
context.rs

1use std::{
2    any::TypeId,
3    rc::Rc,
4};
5
6use crate::{
7    current_context::CurrentContext,
8    prelude::use_hook,
9    scope_id::ScopeId,
10};
11
12pub fn provide_context<T: Clone + 'static>(value: T) {
13    provide_context_for_scope_id(value, None)
14}
15
16/// Store the given value in the root scope.
17///
18/// Only needed for specific cases like values consumed by queries or mutations, which run in
19/// the root scope. Prefer [provide_context] otherwise.
20pub fn provide_root_context<T: Clone + 'static>(value: T) {
21    provide_context_for_scope_id(value, Some(ScopeId::ROOT))
22}
23
24pub fn provide_context_for_scope_id<T: Clone + 'static>(
25    value: T,
26    scope_id: impl Into<Option<ScopeId>>,
27) {
28    CurrentContext::with(|context| {
29        let mut scopes_storages = context.scopes_storages.borrow_mut();
30        let scopes_storage = scopes_storages
31            .get_mut(&scope_id.into().unwrap_or(context.scope_id))
32            .unwrap();
33        let type_id = TypeId::of::<T>();
34        scopes_storage.contexts.insert(type_id, Rc::new(value));
35    })
36}
37
38pub fn try_consume_context<T: Clone + 'static>() -> Option<T> {
39    try_consume_context_from_scope_id(None)
40}
41
42/// Try to get a context value stored only in the current scope, without walking up to ancestors.
43pub fn try_consume_own_context<T: Clone + 'static>() -> Option<T> {
44    CurrentContext::with(|context| {
45        let scopes_storages = context.scopes_storages.borrow();
46        let scopes_storage = scopes_storages.get(&context.scope_id)?;
47        let type_id = TypeId::of::<T>();
48        scopes_storage
49            .contexts
50            .get(&type_id)?
51            .downcast_ref::<T>()
52            .cloned()
53    })
54}
55
56pub fn try_consume_root_context<T: Clone + 'static>() -> Option<T> {
57    try_consume_context_from_scope_id(Some(ScopeId::ROOT))
58}
59
60pub fn consume_context<T: Clone + 'static>() -> T {
61    try_consume_context_from_scope_id(None)
62        .unwrap_or_else(|| panic!("Context <{}> was not found.", std::any::type_name::<T>()))
63}
64
65pub fn consume_root_context<T: Clone + 'static>() -> T {
66    try_consume_context_from_scope_id(Some(ScopeId::ROOT)).unwrap_or_else(|| {
67        panic!(
68            "Root context <{}> was not found.",
69            std::any::type_name::<T>()
70        )
71    })
72}
73
74pub fn try_consume_context_from_scope_id<T: Clone + 'static>(
75    scope_id: Option<ScopeId>,
76) -> Option<T> {
77    CurrentContext::with(|context| {
78        let scopes_storages = context.scopes_storages.borrow_mut();
79
80        let mut ladder = vec![scope_id.unwrap_or(context.scope_id)];
81
82        let type_id = TypeId::of::<T>();
83
84        while let Some(scope_id) = ladder.pop() {
85            let scopes_storage = scopes_storages.get(&scope_id)?;
86
87            if let Some(context) = scopes_storage.contexts.get(&type_id) {
88                return context.downcast_ref::<T>().cloned();
89            } else if let Some(parent_scope_id) = scopes_storage.parent_id {
90                ladder.push(parent_scope_id);
91            }
92        }
93
94        None
95    })
96}
97
98/// Store the given value in this component instance.
99/// Any descendant component of this component calling [use_consume] or [consume_context] will have access to it.
100pub fn use_provide_context<T: Clone + 'static>(init: impl FnOnce() -> T) -> T {
101    use_hook(|| {
102        let ctx = init();
103        provide_context(ctx.clone());
104        ctx
105    })
106}
107
108/// Store the given value in the root scope.
109///
110/// Only needed for specific cases like values consumed by queries or mutations, which run in
111/// the root scope. Prefer [use_provide_context] otherwise.
112pub fn use_provide_root_context<T: Clone + 'static>(init: impl FnOnce() -> T) -> T {
113    use_hook(|| {
114        let ctx = init();
115        provide_root_context(ctx.clone());
116        ctx
117    })
118}
119
120/// Get access to a value stored in this component instance or some ancestor.
121pub fn use_consume<T: Clone + 'static>() -> T {
122    use_hook(|| consume_context())
123}
124
125/// Try to get access to a value stored in this component instance or some ancestor.
126pub fn use_try_consume<T: Clone + 'static>() -> Option<T> {
127    use_hook(|| try_consume_context())
128}