1use std::{
2 collections::HashMap,
3 sync::{Arc, Mutex},
4 task::{Poll, Waker},
5};
6
7#[derive(Clone, Debug)]
32pub struct Browser(pub(crate) Arc<Mutex<BrowserInternal>>);
33
34impl Browser {
35 pub fn new() -> Self {
39 let link = BrowserInternal {
40 retrievals: HashMap::new(),
41 last_id: 1,
42 commands_buf: String::new(),
43 outgoing_waker: None,
44 dead: ErrorState::NoError,
45 };
46 Self(Arc::new(Mutex::new(link)))
47 }
48 pub fn receive_incoming_message(&self, message: String) {
53 self.0.lock().unwrap().receive(message);
54 }
55 pub fn take_error(&self) -> Option<Error> {
60 let mut link = self.0.lock().unwrap();
61 match std::mem::replace(&mut link.dead, ErrorState::ErrorTaken) {
62 ErrorState::NoError => {
63 link.dead = ErrorState::NoError;
64 None
65 }
66 ErrorState::Error(e) => Some(e),
67 ErrorState::ErrorTaken => None,
68 }
69 }
70}
71
72impl futures_core::Stream for Browser {
74 type Item = String;
75
76 fn poll_next(
77 self: std::pin::Pin<&mut Self>,
78 cx: &mut std::task::Context<'_>,
79 ) -> Poll<Option<Self::Item>> {
80 let this = self.get_mut();
81 let mut link = this.0.lock().unwrap();
82
83 if !matches!(&link.dead, ErrorState::NoError) {
84 return Poll::Ready(None);
85 }
86
87 let new_waker = cx.waker();
88 if !link
89 .outgoing_waker
90 .as_ref()
91 .is_some_and(|w| new_waker.will_wake(w))
92 {
93 link.outgoing_waker = Some(new_waker.to_owned());
94 }
95 if !link.commands_buf.is_empty() {
96 Poll::Ready(Some(std::mem::take(&mut link.commands_buf)))
97 } else {
98 Poll::Pending
99 }
100 }
101}
102
103#[derive(Debug)]
104pub struct BrowserInternal {
105 pub(crate) retrievals: HashMap<u64, RetrievalState>,
106 last_id: u64,
107 commands_buf: String,
108 outgoing_waker: Option<Waker>,
109 dead: ErrorState,
110}
111
112#[derive(Debug)]
116pub enum Error {
117 CommandSerialize(std::fmt::Error),
118 DataDeserialize(serde_json::Error),
119}
120#[derive(Debug)]
121enum ErrorState {
122 NoError,
123 Error(Error),
124 ErrorTaken,
125}
126
127#[derive(Debug)]
128pub(crate) struct RetrievalState {
129 pub(crate) waker: Waker,
130 pub(crate) last_value: String,
131 pub(crate) times: usize,
132}
133
134impl BrowserInternal {
135 pub fn receive(&mut self, message: String) {
136 match message
137 .split_once(':')
138 .and_then(|(id, _)| id.parse::<u64>().ok())
139 {
140 Some(id) => match self.retrievals.get_mut(&id) {
141 Some(s) => {
142 s.times += 1;
143 s.last_value = message;
144 s.waker.wake_by_ref();
145 }
146 _ => {}
147 },
148 None => {}
149 }
150 }
151 pub fn raw_commands_buf(&mut self) -> &mut String {
152 &mut self.commands_buf
153 }
154 pub(crate) fn get_new_id(&mut self) -> u64 {
155 self.last_id += 1;
156 self.last_id
157 }
158 pub(crate) fn kill(&mut self, err: Error) {
159 if matches!(self.dead, ErrorState::NoError) {
160 self.dead = ErrorState::Error(err);
161 }
162 }
163 pub(crate) fn wake_outgoing(&mut self) {
164 if let Some(waker) = self.outgoing_waker.as_ref() {
165 waker.wake_by_ref();
166 }
167 }
168 pub(crate) fn wake_outgoing_lazy(&mut self) {
169 self.wake_outgoing();
170 }
171}
172
173struct InvalidReturn;
174impl std::fmt::Debug for InvalidReturn {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 f.debug_struct("InvalidReturn").finish()
177 }
178}
179impl std::fmt::Display for InvalidReturn {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 std::fmt::Debug::fmt(self, f)
182 }
183}
184impl std::error::Error for InvalidReturn {}