1use {
6 crate::{error::Error, ext, invoke::Options, string::ToStringValue},
7 js_sys::{JsString, Promise},
8 serde::Serialize,
9 std::{
10 pin::Pin,
11 task::{Context, Poll},
12 },
13 wasm_bindgen::prelude::*,
14 wasm_bindgen_futures::JsFuture,
15};
16
17#[rustfmt::skip]
18#[wasm_bindgen]
19extern "C" {
20 #[wasm_bindgen(thread_local_v2, static_string)]
21 static EMIT: JsString = "plugin:event|emit";
22
23 #[wasm_bindgen(thread_local_v2, static_string)]
24 static EMIT_TO: JsString = "plugin:event|emit_to";
25}
26
27pub(crate) mod api {
28 use super::*;
29
30 #[cfg_attr(feature = "serde", doc = "```")]
39 #[cfg_attr(not(feature = "serde"), doc = "```ignore")]
40 #[cfg_attr(feature = "serde", doc = "```")]
49 #[cfg_attr(not(feature = "serde"), doc = "```ignore")]
50 #[inline]
90 pub fn emit<E, P>(event: E, payload: &P) -> Result<Emit<E::Js>, Error>
91 where
92 E: ToStringValue,
93 P: Serialize + ?Sized,
94 {
95 let event = event.to_string_value();
96 let payload =
97 serde_wasm_bindgen::to_value(&payload).map_err(|e| Error(JsValue::from(e)))?;
98 let target = None;
99
100 Ok(Emit {
101 event,
102 payload,
103 target,
104 })
105 }
106}
107
108pub struct Emit<E, T = JsValue> {
110 event: E,
111 payload: JsValue,
112 target: Option<EventTarget<T>>,
113}
114
115impl<E> Emit<E> {
116 #[cfg_attr(feature = "serde", doc = "```")]
125 #[cfg_attr(not(feature = "serde"), doc = "```ignore")]
126 #[inline]
135 pub fn to<S>(self, target: EventTarget<S>) -> Emit<E, S::Js>
136 where
137 S: ToStringValue,
138 {
139 let event = self.event;
140 let payload = self.payload;
141 let target = Some(target.map(|s| s.to_string_value()));
142
143 Emit {
144 event,
145 payload,
146 target,
147 }
148 }
149}
150
151pub struct EmitFuture(JsFuture);
153
154impl EmitFuture {
155 #[inline]
157 pub fn into_future(self) -> JsFuture {
158 self.0
159 }
160}
161
162impl Future for EmitFuture {
163 type Output = Result<JsValue, Error>;
164
165 #[inline]
166 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
167 let me = self.get_mut();
168 Pin::new(&mut me.0).poll(cx).map_err(Error)
169 }
170}
171
172impl<E, T> IntoFuture for Emit<E, T>
173where
174 E: AsRef<JsValue>,
175 T: AsRef<JsValue>,
176{
177 type Output = Result<JsValue, Error>;
178 type IntoFuture = EmitFuture;
179
180 #[inline]
181 fn into_future(self) -> Self::IntoFuture {
182 let target = self.target.as_ref().map(|s| s.as_ref().map(|s| s.as_ref()));
183 let promise = invoke_emit(target, self.event.as_ref(), &self.payload);
184 EmitFuture(JsFuture::from(promise))
185 }
186}
187
188fn invoke_emit(
189 target: Option<EventTarget<&JsValue>>,
190 event: &JsValue,
191 payload: &JsValue,
192) -> Promise {
193 let cmd = if target.is_none() { &EMIT } else { &EMIT_TO };
194
195 let (kind, label) = match target {
196 None => (0, &JsValue::UNDEFINED),
197 Some(target) => match target {
198 EventTarget::Any => (1, &JsValue::UNDEFINED),
199 EventTarget::AnyLabel(s) => (2, s),
200 EventTarget::App => (3, &JsValue::UNDEFINED),
201 EventTarget::Window(s) => (4, s),
202 EventTarget::Webview(s) => (5, s),
203 EventTarget::WebviewWindow(s) => (6, s),
204 },
205 };
206
207 let cmd = cmd.with(|s| JsValue::from(s));
208 let args = ext::eargs(event, payload, kind, label);
209 ext::invoke(&cmd, &args, Options::empty())
210}
211
212pub enum EventTarget<S> {
214 Any,
215 AnyLabel(S),
216 App,
217 Window(S),
218 Webview(S),
219 WebviewWindow(S),
220}
221
222impl<S> EventTarget<S> {
223 #[inline]
224 pub fn from_string(s: S) -> Self {
225 Self::AnyLabel(s)
226 }
227
228 #[inline]
229 pub fn as_ref(&self) -> EventTarget<&S> {
230 match self {
231 Self::Any => EventTarget::Any,
232 Self::AnyLabel(s) => EventTarget::AnyLabel(s),
233 Self::App => EventTarget::App,
234 Self::Window(s) => EventTarget::Window(s),
235 Self::Webview(s) => EventTarget::Webview(s),
236 Self::WebviewWindow(s) => EventTarget::WebviewWindow(s),
237 }
238 }
239
240 #[inline]
241 pub fn map<F, T>(self, f: F) -> EventTarget<T>
242 where
243 F: FnOnce(S) -> T,
244 {
245 match self {
246 Self::Any => EventTarget::Any,
247 Self::AnyLabel(s) => EventTarget::AnyLabel(f(s)),
248 Self::App => EventTarget::App,
249 Self::Window(s) => EventTarget::Window(f(s)),
250 Self::Webview(s) => EventTarget::Webview(f(s)),
251 Self::WebviewWindow(s) => EventTarget::WebviewWindow(f(s)),
252 }
253 }
254}
255
256impl From<&str> for EventTarget<JsString> {
257 #[inline]
258 fn from(s: &str) -> Self {
259 Self::from_string(JsString::from(s))
260 }
261}