1use {
2 crate::{
3 error::Error,
4 ext,
5 invoke::{Options, ToStringValue},
6 },
7 js_sys::JsString,
8 serde::Serialize,
9 wasm_bindgen::prelude::*,
10 wasm_bindgen_futures::JsFuture,
11};
12
13#[rustfmt::skip]
14#[wasm_bindgen]
15extern "C" {
16 #[wasm_bindgen(thread_local_v2, static_string)]
17 static EMIT: JsString = "plugin:event|emit";
18
19 #[wasm_bindgen(thread_local_v2, static_string)]
20 static EMIT_TO: JsString = "plugin:event|emit_to";
21}
22
23#[cfg_attr(feature = "serde", doc = "```")]
32#[cfg_attr(not(feature = "serde"), doc = "```ignore")]
33#[cfg_attr(feature = "serde", doc = "```")]
42#[cfg_attr(not(feature = "serde"), doc = "```ignore")]
43#[inline]
65pub async fn emit<E, P>(event: E, payload: &P) -> Result<(), Error>
66where
67 E: ToStringValue,
68 P: Serialize + ?Sized,
69{
70 let event = event.to_string_value();
71
72 let payload = serde_wasm_bindgen::to_value(&payload).map_err(|e| Error(JsValue::from(e)))?;
73
74 invoke_emit(None, event.as_ref(), &payload)
75 .await
76 .map_err(Error)?;
77
78 Ok(())
79}
80
81#[cfg_attr(feature = "serde", doc = "```")]
90#[cfg_attr(not(feature = "serde"), doc = "```ignore")]
91#[inline]
100pub async fn emit_to<S, E, P>(target: EventTarget<S>, event: E, payload: &P) -> Result<(), Error>
101where
102 S: ToStringValue,
103 E: ToStringValue,
104 P: Serialize + ?Sized,
105{
106 let target = target.map(|s| s.to_string_value());
107 let target = target.as_ref().map(|s| s.as_ref());
108 let event = event.to_string_value();
109
110 let payload = serde_wasm_bindgen::to_value(&payload).map_err(|e| Error(JsValue::from(e)))?;
111
112 invoke_emit(Some(target), event.as_ref(), &payload)
113 .await
114 .map_err(Error)?;
115
116 Ok(())
117}
118
119#[inline]
120fn invoke_emit(
121 target: Option<EventTarget<&JsValue>>,
122 event: &JsValue,
123 payload: &JsValue,
124) -> JsFuture {
125 let cmd = if target.is_none() { &EMIT } else { &EMIT_TO };
126
127 let (kind, label) = match target {
128 None => (0, &JsValue::UNDEFINED),
129 Some(target) => match target {
130 EventTarget::Any => (1, &JsValue::UNDEFINED),
131 EventTarget::AnyLabel(s) => (2, s),
132 EventTarget::App => (3, &JsValue::UNDEFINED),
133 EventTarget::Window(s) => (4, s),
134 EventTarget::Webview(s) => (5, s),
135 EventTarget::WebviewWindow(s) => (6, s),
136 },
137 };
138
139 let cmd = cmd.with(|s| JsValue::from(s));
140 let args = ext::eargs(event, payload, kind, label);
141 JsFuture::from(ext::invoke(&cmd, &args, Options::empty()))
142}
143
144pub enum EventTarget<S> {
146 Any,
147 AnyLabel(S),
148 App,
149 Window(S),
150 Webview(S),
151 WebviewWindow(S),
152}
153
154impl<S> EventTarget<S> {
155 #[inline]
156 pub fn from_string(s: S) -> Self {
157 Self::AnyLabel(s)
158 }
159
160 #[inline]
161 pub fn as_ref(&self) -> EventTarget<&S> {
162 match self {
163 Self::Any => EventTarget::Any,
164 Self::AnyLabel(s) => EventTarget::AnyLabel(s),
165 Self::App => EventTarget::App,
166 Self::Window(s) => EventTarget::Window(s),
167 Self::Webview(s) => EventTarget::Webview(s),
168 Self::WebviewWindow(s) => EventTarget::WebviewWindow(s),
169 }
170 }
171
172 #[inline]
173 pub fn map<F, T>(self, f: F) -> EventTarget<T>
174 where
175 F: FnOnce(S) -> T,
176 {
177 match self {
178 Self::Any => EventTarget::Any,
179 Self::AnyLabel(s) => EventTarget::AnyLabel(f(s)),
180 Self::App => EventTarget::App,
181 Self::Window(s) => EventTarget::Window(f(s)),
182 Self::Webview(s) => EventTarget::Webview(f(s)),
183 Self::WebviewWindow(s) => EventTarget::WebviewWindow(f(s)),
184 }
185 }
186}
187
188impl From<&str> for EventTarget<JsString> {
189 #[inline]
190 fn from(s: &str) -> Self {
191 Self::from_string(JsString::from(s))
192 }
193}