px_wsdom_core/interaction/
callback.rs1use core::{fmt::Write, marker::PhantomData, pin::Pin, task::Poll};
26
27use alloc::{borrow::ToOwned, boxed::Box, string::String};
28
29use crate::{
30 js::value::JsValue,
31 js_cast::JsCast,
32 link::{Browser, RetrievalState},
33 protocol::{DEL, GET, REP, SET},
34};
35
36pub struct Callback<E> {
43 arr_id: u64,
44 ret_id: u64,
45 browser: Browser,
46 consumed: usize,
47 _phantom: PhantomData<Pin<Box<E>>>,
48}
49
50impl<E: JsCast> futures_core::Stream for Callback<E> {
51 type Item = E;
52
53 fn poll_next(
54 self: Pin<&mut Self>,
55 cx: &mut core::task::Context<'_>,
56 ) -> Poll<Option<Self::Item>> {
57 let this = self.get_mut();
58 let mut link = this.browser.0.lock();
59 let ret_id = this.ret_id;
60 match link.retrievals.entry(ret_id) {
61 hashbrown::hash_map::Entry::Occupied(mut occ) => {
62 let state = occ.get_mut();
63
64 let new_waker = cx.waker();
65 if !state.waker.will_wake(new_waker) {
66 state.waker = new_waker.to_owned();
67 }
68
69 if state.times > this.consumed {
70 this.consumed += 1;
71 let val_id = link.get_new_id();
72 let arr_id = this.arr_id;
73 writeln!(
74 link.raw_commands_buf(),
75 "{SET}({val_id}, {GET}({arr_id}).shift());"
76 )
77 .unwrap();
78 link.wake_outgoing_lazy();
79 Poll::Ready(Some(JsCast::unchecked_from_js(JsValue {
80 id: val_id,
81 browser: this.browser.to_owned(),
82 })))
83 } else {
84 Poll::Pending
85 }
86 }
87 hashbrown::hash_map::Entry::Vacant(vac) => {
88 vac.insert(RetrievalState {
89 waker: cx.waker().to_owned(),
90 last_value: String::new(),
91 times: 0,
92 });
93 Poll::Pending
94 }
95 }
96 }
97}
98impl<E> Drop for Callback<E> {
99 fn drop(&mut self) {
100 let mut link = self.browser.0.lock();
101 let ret_id = self.ret_id;
102 link.retrievals.remove(&ret_id);
103 let arr_id = self.arr_id;
104 writeln!(link.raw_commands_buf(), "{DEL}({arr_id});").unwrap();
105 }
106}
107
108pub fn new_callback<E>(browser: &Browser) -> (Callback<E>, JsValue) {
113 let mut link = browser.0.lock();
114 let arr_id = link.get_new_id();
115 let ret_id = link.get_new_id();
116 let func_id = link.get_new_id();
117 let func = JsValue {
118 browser: browser.to_owned(),
119 id: func_id,
120 };
121 writeln!(link.raw_commands_buf(),
122"{SET}({arr_id}, []); {SET}({func_id}, function(e) {{ {GET}({arr_id}).push(e); {REP}({ret_id}, 0) }});").unwrap();
123 link.wake_outgoing_lazy();
124 let callback = Callback {
125 browser: browser.to_owned(),
126 ret_id,
127 arr_id,
128 consumed: 0,
129 _phantom: PhantomData,
130 };
131 (callback, func)
132}