1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
use crate::owner::Owner;
use or_poisoned::OrPoisoned;
use std::{
    any::{Any, TypeId},
    collections::VecDeque,
};

impl Owner {
    fn provide_context<T: Send + Sync + 'static>(&self, value: T) {
        self.inner
            .write()
            .or_poisoned()
            .contexts
            .insert(value.type_id(), Box::new(value));
    }

    fn use_context<T: Clone + 'static>(&self) -> Option<T> {
        let ty = TypeId::of::<T>();
        let inner = self.inner.read().or_poisoned();
        let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());
        let contexts = &self.inner.read().or_poisoned().contexts;
        if let Some(context) = contexts.get(&ty) {
            context.downcast_ref::<T>().cloned()
        } else {
            while let Some(ref this_parent) = parent.clone() {
                let this_parent = this_parent.read().or_poisoned();
                let contexts = &this_parent.contexts;
                let value = contexts.get(&ty);
                let downcast = value
                    .and_then(|context| context.downcast_ref::<T>().cloned());
                if let Some(value) = downcast {
                    return Some(value);
                } else {
                    parent =
                        this_parent.parent.as_ref().and_then(|p| p.upgrade());
                }
            }
            None
        }
    }

    fn take_context<T: 'static>(&self) -> Option<T> {
        let ty = TypeId::of::<T>();
        let inner = self.inner.read().or_poisoned();
        let mut parent = inner.parent.as_ref().and_then(|p| p.upgrade());
        let contexts = &mut self.inner.write().or_poisoned().contexts;
        if let Some(context) = contexts.remove(&ty) {
            context.downcast::<T>().ok().map(|n| *n)
        } else {
            while let Some(ref this_parent) = parent.clone() {
                let mut this_parent = this_parent.write().or_poisoned();
                let contexts = &mut this_parent.contexts;
                let value = contexts.remove(&ty);
                let downcast =
                    value.and_then(|context| context.downcast::<T>().ok());
                if let Some(value) = downcast {
                    return Some(*value);
                } else {
                    parent =
                        this_parent.parent.as_ref().and_then(|p| p.upgrade());
                }
            }
            None
        }
    }

    /// Searches for items stored in context in either direction, either among parents or among
    /// descendants.
    pub fn use_context_bidirectional<T: Clone + 'static>(&self) -> Option<T> {
        self.use_context()
            .unwrap_or_else(|| self.find_context_in_children())
    }

    fn find_context_in_children<T: Clone + 'static>(&self) -> Option<T> {
        let ty = TypeId::of::<T>();
        let inner = self.inner.read().or_poisoned();
        let mut to_search = VecDeque::new();
        to_search.extend(inner.children.clone());
        drop(inner);

        while let Some(next) = to_search.pop_front() {
            if let Some(child) = next.upgrade() {
                let child = child.read().or_poisoned();
                let contexts = &child.contexts;
                if let Some(context) = contexts.get(&ty) {
                    return context.downcast_ref::<T>().cloned();
                }

                to_search.extend(child.children.clone());
            }
        }

        None
    }
}

/// Provides a context value of type `T` to the current reactive [`Owner`]
/// and all of its descendants. This can be accessed using [`use_context`].
///
/// This is useful for passing values down to components or functions lower in a
/// hierarchy without needs to “prop drill” by passing them through each layer as
/// arguments to a function or properties of a component.
///
/// Context works similarly to variable scope: a context that is provided higher in
/// the reactive graph can be used lower down, but a context that is provided lower
/// down cannot be used higher up.
///
/// ```rust
/// # use reactive_graph::prelude::*;
/// # use reactive_graph::owner::*;
/// # use reactive_graph::effect::Effect;
/// # futures::executor::block_on(async move {
/// # any_spawner::Executor::init_futures_executor();
/// Effect::new(move |_| {
///     println!("Provider");
///     provide_context(42i32); // provide an i32
///
///     Effect::new(move |_| {
///         println!("intermediate node");
///
///         Effect::new(move |_| {
///             let value = use_context::<i32>()
///                 .expect("could not find i32 in context");
///             assert_eq!(value, 42);
///         });
///     });
/// });
/// # });
/// ```
///
/// ## Context Shadowing
///
/// Only a single value of any type can be provided via context. If you need to provide multiple
/// values of the same type, wrap each one in a "newtype" struct wrapper so that each one is a
/// distinct type.
///
/// Providing a second value of the same type "lower" in the ownership tree will shadow the value,
/// just as a second `let` declaration with the same variable name will shadow that variable.
///
/// ```rust
/// # use reactive_graph::prelude::*;
/// # use reactive_graph::owner::*;
/// # use reactive_graph::effect::Effect;
/// # futures::executor::block_on(async move {
/// # any_spawner::Executor::init_futures_executor();
/// Effect::new(move |_| {
///     println!("Provider");
///     provide_context("foo"); // provide a &'static str
///
///     Effect::new(move |_| {
///         // before we provide another value of the same type, we can access the old one
///         assert_eq!(use_context::<&'static str>(), Some("foo"));
///         // but providing another value of the same type shadows it
///         provide_context("bar");
///
///         Effect::new(move |_| {
///             assert_eq!(use_context::<&'static str>(), Some("bar"));
///         });
///     });
/// });
/// # });
/// ```
pub fn provide_context<T: Send + Sync + 'static>(value: T) {
    if let Some(owner) = Owner::current() {
        owner.provide_context(value);
    }
}

