1use std::{
2 cell::{Cell, RefCell},
3 collections::HashMap,
4 pin::Pin,
5 rc::Rc,
6 task::{Context, Poll},
7};
8
9use futures_channel::{mpsc, oneshot};
10use futures_core::{future::LocalBoxFuture, Future, Stream};
11use futures_util::{future, FutureExt, StreamExt};
12use js_sys::Array;
13use serde::Serialize;
14
15use crate::{port::Port, Dispatcher, MessageHeader};
16
17#[doc(hidden)]
18pub trait Client {
19 type Response;
20}
21
22#[doc(hidden)]
23pub type CallbackMap<Response> = HashMap<u32, oneshot::Sender<(Response, Array)>>;
24
25#[doc(hidden)]
26pub type StreamCallbackMap<Response> = HashMap<u32, mpsc::UnboundedSender<(Response, Array)>>;
27
28#[doc(hidden)]
31pub struct State<Response> {
32 pub callbacks: Rc<RefCell<CallbackMap<Response>>>,
33 pub stream_callbacks: Rc<RefCell<StreamCallbackMap<Response>>>,
34 pub port: Port,
35 pub listener: Rc<gloo_events::EventListener>,
36 pub dispatcher: Dispatcher,
37 pub sequence: Rc<Cell<u32>>,
38}
39
40impl<Response> Clone for State<Response> {
41 fn clone(&self) -> Self {
42 Self {
43 callbacks: self.callbacks.clone(),
44 stream_callbacks: self.stream_callbacks.clone(),
45 port: self.port.clone(),
46 listener: self.listener.clone(),
47 dispatcher: self.dispatcher.clone(),
48 sequence: self.sequence.clone(),
49 }
50 }
51}
52
53impl<Response: 'static> State<Response> {
54 pub fn send(&self, request: &impl Serialize, post_args: &Array, transfer_args: &Array) -> u32 {
56 let sequence = self.sequence.get();
57 self.sequence.set(sequence.wrapping_add(1));
58 crate::post_message(
59 &self.port,
60 MessageHeader::Request(sequence),
61 request,
62 post_args,
63 transfer_args,
64 );
65 sequence
66 }
67
68 pub fn request<T: 'static>(
70 &self,
71 sequence: u32,
72 decode: impl FnOnce(Response, Array) -> T + 'static,
73 ) -> RequestFuture<T> {
74 let (response_tx, response_rx) = oneshot::channel();
75 self.callbacks.borrow_mut().insert(sequence, response_tx);
76 let result = response_rx.map(move |received| {
77 let (response, js_values) = received.expect("web_rpc: the response channel closed");
78 decode(response, js_values)
79 });
80 let callbacks = self.callbacks.clone();
81 let port = self.port.clone();
82 RequestFuture {
83 result: future::select(result.boxed_local(), self.dispatcher.clone())
84 .map(|selected| match selected {
85 future::Either::Left((result, _)) => result,
86 future::Either::Right(_) => {
87 unreachable!(
88 "web_rpc: the dispatcher completed while a request was pending"
89 )
90 }
91 })
92 .boxed_local(),
93 _listener: self.listener.clone(),
94 abort: AbortOnDrop::new(move || {
95 callbacks.borrow_mut().remove(&sequence);
96 crate::post_header(&port, MessageHeader::Abort(sequence));
97 }),
98 }
99 }
100
101 pub fn stream<T: 'static>(
103 &self,
104 sequence: u32,
105 mut decode: impl FnMut(Response, Array) -> T + 'static,
106 ) -> StreamReceiver<T> {
107 let (item_tx, item_rx) = mpsc::unbounded();
108 self.stream_callbacks.borrow_mut().insert(sequence, item_tx);
109 let items = item_rx.map(move |(response, js_values)| decode(response, js_values));
110 let stream_callbacks = self.stream_callbacks.clone();
111 let port = self.port.clone();
112 StreamReceiver {
113 items: Box::pin(items),
114 dispatcher: self.dispatcher.clone(),
115 _listener: self.listener.clone(),
116 abort: AbortOnDrop::new(move || {
117 stream_callbacks.borrow_mut().remove(&sequence);
118 crate::post_header(&port, MessageHeader::Abort(sequence));
119 }),
120 }
121 }
122}
123
124struct AbortOnDrop {
126 active: bool,
127 abort: Box<dyn Fn()>,
128}
129
130impl AbortOnDrop {
131 fn new(abort: impl Fn() + 'static) -> Self {
132 Self {
133 active: true,
134 abort: Box::new(abort),
135 }
136 }
137
138 fn fire(&mut self) {
139 if self.active {
140 self.active = false;
141 (self.abort)();
142 }
143 }
144
145 fn disarm(&mut self) {
146 self.active = false;
147 }
148}
149
150impl Drop for AbortOnDrop {
151 fn drop(&mut self) {
152 self.fire();
153 }
154}
155
156#[must_use = "Either await this future or remove the return type from the RPC method"]
159pub struct RequestFuture<T: 'static> {
160 result: LocalBoxFuture<'static, T>,
161 _listener: Rc<gloo_events::EventListener>,
162 abort: AbortOnDrop,
163}
164
165impl<T> Future for RequestFuture<T> {
166 type Output = T;
167
168 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
169 let polled = self.result.poll_unpin(cx);
170 if polled.is_ready() {
171 self.abort.disarm();
172 }
173 polled
174 }
175}
176
177pub struct StreamReceiver<T: 'static> {
181 items: Pin<Box<dyn Stream<Item = T>>>,
182 dispatcher: Dispatcher,
183 _listener: Rc<gloo_events::EventListener>,
184 abort: AbortOnDrop,
185}
186
187impl<T> StreamReceiver<T> {
188 pub fn close(&mut self) {
191 self.abort.fire();
192 }
193}
194
195impl<T> Stream for StreamReceiver<T> {
196 type Item = T;
197
198 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
199 match self.items.as_mut().poll_next(cx) {
200 Poll::Ready(Some(item)) => Poll::Ready(Some(item)),
201 Poll::Ready(None) => {
202 self.abort.disarm();
203 Poll::Ready(None)
204 }
205 Poll::Pending => match self.dispatcher.poll_unpin(cx) {
206 Poll::Ready(_) => {
207 unreachable!("web_rpc: the dispatcher completed while a stream was open")
208 }
209 Poll::Pending => Poll::Pending,
210 },
211 }
212 }
213}