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
70pub fn try_use_context<T: Clone + 'static>() -> Option<T> {
72 let root = Root::global();
73 let nodes = root.nodes.borrow();
74 let mut current = Some(&nodes[root.current_node.get()]);
76 while let Some(next) = current {
77 for value in &next.context {
78 if let Some(value) = value.downcast_ref::<T>().cloned() {
79 return Some(value);
80 }
81 }
82 if next.parent.is_null() {
84 current = None;
85 } else {
86 current = Some(&nodes[next.parent]);
87 }
88 }
89 None
90}
91
92#[cfg_attr(debug_assertions, track_caller)]
94pub fn use_context<T: Clone + 'static>() -> T {
95 if let Some(value) = try_use_context() {
96 value
97 } else {
98 panic!("no context of type `{}` found", type_name::<T>())
99 }
100}
101
102pub fn use_context_or_else<T: Clone + 'static, F: FnOnce() -> T>(f: F) -> T {
105 try_use_context().unwrap_or_else(|| {
106 let value = f();
107 provide_context(value.clone());
108 value
109 })
110}
111
112pub fn use_scope_depth() -> u32 {
115 let root = Root::global();
116 let nodes = root.nodes.borrow();
117 let mut current = Some(&nodes[root.current_node.get()]);
118 let mut depth = 0;
119
120 while let Some(next) = current {
121 depth += 1;
122 if next.parent.is_null() {
123 current = None;
124 } else {
125 current = Some(&nodes[next.parent]);
126 }
127 }
128 depth
129}