sycamore_reactive/
context.rs1use std::any::{type_name, Any};
4
5use slotmap::Key;
6
7use crate::{create_child_scope, NodeId, Root};
8
9#[cfg_attr(debug_assertions, track_caller)]
15pub fn provide_context<T: 'static>(value: T) {
16 let root = Root::global();
17 provide_context_in_node(root.current_node.get(), value);
18}
19
20pub fn provide_context_in_new_scope<T: 'static, U>(value: T, f: impl FnOnce() -> U) -> U {
41 let mut ret = None;
42 create_child_scope(|| {
43 provide_context(value);
44 ret = Some(f());
45 });
46 ret.unwrap()
47}
48
49#[cfg_attr(debug_assertions, track_caller)]
51fn provide_context_in_node<T: 'static>(id: NodeId, value: T) {
52 let root = Root::global();
53 let mut nodes = root.nodes.borrow_mut();
54 let any: Box<dyn Any> = Box::new(value);
55
56 let node = &mut nodes[id];
57 if node
58 .context
59 .iter()
60 .any(|x| (**x).type_id() == (*any).type_id())
61 {
62 panic!(
63 "a context with type `{}` exists already in this scope",
64 type_name::<T>()
65 );
66 }
67 node.context.push(any);
68}
69
70#[cfg_attr(debug_assertions, track_caller)]
72pub fn try_use_context<T: Clone + 'static>() -> Option<T> {
73 let root = Root::global();
74 let nodes = root.nodes.borrow();
75 let mut current = Some(&nodes[root.current_node.get()]);
77 while let Some(next) = current {
78 for value in &next.context {
79 if let Some(value) = value.downcast_ref::<T>().cloned() {
80 return Some(value);
81 }
82 }
83 if next.parent.is_null() {
85 current = None;
86 } else {
87 current = Some(&nodes[next.parent]);
88 }
89 }
90 None
91}
92
93#[cfg_attr(debug_assertions, track_caller)]
95pub fn use_context<T: Clone + 'static>() -> T {
96 if let Some(value) = try_use_context() {
97 value
98 } else {
99 panic!("no context of type `{}` found", type_name::<T>())
100 }
101}
102
103pub fn use_context_or_else<T: Clone + 'static, F: FnOnce() -> T>(f: F) -> T {
106 try_use_context().unwrap_or_else(|| {
107 let value = f();
108 provide_context(value.clone());
109 value
110 })
111}
112
113pub fn use_scope_depth() -> u32 {
116 let root = Root::global();
117 let nodes = root.nodes.borrow();
118 let mut current = Some(&nodes[root.current_node.get()]);
119 let mut depth = 0;
120
121 while let Some(next) = current {
122 depth += 1;
123 if next.parent.is_null() {
124 current = None;
125 } else {
126 current = Some(&nodes[next.parent]);
127 }
128 }
129 depth
130}
131
132#[cfg(test)]
133mod tests {
134 use crate::*;
135
136 #[test]
138 fn cleanup_resets_context() {
139 let _ = create_root(|| {
140 let trigger = create_signal(());
141 create_memo(move || {
142 trigger.track();
143 assert!(try_use_context::<i32>().is_none());
144 provide_context(123);
145 });
146 trigger.set(());
147 });
148 }
149}