use {
crate::{error::Error, ext, invoke::Options, string::ToStringValue},
js_sys::{JsString, Promise},
serde::Serialize,
std::{
pin::Pin,
task::{Context, Poll},
},
wasm_bindgen::prelude::*,
wasm_bindgen_futures::JsFuture,
};
#[rustfmt::skip]
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(thread_local_v2, static_string)]
static EMIT: JsString = "plugin:event|emit";
#[wasm_bindgen(thread_local_v2, static_string)]
static EMIT_TO: JsString = "plugin:event|emit_to";
}
pub(crate) mod api {
use super::*;
#[cfg_attr(feature = "serde", doc = "```")]
#[cfg_attr(not(feature = "serde"), doc = "```ignore")]
#[cfg_attr(feature = "serde", doc = "```")]
#[cfg_attr(not(feature = "serde"), doc = "```ignore")]
#[inline]
pub fn emit<E, P>(event: E, payload: &P) -> Result<Emit<E::Js>, Error>
where
E: ToStringValue,
P: Serialize + ?Sized,
{
let event = event.to_string_value();
let payload =
serde_wasm_bindgen::to_value(&payload).map_err(|e| Error(JsValue::from(e)))?;
let target = None;
Ok(Emit {
event,
payload,
target,
})
}
}
pub struct Emit<E, T = JsValue> {
event: E,
payload: JsValue,
target: Option<EventTarget<T>>,
}
impl<E> Emit<E> {
#[cfg_attr(feature = "serde", doc = "```")]
#[cfg_attr(not(feature = "serde"), doc = "```ignore")]
#[inline]
pub fn to<S>(self, target: EventTarget<S>) -> Emit<E, S::Js>
where
S: ToStringValue,
{
let event = self.event;
let payload = self.payload;
let target = Some(target.map(|s| s.to_string_value()));
Emit {
event,
payload,
target,
}
}
}
pub struct EmitFuture(JsFuture);
impl EmitFuture {
#[inline]
pub fn into_future(self) -> JsFuture {
self.0
}
}
impl Future for EmitFuture {
type Output = Result<JsValue, Error>;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let me = self.get_mut();
Pin::new(&mut me.0).poll(cx).map_err(Error)
}
}
impl<E, T> IntoFuture for Emit<E, T>
where
E: AsRef<JsValue>,
T: AsRef<JsValue>,
{
type Output = Result<JsValue, Error>;
type IntoFuture = EmitFuture;
#[inline]
fn into_future(self) -> Self::IntoFuture {
let target = self.target.as_ref().map(|s| s.as_ref().map(|s| s.as_ref()));
let promise = invoke_emit(target, self.event.as_ref(), &self.payload);
EmitFuture(JsFuture::from(promise))
}
}
fn invoke_emit(
target: Option<EventTarget<&JsValue>>,
event: &JsValue,
payload: &JsValue,
) -> Promise {
let cmd = if target.is_none() { &EMIT } else { &EMIT_TO };
let (kind, label) = match target {
None => (0, &JsValue::UNDEFINED),
Some(target) => match target {
EventTarget::Any => (1, &JsValue::UNDEFINED),
EventTarget::AnyLabel(s) => (2, s),
EventTarget::App => (3, &JsValue::UNDEFINED),
EventTarget::Window(s) => (4, s),
EventTarget::Webview(s) => (5, s),
EventTarget::WebviewWindow(s) => (6, s),
},
};
let cmd = cmd.with(|s| JsValue::from(s));
let args = ext::eargs(event, payload, kind, label);
ext::invoke(&cmd, &args, Options::empty())
}
pub enum EventTarget<S> {
Any,
AnyLabel(S),
App,
Window(S),
Webview(S),
WebviewWindow(S),
}
impl<S> EventTarget<S> {
#[inline]
pub fn from_string(s: S) -> Self {
Self::AnyLabel(s)
}
#[inline]
pub fn as_ref(&self) -> EventTarget<&S> {
match self {
Self::Any => EventTarget::Any,
Self::AnyLabel(s) => EventTarget::AnyLabel(s),
Self::App => EventTarget::App,
Self::Window(s) => EventTarget::Window(s),
Self::Webview(s) => EventTarget::Webview(s),
Self::WebviewWindow(s) => EventTarget::WebviewWindow(s),
}
}
#[inline]
pub fn map<F, T>(self, f: F) -> EventTarget<T>
where
F: FnOnce(S) -> T,
{
match self {
Self::Any => EventTarget::Any,
Self::AnyLabel(s) => EventTarget::AnyLabel(f(s)),
Self::App => EventTarget::App,
Self::Window(s) => EventTarget::Window(f(s)),
Self::Webview(s) => EventTarget::Webview(f(s)),
Self::WebviewWindow(s) => EventTarget::WebviewWindow(f(s)),
}
}
}
impl From<&str> for EventTarget<JsString> {
#[inline]
fn from(s: &str) -> Self {
Self::from_string(JsString::from(s))
}
}