yew_agent/oneshot/
provider.rs1use core::fmt;
2use std::any::type_name;
3use std::cell::RefCell;
4use std::rc::Rc;
5
6use serde::{Deserialize, Serialize};
7use yew::prelude::*;
8
9use super::{Oneshot, OneshotBridge, OneshotSpawner};
10use crate::utils::get_next_id;
11use crate::worker::WorkerProviderProps;
12use crate::{Bincode, Codec, Reach};
13
14pub(crate) struct OneshotProviderState<T>
15where
16 T: Oneshot + 'static,
17{
18 id: usize,
19 spawn_bridge_fn: Rc<dyn Fn() -> OneshotBridge<T>>,
20 reach: Reach,
21 held_bridge: Rc<RefCell<Option<OneshotBridge<T>>>>,
22}
23
24impl<T> fmt::Debug for OneshotProviderState<T>
25where
26 T: Oneshot,
27{
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 f.write_str(type_name::<Self>())
30 }
31}
32
33impl<T> OneshotProviderState<T>
34where
35 T: Oneshot,
36{
37 fn get_held_bridge(&self) -> OneshotBridge<T> {
38 let mut held_bridge = self.held_bridge.borrow_mut();
39
40 match held_bridge.as_mut() {
41 Some(m) => m.fork(),
42 None => {
43 let bridge = (self.spawn_bridge_fn)();
44 *held_bridge = Some(bridge.fork());
45 bridge
46 }
47 }
48 }
49
50 pub fn create_bridge(&self) -> OneshotBridge<T> {
52 match self.reach {
53 Reach::Public => {
54 let held_bridge = self.get_held_bridge();
55 held_bridge.fork()
56 }
57 Reach::Private => (self.spawn_bridge_fn)(),
58 }
59 }
60}
61
62impl<T> Clone for OneshotProviderState<T>
63where
64 T: Oneshot,
65{
66 fn clone(&self) -> Self {
67 Self {
68 id: self.id,
69 spawn_bridge_fn: self.spawn_bridge_fn.clone(),
70 reach: self.reach,
71 held_bridge: self.held_bridge.clone(),
72 }
73 }
74}
75
76impl<T> PartialEq for OneshotProviderState<T>
77where
78 T: Oneshot,
79{
80 fn eq(&self, rhs: &Self) -> bool {
81 self.id == rhs.id
82 }
83}
84
85#[function_component]
89pub fn OneshotProvider<T, C = Bincode>(props: &WorkerProviderProps) -> Html
90where
91 T: Oneshot + 'static,
92 T::Input: Serialize + for<'de> Deserialize<'de> + 'static,
93 T::Output: Serialize + for<'de> Deserialize<'de> + 'static,
94 C: Codec + 'static,
95{
96 let WorkerProviderProps {
97 children,
98 path,
99 lazy,
100 module,
101 reach,
102 } = props.clone();
103
104 let spawn_bridge_fn: Rc<dyn Fn() -> OneshotBridge<T>> = {
106 let path = path.clone();
107 Rc::new(move || {
108 OneshotSpawner::<T>::new()
109 .as_module(module)
110 .encoding::<C>()
111 .spawn(&path)
112 })
113 };
114
115 let state = {
116 use_memo((path, lazy, reach), move |(_path, lazy, reach)| {
117 let state = OneshotProviderState::<T> {
118 id: get_next_id(),
119 spawn_bridge_fn,
120 reach: *reach,
121 held_bridge: Rc::default(),
122 };
123
124 if *reach == Reach::Public && !*lazy {
125 state.get_held_bridge();
126 }
127 state
128 })
129 };
130
131 html! {
132 <ContextProvider<OneshotProviderState<T>> context={(*state).clone()}>
133 {children}
134 </ContextProvider<OneshotProviderState<T>>>
135 }
136}