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