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