This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew_agent/worker/
bridge.rs

1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::fmt;
4use std::marker::PhantomData;
5use std::rc::{Rc, Weak};
6
7use serde::{Deserialize, Serialize};
8
9use super::handler_id::HandlerId;
10use super::messages::ToWorker;
11use super::native_worker::NativeWorkerExt;
12use super::traits::Worker;
13use super::{Callback, Shared};
14use crate::codec::Codec;
15
16pub(crate) type ToWorkerQueue<W> = Vec<ToWorker<W>>;
17pub(crate) type CallbackMap<W> = HashMap<HandlerId, Weak<dyn Fn(<W as Worker>::Output)>>;
18
19struct WorkerBridgeInner<W>
20where
21    W: Worker,
22{
23    // When worker is loaded, queue becomes None.
24    pending_queue: Shared<Option<ToWorkerQueue<W>>>,
25    callbacks: Shared<CallbackMap<W>>,
26    post_msg: Rc<dyn Fn(ToWorker<W>)>,
27}
28
29impl<W> fmt::Debug for WorkerBridgeInner<W>
30where
31    W: Worker,
32{
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        f.write_str("WorkerBridgeInner<_>")
35    }
36}
37
38impl<W> WorkerBridgeInner<W>
39where
40    W: Worker,
41{
42    /// Send a message to the worker, queuing the message if necessary
43    fn send_message(&self, msg: ToWorker<W>) {
44        let mut pending_queue = self.pending_queue.borrow_mut();
45
46        match pending_queue.as_mut() {
47            Some(m) => {
48                m.push(msg);
49            }
50            None => {
51                (self.post_msg)(msg);
52            }
53        }
54    }
55}
56
57impl<W> Drop for WorkerBridgeInner<W>
58where
59    W: Worker,
60{
61    fn drop(&mut self) {
62        let destroy = ToWorker::Destroy;
63        self.send_message(destroy);
64    }
65}
66
67/// A connection manager for components interaction with workers.
68pub struct WorkerBridge<W>
69where
70    W: Worker,
71{
72    inner: Rc<WorkerBridgeInner<W>>,
73    id: HandlerId,
74    _worker: PhantomData<W>,
75    _cb: Option<Rc<dyn Fn(W::Output)>>,
76}
77
78impl<W> WorkerBridge<W>
79where
80    W: Worker,
81{
82    fn init(&self) {
83        self.inner.send_message(ToWorker::Connected(self.id));
84    }
85
86    pub(crate) fn new<CODEC>(
87        id: HandlerId,
88        native_worker: web_sys::Worker,
89        pending_queue: Rc<RefCell<Option<ToWorkerQueue<W>>>>,
90        callbacks: Rc<RefCell<CallbackMap<W>>>,
91        callback: Option<Callback<W::Output>>,
92    ) -> Self
93    where
94        CODEC: Codec,
95        W::Input: Serialize + for<'de> Deserialize<'de>,
96    {
97        let post_msg = move |msg: ToWorker<W>| native_worker.post_packed_message::<_, CODEC>(msg);
98
99        let self_ = Self {
100            inner: WorkerBridgeInner {
101                pending_queue,
102                callbacks,
103                post_msg: Rc::new(post_msg),
104            }
105            .into(),
106            id,
107            _worker: PhantomData,
108            _cb: callback,
109        };
110        self_.init();
111
112        self_
113    }
114
115    /// Send a message to the current worker.
116    pub fn send(&self, msg: W::Input) {
117        let msg = ToWorker::ProcessInput(self.id, msg);
118        self.inner.send_message(msg);
119    }
120
121    /// Forks the bridge with a different callback.
122    ///
123    /// This creates a new [HandlerID] that helps the worker to differentiate bridges.
124    pub fn fork<F>(&self, cb: Option<F>) -> Self
125    where
126        F: 'static + Fn(W::Output),
127    {
128        let cb = cb.map(|m| Rc::new(m) as Rc<dyn Fn(W::Output)>);
129        let handler_id = HandlerId::new();
130
131        if let Some(cb_weak) = cb.as_ref().map(Rc::downgrade) {
132            self.inner
133                .callbacks
134                .borrow_mut()
135                .insert(handler_id, cb_weak);
136        }
137
138        let self_ = Self {
139            inner: self.inner.clone(),
140            id: handler_id,
141            _worker: PhantomData,
142            _cb: cb,
143        };
144        self_.init();
145
146        self_
147    }
148}
149
150impl<W> Drop for WorkerBridge<W>
151where
152    W: Worker,
153{
154    fn drop(&mut self) {
155        let disconnected = ToWorker::Disconnected(self.id);
156        self.inner.send_message(disconnected);
157    }
158}
159
160impl<W> fmt::Debug for WorkerBridge<W>
161where
162    W: Worker,
163{
164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165        f.write_str("WorkerBridge<_>")
166    }
167}
168
169impl<W> PartialEq for WorkerBridge<W>
170where
171    W: Worker,
172{
173    fn eq(&self, rhs: &Self) -> bool {
174        self.id == rhs.id
175    }
176}