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
16pub fn provide_context_for_scope_id<T: Clone + 'static>(
17    value: T,
18    scope_id: impl Into<Option<ScopeId>>,
19) {
20    CurrentContext::with(|context| {
21        let mut scopes_storages = context.scopes_storages.borrow_mut();
22        let scopes_storage = scopes_storages
23            .get_mut(&scope_id.into().unwrap_or(context.scope_id))
24            .unwrap();
25        let type_id = TypeId::of::<T>();
26        scopes_storage.contexts.insert(type_id, Rc::new(value));
27    })
28}
29
30pub fn try_consume_context<T: Clone + 'static>() -> Option<T> {
31    try_consume_context_from_scope_id(None)
32}
33
34/// Try to get a context value stored only in the current scope, without walking up to ancestors.
35pub fn try_consume_own_context<T: Clone + 'static>() -> Option<T> {
36    CurrentContext::with(|context| {
37        let scopes_storages = context.scopes_storages.borrow();
38        let scopes_storage = scopes_storages.get(&context.scope_id)?;
39        let type_id = TypeId::of::<T>();
40        scopes_storage
41            .contexts
42            .get(&type_id)?
43            .downcast_ref::<T>()
44            .cloned()
45    })
46}
47
48pub fn try_consume_root_context<T: Clone + 'static>() -> Option<T> {
49    try_consume_context_from_scope_id(Some(ScopeId::ROOT))
50}
51
52pub fn consume_context<T: Clone + 'static>() -> T {
53    try_consume_context_from_scope_id(None)
54        .unwrap_or_else(|| panic!("Context <{}> was not found.", std::any::type_name::<T>()))
55}
56
57pub fn consume_root_context<T: Clone + 'static>() -> T {
58    try_consume_context_from_scope_id(Some(ScopeId::ROOT)).unwrap_or_else(|| {
59        panic!(
60            "Root context <{}> was not found.",
61            std::any::type_name::<T>()
62        )
63    })
64}
65
66pub fn try_consume_context_from_scope_id<T: Clone + 'static>(
67    scope_id: Option<ScopeId>,
68) -> Option<T> {
69    CurrentContext::with(|context| {
70        let scopes_storages = context.scopes_storages.borrow_mut();
71
72        let mut ladder = vec![scope_id.unwrap_or(context.scope_id)];
73
74        let type_id = TypeId::of::<T>();
75
76        while let Some(scope_id) = ladder.pop() {
77            let scopes_storage = scopes_storages.get(&scope_id)?;
78
79            if let Some(context) = scopes_storage.contexts.get(&type_id) {
80                return context.downcast_ref::<T>().cloned();
81            } else if let Some(parent_scope_id) = scopes_storage.parent_id {
82                ladder.push(parent_scope_id);
83            }
84        }
85
86        None
87    })
88}
89
90/// Store the given value in this component instance.
91/// Any descendant component of this component calling [use_consume] or [consume_context] will have access to it.
92pub fn use_provide_context<T: Clone + 'static>(init: impl FnOnce() -> T) -> T {
93    use_hook(|| {
94        let ctx = init();
95        provide_context(ctx.clone());
96        ctx
97    })
98}
99
100/// Get access to a value stored in this component instance or some ancestor.
101pub fn use_consume<T: Clone + 'static>() -> T {
102    use_hook(|| consume_context())
103}
104
105/// Try to get access to a value stored in this component instance or some ancestor.
106pub fn use_try_consume<T: Clone + 'static>() -> Option<T> {
107    use_hook(|| try_consume_context())
108}