1mod stack;
4
5use std::{fmt, sync::Arc};
6
7use crate::{
8 context::stack::GLOBAL_CONTEXT_STACK,
9 evaluator::{Evaluator, EvaluatorRef, WeakEvaluatorRef, get_default},
10 extensions::Extensions,
11 fields::Fields,
12};
13
14#[derive(Clone)]
22pub struct Context {
23 data: Option<Arc<Data>>,
24}
25
26struct Data {
27 evaluator: WeakEvaluatorRef,
28 parent: Option<Context>,
29 extensions: Extensions,
30}
31
32impl Context {
33 pub fn new(fields: Fields<'_>) -> Context {
40 Context::new_with_parent(Context::current().as_ref(), fields)
41 }
42
43 pub fn new_with_parent(mut parent: Option<&Context>, fields: Fields<'_>) -> Context {
50 if parent.is_some_and(|p| p.is_root()) {
51 parent = None;
52 }
53
54 get_default(|evaluator| {
55 let data = match evaluator {
56 Some(evaluator) => {
57 let mut data = Data {
58 evaluator: evaluator.downgrade(),
59 parent: parent.cloned(),
60 extensions: Extensions::new(),
61 };
62
63 evaluator.on_new_context(ContextRef { data: &mut data }, fields);
64
65 data
66 }
67 _ => Data {
68 evaluator: WeakEvaluatorRef::new(),
69 parent: parent.cloned(),
70 extensions: Extensions::new(),
71 },
72 };
73
74 Context {
75 data: Some(Arc::new(data)),
76 }
77 })
78 }
79
80 pub const fn root() -> Context {
85 Context { data: None }
86 }
87
88 pub fn is_root(&self) -> bool {
90 self.data.is_none()
91 }
92
93 pub fn current() -> Option<Context> {
95 GLOBAL_CONTEXT_STACK.current()
96 }
97
98 pub fn current_or_root() -> Context {
100 Context::current().unwrap_or(Context::root())
101 }
102
103 pub fn parent(&self) -> Option<&Context> {
108 self.data
109 .as_ref()?
110 .parent
111 .as_ref()
112 .or(Some(const { &Context::root() }))
113 }
114
115 pub fn extensions(&self) -> &Extensions {
117 self.data
118 .as_ref()
119 .map(|data| &data.extensions)
120 .unwrap_or(const { &Extensions::new() })
121 }
122
123 pub fn iter(&self) -> impl Iterator<Item = &Context> {
125 std::iter::successors(Some(self), |context| context.parent())
126 }
127
128 pub fn in_scope<F: FnOnce() -> R, R>(&self, f: F) -> R {
130 GLOBAL_CONTEXT_STACK.in_scope(self, f)
131 }
132
133 pub(crate) fn evaluator(&self) -> Option<EvaluatorRef> {
135 match &self.data {
136 Some(data) => data.evaluator.upgrade(),
137 None => {
138 get_default(|evaluator| evaluator.cloned())
140 }
141 }
142 }
143}
144
145impl fmt::Debug for Context {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 f.debug_struct("Context").finish_non_exhaustive()
148 }
149}
150
151impl Drop for Data {
152 fn drop(&mut self) {
153 if let Some(evaluator) = self.evaluator.upgrade() {
154 evaluator.on_close_context(ContextRef { data: self })
155 }
156 }
157}
158
159pub struct ContextRef<'a> {
161 data: &'a mut Data,
162}
163
164impl ContextRef<'_> {
165 pub fn parent(&self) -> Option<&Context> {
169 self.data.parent.as_ref()
170 }
171
172 pub fn extensions(&self) -> &Extensions {
174 &self.data.extensions
175 }
176
177 pub fn extensions_mut(&mut self) -> &mut Extensions {
179 &mut self.data.extensions
180 }
181
182 pub fn iter(&self) -> impl Iterator<Item = &Context> {
187 self.data.parent.iter().flat_map(Context::iter)
188 }
189
190 pub(crate) fn by_mut(&mut self) -> ContextRef<'_> {
191 ContextRef { data: self.data }
192 }
193}
194
195#[macro_export]
210macro_rules! context {
211 (parent: $parent:expr $(, $($fields:tt)*)?) => {
212 $crate::context::Context::new_with_parent(
213 $crate::context::AsContextParam::as_context_param(
214 &$parent
215 ),
216 $crate::fields!($($($fields)*)?),
217 )
218 };
219 ($($fields:tt)*) => {
220 $crate::context::Context::new($crate::fields!($($fields)*))
221 };
222}
223
224#[allow(unused_imports)]
226use crate::context;
227
228#[doc(hidden)]
230pub trait AsContextParam {
231 fn as_context_param(&self) -> Option<&Context>;
232}
233
234impl AsContextParam for Context {
235 fn as_context_param(&self) -> Option<&Context> {
236 Some(self)
237 }
238}
239
240impl AsContextParam for Option<Context> {
241 fn as_context_param(&self) -> Option<&Context> {
242 self.as_ref()
243 }
244}
245
246impl AsContextParam for () {
247 fn as_context_param(&self) -> Option<&Context> {
248 None
249 }
250}