#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{error::Error, fmt::Display, sync::Arc};
#[cfg(feature = "build")]
mod build;
#[cfg(feature = "build")]
pub use build::*;
use dioxus::document::{self, Eval};
pub use dioxus_use_js_macro::use_js;
#[doc(hidden)]
pub use serde::Serialize as SerdeSerialize;
#[doc(hidden)]
pub use serde::de::DeserializeOwned as SerdeDeDeserializeOwned;
#[doc(hidden)]
pub use serde::de::Error as SerdeDeError;
#[doc(hidden)]
pub use serde_json::Error as SerdeJsonError;
#[doc(hidden)]
pub use serde_json::Value as SerdeJsonValue;
#[doc(hidden)]
pub use serde_json::from_value as serde_json_from_value;
#[doc(hidden)]
pub const __CALLBACK_SEND_VALIDATION_MSG: &str =
"Callbacks should always send back a value that is an array of three.";
#[doc(hidden)]
pub const __RESULT_SEND_VALIDATION_MSG: &str =
"Result should always send back a value that is an array of two.";
#[doc(hidden)]
pub const __INDEX_VALIDATION_MSG: &str = "The index value was an unexpected type";
#[doc(hidden)]
pub const __BAD_CALL_MSG: &str = "Should only attempt to call known actions.";
#[doc(hidden)]
pub const __BAD_VOID_RETURN: &str =
"A function that should return no value instead returned a value";
#[doc(hidden)]
pub const __UNEXPECTED_CALLBACK_TYPE: &str = "The callback was called with the wrong type";
fn _send_sync_error_assert() {
fn is_send<T: Send>(_: &T) {}
fn is_sync<T: Sync>(_: &T) {}
fn is_error<T: Error>(_: &T) {}
let o: JsError = JsError::Threw { func: "" };
is_send(&o);
is_sync(&o);
is_error(&o);
}
#[derive(Debug, Clone)]
pub enum JsError {
Eval {
func: &'static str,
error: Arc<dioxus::document::EvalError>,
},
Threw {
func: &'static str,
},
}
impl std::fmt::Display for JsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
JsError::Eval { func: name, error } => {
write!(f, "JavaScript function '{}' eval error: {}", name, error)
}
JsError::Threw { func: name } => {
write!(
f,
"JavaScript function '{}' threw an error during execution",
name
)
}
}
}
}
impl std::error::Error for JsError {}
#[derive(
serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq, Hash,
)]
pub struct JsValue(Arc<JsValueInner>);
#[derive(
serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq, Hash,
)]
struct JsValueInner(String);
impl JsValue {
#[deprecated(note = "This constructor is for internal use only. Do not use directly.")]
#[doc(hidden)]
pub fn internal_create(id: String) -> Self {
Self(Arc::new(JsValueInner(id)))
}
#[deprecated(note = "This is for internal use only. Do not use directly.")]
#[doc(hidden)]
pub fn internal_get(&self) -> &str {
self.0.0.as_str()
}
}
impl Display for JsValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Value in js: window[\"{}\"]", self.0.0)
}
}
impl Drop for JsValueInner {
fn drop(&mut self) {
let object_name = std::mem::take(&mut self.0);
dioxus::core::spawn_forever(async move {
let eval =
dioxus::document::eval(&format!("delete window[\"{object_name}\"];return null;"));
if let Err(error) = eval.await {
dioxus::logger::tracing::error!(
"Failed to clean up JavaScript object `window[\"{object_name}\"]`. Error: {error}"
);
} else {
dioxus::logger::tracing::trace!(
"Cleaned up JavaScript object `window[\"{object_name}\"]`."
);
}
});
}
}
#[doc(hidden)]
pub struct SignalDrop(String);
impl SignalDrop {
pub fn new(invocation_id: String) -> Self {
SignalDrop(invocation_id)
}
}
impl Drop for SignalDrop {
fn drop(&mut self) {
let invocation_id = std::mem::take(&mut self.0);
dioxus::core::spawn_forever(async move {
let eval =
dioxus::document::eval(&format!("let i=\"{invocation_id}d\";let p=window[i];delete window[i];p();dioxus.close();return null;"));
if let Err(error) = eval.await {
dioxus::logger::tracing::error!(
"Failed to notify of drop for invocation `window[\"{invocation_id}d\"]`. Error: {error}"
);
} else {
dioxus::logger::tracing::trace!(
"Notified of drop for invocation `window[\"{invocation_id}d\"]`."
);
}
});
}
}
#[doc(hidden)]
#[derive(Clone)]
pub struct CallbackResponder(Arc<CallbackResponderInner>);
struct CallbackResponderInner(Eval);
impl CallbackResponder {
pub fn new(invocation_id: &str) -> Self {
CallbackResponder(Arc::new(CallbackResponderInner(dioxus::document::eval(
&format!(
"while(true){{let r=await dioxus.recv();if(!Array.isArray(r))break;let f=window[\"{invocation_id}\"];if(f==null)break;let i=r[0],o=r[1],d=r[2],x=f[i];delete f[i];if(o)x[0](d);else x[1](d);}}",
),
))))
}
pub fn respond<T: serde::Serialize>(&self, request_id: u64, is_ok: bool, data: T) {
let payload = (request_id, is_ok, data);
let result = self.0.0.send(payload);
if let Err(e) = result {
dioxus::logger::tracing::error!(
"Failed to send callback response for invocation '{}' request '{}': {}",
request_id,
is_ok,
e
);
}
}
}
impl Drop for CallbackResponderInner {
fn drop(&mut self) {
let result = self.0.send(serde_json::Value::Null);
if let Err(e) = result {
dioxus::logger::tracing::error!("Failed to shut down callback responder: {}", e);
}
}
}
#[doc(hidden)]
pub struct PendingFuture;
impl Future for PendingFuture {
type Output = ();
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Pending
}
}