/// Extracts a context value of type `T` from the reactive system by traversing
/// it upwards, beginning from the current reactive [`Owner`] and iterating
/// through its parents, if any. When the value is found, it is cloned.
///
/// The context value should have been provided elsewhere using
/// [`provide_context`](provide_context).
///
/// This is useful for passing values down to components or functions lower in a
/// hierarchy without needs to “prop drill” by passing them through each layer as
/// arguments to a function or properties of a component.
///
/// Context works similarly to variable scope: a context that is provided higher in
/// the reactive graph can be used lower down, but a context that is provided lower
/// in the tree cannot be used higher up.
///
/// While the term “consume” is sometimes used, note that [`use_context`] clones the value, rather
/// than removing it; it is still accessible to other users.
///
/// ```rust
/// # use reactive_graph::prelude::*;
/// # use reactive_graph::owner::*;
/// # use reactive_graph::effect::Effect;
/// # futures::executor::block_on(async move {
/// # any_spawner::Executor::init_futures_executor();
/// Effect::new(move |_| {
///     provide_context(String::from("foo"));
///
///     Effect::new(move |_| {
///         // each use_context clones the value
///         let value =
///             use_context::<String>().expect("could not find i32 in context");
///         assert_eq!(value, "foo");
///         let value2 =
///             use_context::<String>().expect("could not find i32 in context");
///         assert_eq!(value2, "foo");
///     });
/// });
/// # });
/// ```
pub fn use_context<T: Clone + 'static>() -> Option<T> {
    Owner::current().and_then(|owner| owner.use_context())
}

/// Extracts a context value of type `T` from the reactive system by traversing
/// it upwards, beginning from the current reactive [`Owner`] and iterating
/// through its parents, if any. When the value is found, it is cloned.
///
/// Panics if no value is found.
///
/// The context value should have been provided elsewhere using
/// [`provide_context`](provide_context).
///
/// This is useful for passing values down to components or functions lower in a
/// hierarchy without needs to “prop drill” by passing them through each layer as
/// arguments to a function or properties of a component.
///
/// Context works similarly to variable scope: a context that is provided higher in
/// the reactive graph can be used lower down, but a context that is provided lower
/// in the tree cannot be used higher up.
///
/// While the term “consume” is sometimes used, note that [`use_context`] clones the value, rather
/// than removing it; it is still accessible to other users.
///
/// ```rust
/// # use reactive_graph::prelude::*;
/// # use reactive_graph::owner::*;
/// # use reactive_graph::effect::Effect;
/// # futures::executor::block_on(async move {
/// # any_spawner::Executor::init_futures_executor();
/// Effect::new(move |_| {
///     provide_context(String::from("foo"));
///
///     Effect::new(move |_| {
///         // each use_context clones the value
///         let value =
///             use_context::<String>().expect("could not find i32 in context");
///         assert_eq!(value, "foo");
///         let value2 =
///             use_context::<String>().expect("could not find i32 in context");
///         assert_eq!(value2, "foo");
///     });
/// });
/// # });
/// ```
/// ## Panics
/// Panics if a context of this type is not found in the current reactive
/// owner or its ancestors.
#[track_caller]
pub fn expect_context<T: Clone + 'static>() -> T {
    let location = std::panic::Location::caller();

    use_context().unwrap_or_else(|| {
        panic!(
            "{:?} expected context of type {:?} to be present",
            location,
            std::any::type_name::<T>()
        )
    })
}

/// Extracts a context value of type `T` from the reactive system by traversing
/// it upwards, beginning from the current reactive [`Owner`] and iterating
/// through its parents, if any. When the value is found, it is removed from the context,
/// and is not available to any other [`use_context`] or [`take_context`] calls.
///
/// If the value is `Clone`, use [`use_context`] instead.
///
/// The context value should have been provided elsewhere using
/// [`provide_context`](provide_context).
///
/// This is useful for passing values down to components or functions lower in a
/// hierarchy without needs to “prop drill” by passing them through each layer as
/// arguments to a function or properties of a component.
///
/// Context works similarly to variable scope: a context that is provided higher in
/// the reactive graph can be used lower down, but a context that is provided lower
/// in the tree cannot be used higher up.
/// ```rust
/// # use reactive_graph::prelude::*;
/// # use reactive_graph::owner::*;
/// # use reactive_graph::effect::Effect;
/// # futures::executor::block_on(async move {
/// # any_spawner::Executor::init_futures_executor();
///
/// #[derive(Debug, PartialEq)]
/// struct NotClone(String);
///
/// Effect::new(move |_| {
///     provide_context(NotClone(String::from("foo")));
///
///     Effect::new(move |_| {
///         // take_context removes the value from context without needing to clone
///         let value = take_context::<NotClone>();
///         assert_eq!(value, Some(NotClone(String::from("foo"))));
///         let value2 = take_context::<NotClone>();
///         assert_eq!(value2, None);
///     });
/// });
/// # });
/// ```
pub fn take_context<T: 'static>() -> Option<T> {
    Owner::current().and_then(|owner| owner.take_context())
}