yew_agent/worker/
provider.rs1use std::any::type_name;
2use std::cell::RefCell;
3use std::fmt;
4use std::rc::Rc;
5
6use gloo_worker::Spawnable;
7use serde::{Deserialize, Serialize};
8use yew::prelude::*;
9
10use super::{Worker, WorkerBridge};
11use crate::reach::Reach;
12use crate::utils::get_next_id;
13use crate::{Bincode, Codec};
14
15#[derive(Debug, Properties, PartialEq, Clone)]
17pub struct WorkerProviderProps {
18 pub path: AttrValue,
20
21 #[prop_or(Reach::Public)]
25 pub reach: Reach,
26
27 #[prop_or(true)]
35 pub lazy: bool,
36
37 #[prop_or_default]
39 pub children: Html,
40}
41
42pub(crate) struct WorkerProviderState<W>
43where
44 W: Worker,
45{
46 id: usize,
47 spawn_bridge_fn: Rc<dyn Fn() -> WorkerBridge<W>>,
48 reach: Reach,
49 held_bridge: Rc<RefCell<Option<WorkerBridge<W>>>>,
50}
51
52impl<W> fmt::Debug for WorkerProviderState<W>
53where
54 W: Worker,
55{
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.write_str(type_name::<Self>())
58 }
59}
60
61impl<W> WorkerProviderState<W>
62where
63 W: Worker,
64 W::Output: 'static,
65{
66 fn get_held_bridge(&self) -> WorkerBridge<W> {
67 let mut held_bridge = self.held_bridge.borrow_mut();
68
69 match held_bridge.as_mut() {
70 Some(m) => m.clone(),
71 None => {
72 let bridge = (self.spawn_bridge_fn)();
73 *held_bridge = Some(bridge.clone());
74 bridge
75 }
76 }
77 }
78
79 pub fn create_bridge(&self, cb: Callback<W::Output>) -> WorkerBridge<W> {
81 match self.reach {
82 Reach::Public => {
83 let held_bridge = self.get_held_bridge();
84 held_bridge.fork(Some(move |m| cb.emit(m)))
85 }
86 Reach::Private => (self.spawn_bridge_fn)(),
87 }
88 }
89}
90
91impl<W> Clone for WorkerProviderState<W>
92where
93 W: Worker,
94{
95 fn clone(&self) -> Self {
96 Self {
97 id: self.id,
98 spawn_bridge_fn: self.spawn_bridge_fn.clone(),
99 reach: self.reach,
100 held_bridge: self.held_bridge.clone(),
101 }
102 }
103}
104
105impl<W> PartialEq for WorkerProviderState<W>
106where
107 W: Worker,
108{
109 fn eq(&self, rhs: &Self) -> bool {
110 self.id == rhs.id
111 }
112}
113
114#[function_component]
118pub fn WorkerProvider<W, C = Bincode>(props: &WorkerProviderProps) -> Html
119where
120 W: Worker + 'static,
121 W::Input: Serialize + for<'de> Deserialize<'de> + 'static,
122 W::Output: Serialize + for<'de> Deserialize<'de> + 'static,
123 C: Codec + 'static,
124{
125 let WorkerProviderProps {
126 children,
127 path,
128 lazy,
129 reach,
130 } = props.clone();
131
132 let spawn_bridge_fn: Rc<dyn Fn() -> WorkerBridge<W>> = {
134 let path = path.clone();
135 Rc::new(move || W::spawner().encoding::<C>().spawn(&path))
136 };
137
138 let state = {
139 use_memo((path, lazy, reach), move |(_path, lazy, reach)| {
140 let state = WorkerProviderState::<W> {
141 id: get_next_id(),
142 spawn_bridge_fn,
143 reach: *reach,
144 held_bridge: Rc::default(),
145 };
146
147 if *reach == Reach::Public && !*lazy {
148 state.get_held_bridge();
149 }
150 state
151 })
152 };
153
154 html! {
155 <ContextProvider<WorkerProviderState<W>> context={(*state).clone()}>
156 {children}
157 </ContextProvider<WorkerProviderState<W>>>
158 }
159}