async_ui_core/vnode/
node_context.rs1use 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}