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
34pub fn try_consume_root_context<T: Clone + 'static>() -> Option<T> {
35    try_consume_context_from_scope_id(Some(ScopeId::ROOT))
36}
37
38pub fn consume_context<T: Clone + 'static>() -> T {
39    try_consume_context_from_scope_id(None)
40        .unwrap_or_else(|| panic!("Context <{}> was not found.", std::any::type_name::<T>()))
41}
42
43pub fn consume_root_context<T: Clone + 'static>() -> T {
44    try_consume_context_from_scope_id(Some(ScopeId::ROOT)).unwrap_or_else(|| {
45        panic!(
46            "Root context <{}> was not found.",
47            std::any::type_name::<T>()
48        )
49    })
50}
51
52pub fn try_consume_context_from_scope_id<T: Clone + 'static>(
53    scope_id: Option<ScopeId>,
54) -> Option<T> {
55    CurrentContext::with(|context| {
56        let scopes_storages = context.scopes_storages.borrow_mut();
57
58        let mut ladder = vec![scope_id.unwrap_or(context.scope_id)];
59
60        let type_id = TypeId::of::<T>();
61
62        while let Some(scope_id) = ladder.pop() {
63            let scopes_storage = scopes_storages.get(&scope_id)?;
64
65            if let Some(context) = scopes_storage.contexts.get(&type_id) {
66                return context.downcast_ref::<T>().cloned();
67            } else if let Some(parent_scope_id) = scopes_storage.parent_id {
68                ladder.push(parent_scope_id);
69            }
70        }
71
72        None
73    })
74}
75
76pub fn use_provide_context<T: Clone + 'static>(init: impl FnOnce() -> T) -> T {
77    use_hook(|| {
78        let ctx = init();
79        provide_context(ctx.clone());
80        ctx
81    })
82}
83
84pub fn use_consume<T: Clone + 'static>() -> T {
85    use_hook(|| consume_context())
86}
87
88pub fn use_try_consume<T: Clone + 'static>() -> Option<T> {
89    use_hook(|| try_consume_context())
90}