use crate::{
api::ipc::{format_callback, format_callback_result, CallbackFn},
app::App,
Runtime, StateManager, Window,
};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use serialize_to_javascript::{default_template, Template};
use std::{future::Future, sync::Arc};
use tauri_macros::default_runtime;
pub type SetupHook<R> =
Box<dyn FnOnce(&mut App<R>) -> Result<(), Box<dyn std::error::Error>> + Send>;
pub type InvokeHandler<R> = dyn Fn(Invoke<R>) + Send + Sync + 'static;
pub type InvokeResponder<R> =
dyn Fn(Window<R>, InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static;
pub type OnPageLoad<R> = dyn Fn(Window<R>, PageLoadPayload) + Send + Sync + 'static;
#[derive(Template)]
#[default_template("../scripts/ipc.js")]
pub(crate) struct IpcJavascript<'a> {
pub(crate) isolation_origin: &'a str,
}
#[cfg(feature = "isolation")]
#[derive(Template)]
#[default_template("../scripts/isolation.js")]
pub(crate) struct IsolationJavascript<'a> {
pub(crate) isolation_src: &'a str,
pub(crate) style: &'a str,
}
#[derive(Debug, Clone, Deserialize)]
pub struct PageLoadPayload {
url: String,
}
impl PageLoadPayload {
pub fn url(&self) -> &str {
&self.url
}
}
#[derive(Debug, Deserialize)]
pub struct InvokePayload {
pub cmd: String,
#[serde(rename = "__tauriModule")]
#[doc(hidden)]
pub tauri_module: Option<String>,
pub callback: CallbackFn,
pub error: CallbackFn,
#[serde(flatten)]
pub inner: JsonValue,
}
#[default_runtime(crate::Wry, wry)]
#[derive(Debug)]
pub struct Invoke<R: Runtime> {
pub message: InvokeMessage<R>,
pub resolver: InvokeResolver<R>,
}
#[derive(Debug)]
pub struct InvokeError(JsonValue);
impl InvokeError {
#[inline(always)]
pub fn from_serde_json(error: serde_json::Error) -> Self {
Self(JsonValue::String(error.to_string()))
}
#[inline(always)]
pub fn from_anyhow(error: anyhow::Error) -> Self {
Self(JsonValue::String(format!("{error:#}")))
}
}
impl<T: Serialize> From<T> for InvokeError {
#[inline]
fn from(value: T) -> Self {
serde_json::to_value(value)
.map(Self)
.unwrap_or_else(Self::from_serde_json)
}
}
impl From<crate::Error> for InvokeError {
#[inline(always)]
fn from(error: crate::Error) -> Self {
Self(JsonValue::String(error.to_string()))
}
}
#[derive(Debug)]
pub enum InvokeResponse {
Ok(JsonValue),
Err(InvokeError),
}
impl InvokeResponse {
#[inline(always)]
pub fn into_result(self) -> Result<JsonValue, JsonValue> {
match self {
Self::Ok(v) => Ok(v),
Self::Err(e) => Err(e.0),
}
}
}
impl<T: Serialize> From<Result<T, InvokeError>> for InvokeResponse {
#[inline]
fn from(result: Result<T, InvokeError>) -> Self {
match result {
Ok(ok) => match serde_json::to_value(ok) {
Ok(value) => Self::Ok(value),
Err(err) => Self::Err(InvokeError::from_serde_json(err)),
},
Err(err) => Self::Err(err),
}
}
}
impl From<InvokeError> for InvokeResponse {
fn from(error: InvokeError) -> Self {
Self::Err(error)
}
}
#[default_runtime(crate::Wry, wry)]
#[derive(Debug)]
pub struct InvokeResolver<R: Runtime> {
window: Window<R>,
pub(crate) callback: CallbackFn,
pub(crate) error: CallbackFn,
}
impl<R: Runtime> InvokeResolver<R> {
pub(crate) fn new(window: Window<R>, callback: CallbackFn, error: CallbackFn) -> Self {
Self {
window,
callback,
error,
}
}
pub fn respond_async<T, F>(self, task: F)
where
T: Serialize,
F: Future<Output = Result<T, InvokeError>> + Send + 'static,
{
crate::async_runtime::spawn(async move {
Self::return_task(self.window, task, self.callback, self.error).await;
});
}
pub fn respond_async_serialized<F>(self, task: F)
where
F: Future<Output = Result<JsonValue, InvokeError>> + Send + 'static,
{
crate::async_runtime::spawn(async move {
let response = match task.await {
Ok(ok) => InvokeResponse::Ok(ok),
Err(err) => InvokeResponse::Err(err),
};
Self::return_result(self.window, response, self.callback, self.error)
});
}
pub fn respond<T: Serialize>(self, value: Result<T, InvokeError>) {
Self::return_result(self.window, value.into(), self.callback, self.error)
}
pub fn resolve<T: Serialize>(self, value: T) {
Self::return_result(self.window, Ok(value).into(), self.callback, self.error)
}
pub fn reject<T: Serialize>(self, value: T) {
Self::return_result(
self.window,
Result::<(), _>::Err(value.into()).into(),
self.callback,
self.error,
)
}
pub fn invoke_error(self, error: InvokeError) {
Self::return_result(self.window, error.into(), self.callback, self.error)
}
pub async fn return_task<T, F>(
window: Window<R>,
task: F,
success_callback: CallbackFn,
error_callback: CallbackFn,
) where
T: Serialize,
F: Future<Output = Result<T, InvokeError>> + Send + 'static,
{
let result = task.await;
Self::return_closure(window, || result, success_callback, error_callback)
}
pub(crate) fn return_closure<T: Serialize, F: FnOnce() -> Result<T, InvokeError>>(
window: Window<R>,
f: F,
success_callback: CallbackFn,
error_callback: CallbackFn,
) {
Self::return_result(window, f().into(), success_callback, error_callback)
}
pub(crate) fn return_result(
window: Window<R>,
response: InvokeResponse,
success_callback: CallbackFn,
error_callback: CallbackFn,
) {
(window.invoke_responder())(window, response, success_callback, error_callback);
}
}
pub fn window_invoke_responder<R: Runtime>(
window: Window<R>,
response: InvokeResponse,
success_callback: CallbackFn,
error_callback: CallbackFn,
) {
let callback_string =
match format_callback_result(response.into_result(), success_callback, error_callback) {
Ok(callback_string) => callback_string,
Err(e) => format_callback(error_callback, &e.to_string())
.expect("unable to serialize response string to json"),
};
let _ = window.eval(&callback_string);
}
#[default_runtime(crate::Wry, wry)]
#[derive(Debug)]
pub struct InvokeMessage<R: Runtime> {
pub(crate) window: Window<R>,
pub(crate) state: Arc<StateManager>,
pub(crate) command: String,
pub(crate) payload: JsonValue,
}
impl<R: Runtime> InvokeMessage<R> {
pub(crate) fn new(
window: Window<R>,
state: Arc<StateManager>,
command: String,
payload: JsonValue,
) -> Self {
Self {
window,
state,
command,
payload,
}
}
#[inline(always)]
pub fn command(&self) -> &str {
&self.command
}
#[inline(always)]
pub fn window(&self) -> Window<R> {
self.window.clone()
}
#[inline(always)]
pub fn window_ref(&self) -> &Window<R> {
&self.window
}
#[inline(always)]
pub fn payload(&self) -> &JsonValue {
&self.payload
}
#[inline(always)]
pub fn state(&self) -> Arc<StateManager> {
self.state.clone()
}
#[inline(always)]
pub fn state_ref(&self) -> &StateManager {
&self.state
}
}