1use std::{
6 pin::Pin,
7 task::{Context, Poll},
8};
9
10use axum::extract::ws::{Message, WebSocket};
11use futures_util::{Future, Sink, Stream, StreamExt};
12use pin_project_lite::pin_project;
13use wsdom_core::Browser;
14
15pin_project! {
16 pub struct ToBrowserFuture<Fut: Future> {
18 #[pin] ws: WebSocket,
19 #[pin] fut: Fut,
20 browser: Browser,
21 output: Option<Fut::Output>
22 }
23}
24
25impl<Fut> Future for ToBrowserFuture<Fut>
26where
27 Fut: Future,
28{
29 type Output = Output<Fut::Output>;
30
31 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
32 let mut this = self.project();
33 match this.ws.as_mut().poll_next(cx) {
34 Poll::Ready(Some(Ok(Message::Text(message)))) => {
35 this.browser.receive_incoming_message(message);
36 }
37 Poll::Ready(None | Some(Ok(Message::Close(_)))) => {
38 return Poll::Ready(Output::ConnectionClosed);
39 }
40 Poll::Ready(Some(Ok(_))) => {}
41 Poll::Ready(Some(Err(e))) => {
42 return Poll::Ready(Output::AxumError(e));
43 }
44 Poll::Pending => {}
45 }
46 match this.ws.as_mut().poll_ready(cx) {
47 Poll::Ready(Ok(_)) => {
48 match this.browser.poll_next_unpin(cx) {
49 Poll::Ready(Some(message)) => {
50 match this.ws.as_mut().start_send(Message::Text(message)) {
51 Ok(_) => {}
52 Err(e) => return Poll::Ready(Output::AxumError(e)),
53 }
54 }
55 Poll::Ready(None) => {
56 if let Some(err) = this.browser.take_error() {
57 return Poll::Ready(Output::WsdomError(err));
58 } else {
59 return Poll::Pending;
60 }
61 }
62 Poll::Pending => {}
63 }
64 match this.ws.as_mut().poll_flush(cx) {
65 Poll::Ready(Err(e)) => {
66 return Poll::Ready(Output::AxumError(e));
67 }
68 Poll::Ready(Ok(_)) => {
69 if let Some(output) = this.output.take() {
70 return Poll::Ready(Output::Done(output));
71 }
72 }
73 _ => {}
74 }
75 }
76 Poll::Ready(Err(e)) => {
77 return Poll::Ready(Output::AxumError(e));
78 }
79 Poll::Pending => {}
80 }
81 if this.output.is_none() {
82 if let Poll::Ready(t) = this.fut.poll(cx) {
83 *this.output = Some(t);
84 }
85 }
86 Poll::Pending
87 }
88}
89
90pub enum Output<T> {
92 Done(T),
94 ConnectionClosed,
96 AxumError(axum::Error),
98 WsdomError(wsdom_core::Error),
100}
101
102#[must_use = "the return type is a Future and should be .awaited"]
124pub fn socket_to_browser<Func, Fut>(ws: WebSocket, f: Func) -> ToBrowserFuture<Fut>
125where
126 Func: FnOnce(Browser) -> Fut,
127 Fut: Future,
128{
129 let browser = Browser::new();
130 ToBrowserFuture {
131 fut: f(browser.clone()),
132 ws,
133 browser,
134 output: None,
135 }
136}