Skip to main content

freya_core/
current_context.rs

1use std::{
2    cell::RefCell,
3    rc::Rc,
4    sync::atomic::AtomicU64,
5};
6
7use rustc_hash::FxHashMap;
8
9use crate::{
10    prelude::{
11        Task,
12        TaskId,
13    },
14    reactive_context::ReactiveContext,
15    runner::Message,
16    scope::ScopeStorage,
17    scope_id::ScopeId,
18};
19
20// TODO: rendering flag.
21pub struct CurrentContext {
22    pub scope_id: ScopeId,
23    pub scopes_storages: Rc<RefCell<FxHashMap<ScopeId, ScopeStorage>>>,
24
25    pub tasks: Rc<RefCell<FxHashMap<TaskId, Rc<RefCell<Task>>>>>,
26    pub task_id_counter: Rc<AtomicU64>,
27    pub sender: futures_channel::mpsc::UnboundedSender<Message>,
28}
29
30impl CurrentContext {
31    pub fn run_with_reactive<T>(new_context: Self, run: impl FnOnce() -> T) -> T {
32        let reactive_context = {
33            let scope_storages = new_context.scopes_storages.borrow();
34            let scope_storage = scope_storages.get(&new_context.scope_id).unwrap();
35            scope_storage.reactive_context.clone()
36        };
37        Self::run(new_context, || ReactiveContext::run(reactive_context, run))
38    }
39
40    pub fn run<T>(new_context: Self, run: impl FnOnce() -> T) -> T {
41        let previous = CURRENT_CONTEXT.with_borrow_mut(|context| context.replace(new_context));
42        let result = run();
43        let _removed_context =
44            CURRENT_CONTEXT.with_borrow_mut(|context| std::mem::replace(context, previous));
45        result
46    }
47
48    /// Run a closure using `scope_id` as the current scope, restoring the previous one afterwards.
49    ///
50    /// The closure still runs when there is no current context, just without any scope change.
51    pub(crate) fn run_in_scope<T>(scope_id: ScopeId, run: impl FnOnce() -> T) -> T {
52        let previous_scope_id = CURRENT_CONTEXT.with_borrow_mut(|context| {
53            context
54                .as_mut()
55                .map(|context| std::mem::replace(&mut context.scope_id, scope_id))
56        });
57
58        let res = run();
59
60        CURRENT_CONTEXT.with_borrow_mut(|context| {
61            if let Some(context) = context.as_mut()
62                && let Some(previous_scope_id) = previous_scope_id
63            {
64                context.scope_id = previous_scope_id;
65            }
66        });
67
68        res
69    }
70
71    pub fn with<T>(with: impl FnOnce(&CurrentContext) -> T) -> T {
72        CURRENT_CONTEXT.with(|context| with(context.borrow().as_ref().expect("Your trying to access Freya's current context outside of it, you might be in a separate thread or async task that is not integrated with Freya.")))
73    }
74
75    pub fn try_with<T>(with: impl FnOnce(&CurrentContext) -> T) -> Option<T> {
76        CURRENT_CONTEXT
77            .try_with(|context| {
78                if let Ok(context) = context.try_borrow()
79                    && let Some(context) = context.as_ref()
80                {
81                    Some(with(context))
82                } else {
83                    None
84                }
85            })
86            .ok()
87            .flatten()
88    }
89}
90
91thread_local! {
92    static CURRENT_CONTEXT: RefCell<Option<CurrentContext>> = const { RefCell::new(None) }
93}