async_ui_core/vnode/
node_context.rs

1use std::{
2    any::{Any, TypeId},
3    future::{Future, IntoFuture},
4    pin::Pin,
5    rc::Rc,
6    task::{Context, Poll},
7};
8
9use pin_project_lite::pin_project;
10
11use crate::{backend::BackendTrait, context::ContextMap, position::PositionIndex, vnode::VNode};
12
13use super::VNodeTrait;
14
15pub struct ContextVNode<B: BackendTrait> {
16    parent: Rc<VNode<B>>,
17    context: ContextMap,
18}
19
20impl<B: BackendTrait> ContextVNode<B> {
21    pub fn new(parent: Rc<VNode<B>>, context: ContextMap) -> Self {
22        Self { parent, context }
23    }
24}
25
26impl<B: BackendTrait> VNodeTrait<B> for ContextVNode<B> {
27    fn add_child_node(&self, node: B::Node, position: PositionIndex) {
28        self.parent.add_child_node(node, position)
29    }
30
31    fn del_child_node(&self, position: PositionIndex) -> B::Node {
32        self.parent.del_child_node(position)
33    }
34
35    fn get_context_map<'s>(&'s self) -> &'s ContextMap {
36        &self.context
37    }
38}
39
40pub fn get_context<B: BackendTrait, T: 'static>() -> Rc<T> {
41    B::get_vnode_key().with(|vn| {
42        let cmap = vn.get_context_map();
43        let type_id = TypeId::of::<T>();
44        let entry = cmap
45            .inner
46            .get(&type_id)
47            .expect("Context not set.")
48            .to_owned();
49        let val = entry.downcast::<T>().unwrap();
50        val
51    })
52}
53enum WithContextState<B>
54where
55    B: BackendTrait,
56{
57    NotStarted { value: Rc<dyn Any> },
58    Started { vnode: Rc<VNode<B>> },
59    Null,
60}
61
62pin_project! {
63    pub struct WithContext<B, F>
64    where
65        B: BackendTrait,
66        F: Future
67    {
68        #[pin]
69        future: F,
70        state: WithContextState<B>,
71    }
72}
73impl<B, F> Future for WithContext<B, F>
74where
75    B: BackendTrait,
76    F: Future,
77{
78    type Output = F::Output;
79
80    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
81        let this = self.project();
82        let vk = B::get_vnode_key();
83        let vnode = match std::mem::replace(this.state, WithContextState::Null) {
84            WithContextState::NotStarted { value } => {
85                let parent = vk.with(Clone::clone);
86                let context = ContextMap {
87                    inner: parent
88                        .get_context_map()
89                        .inner
90                        .update(value.type_id(), value),
91                };
92                let vnode = Rc::new(ContextVNode::new(parent, context).into());
93                vnode
94            }
95            WithContextState::Started { vnode } => vnode,
96            _ => unreachable!(),
97        };
98        let res = vk.set(&vnode, || this.future.poll(cx));
99        *this.state = WithContextState::Started { vnode };
100        res
101    }
102}
103impl<B, F> WithContext<B, F>
104where
105    B: BackendTrait,
106    F: Future,
107{
108    pub fn new<T: 'static, I: IntoFuture<IntoFuture = F>>(into_future: I, value: Rc<T>) -> Self {
109        Self {
110            future: into_future.into_future(),
111            state: WithContextState::NotStarted { value },
112        }
113    }
114}