use std::sync::{Arc, Mutex};
use futures_util::Future;
use http::HeaderMap;
use serde::{
de::{DeserializeOwned, IntoDeserializer},
Deserialize, Serialize,
};
use serde_json::Value as JsonValue;
pub use serialize_to_javascript::Options as SerializeOptions;
use tauri_macros::default_runtime;
use tauri_utils::acl::resolved::ResolvedCommand;
use crate::{webview::Webview, Runtime, StateManager};
mod authority;
pub(crate) mod channel;
mod command;
pub(crate) mod format_callback;
pub(crate) mod protocol;
pub use authority::{
CapabilityBuilder, CommandScope, GlobalScope, Origin, RuntimeAuthority, RuntimeCapability,
ScopeObject, ScopeValue,
};
pub use channel::{Channel, JavaScriptChannelId};
pub use command::{private, CommandArg, CommandItem};
pub type InvokeHandler<R> = dyn Fn(Invoke<R>) -> bool + Send + Sync + 'static;
pub type InvokeResponder<R> =
dyn Fn(&Webview<R>, &str, &InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static;
pub type OwnedInvokeResponder<R> =
dyn FnOnce(Webview<R>, String, InvokeResponse, CallbackFn, CallbackFn) + Send + 'static;
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub enum InvokeBody {
Json(JsonValue),
Raw(Vec<u8>),
}
impl Default for InvokeBody {
fn default() -> Self {
Self::Json(Default::default())
}
}
impl From<JsonValue> for InvokeBody {
fn from(value: JsonValue) -> Self {
Self::Json(value)
}
}
impl From<Vec<u8>> for InvokeBody {
fn from(value: Vec<u8>) -> Self {
Self::Raw(value)
}
}
impl IpcResponse for InvokeBody {
fn body(self) -> crate::Result<InvokeBody> {
Ok(self)
}
}
impl InvokeBody {
#[allow(dead_code)]
pub(crate) fn into_json(self) -> JsonValue {
match self {
Self::Json(v) => v,
Self::Raw(v) => {
JsonValue::Array(v.into_iter().map(|n| JsonValue::Number(n.into())).collect())
}
}
}
pub fn deserialize<T: DeserializeOwned>(self) -> serde_json::Result<T> {
match self {
InvokeBody::Json(v) => serde_json::from_value(v),
InvokeBody::Raw(v) => T::deserialize(v.into_deserializer()),
}
}
}
#[derive(Debug)]
pub struct Request<'a> {
body: &'a InvokeBody,
headers: &'a HeaderMap,
}
impl<'a> Request<'a> {
pub fn body(&self) -> &InvokeBody {
self.body
}
pub fn headers(&self) -> &HeaderMap {
self.headers
}
}
impl<'a, R: Runtime> CommandArg<'a, R> for Request<'a> {
fn from_command(command: CommandItem<'a, R>) -> Result<Self, InvokeError> {
Ok(Self {
body: command.message.payload(),
headers: command.message.headers(),
})
}
}
pub trait IpcResponse {
fn body(self) -> crate::Result<InvokeBody>;
}
impl<T: Serialize> IpcResponse for T {
fn body(self) -> crate::Result<InvokeBody> {
serde_json::to_value(self)
.map(Into::into)
.map_err(Into::into)
}
}
pub struct Response {
body: InvokeBody,
}
impl IpcResponse for Response {
fn body(self) -> crate::Result<InvokeBody> {
Ok(self.body)
}
}
impl Response {
pub fn new(body: impl Into<InvokeBody>) -> Self {
Self { body: body.into() }
}
}
#[doc(hidden)]
#[default_runtime(crate::Wry, wry)]
pub struct Invoke<R: Runtime> {
pub message: InvokeMessage<R>,
pub resolver: InvokeResolver<R>,
pub acl: Option<Vec<ResolvedCommand>>,
}
#[derive(Debug)]
pub struct InvokeError(pub JsonValue);
impl InvokeError {
#[inline(always)]
pub fn from_error<E: std::error::Error>(error: E) -> 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_error)
}
}
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(InvokeBody),
Err(InvokeError),
}
impl Serialize for InvokeResponse {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Self::Ok(InvokeBody::Json(j)) => j.serialize(serializer),
Self::Ok(InvokeBody::Raw(b)) => b.serialize(serializer),
Self::Err(e) => e.0.serialize(serializer),
}
}
}
impl<T: IpcResponse, E: Into<InvokeError>> From<Result<T, E>> for InvokeResponse {
#[inline]
fn from(result: Result<T, E>) -> Self {
match result {
Ok(ok) => match ok.body() {
Ok(value) => Self::Ok(value),
Err(err) => Self::Err(InvokeError::from_error(err)),
},
Err(err) => Self::Err(err.into()),
}
}
}
impl From<InvokeError> for InvokeResponse {
fn from(error: InvokeError) -> Self {
Self::Err(error)
}
}
#[default_runtime(crate::Wry, wry)]
pub struct InvokeResolver<R: Runtime> {
webview: Webview<R>,
responder: Arc<Mutex<Option<Box<OwnedInvokeResponder<R>>>>>,
cmd: String,
pub(crate) callback: CallbackFn,
pub(crate) error: CallbackFn,
}
impl<R: Runtime> Clone for InvokeResolver<R> {
fn clone(&self) -> Self {
Self {
webview: self.webview.clone(),
responder: self.responder.clone(),
cmd: self.cmd.clone(),
callback: self.callback,
error: self.error,
}
}
}
impl<R: Runtime> InvokeResolver<R> {
pub(crate) fn new(
webview: Webview<R>,
responder: Arc<Mutex<Option<Box<OwnedInvokeResponder<R>>>>>,
cmd: String,
callback: CallbackFn,
error: CallbackFn,
) -> Self {
Self {
webview,
responder,
cmd,
callback,
error,
}
}
pub fn respond_async<T, F>(self, task: F)
where
T: IpcResponse,
F: Future<Output = Result<T, InvokeError>> + Send + 'static,
{
crate::async_runtime::spawn(async move {
Self::return_task(
self.webview,
self.responder,
task,
self.cmd,
self.callback,
self.error,
)
.await;
});
}
pub fn respond_async_serialized<F>(self, task: F)
where
F: Future<Output = Result<InvokeBody, 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.webview,
self.responder,
response,
self.cmd,
self.callback,
self.error,
)
});
}
pub fn respond<T: IpcResponse>(self, value: Result<T, InvokeError>) {
Self::return_result(
self.webview,
self.responder,
value.into(),
self.cmd,
self.callback,
self.error,
)
}
pub fn resolve<T: IpcResponse>(self, value: T) {
self.respond(Ok(value))
}
pub fn reject<T: Serialize>(self, value: T) {
Self::return_result(
self.webview,
self.responder,
Result::<(), _>::Err(value).into(),
self.cmd,
self.callback,
self.error,
)
}
pub fn invoke_error(self, error: InvokeError) {
Self::return_result(
self.webview,
self.responder,
error.into(),
self.cmd,
self.callback,
self.error,
)
}
pub async fn return_task<T, F>(
webview: Webview<R>,
responder: Arc<Mutex<Option<Box<OwnedInvokeResponder<R>>>>>,
task: F,
cmd: String,
success_callback: CallbackFn,
error_callback: CallbackFn,
) where
T: IpcResponse,
F: Future<Output = Result<T, InvokeError>> + Send + 'static,
{
let result = task.await;
Self::return_closure(
webview,
responder,
|| result,
cmd,
success_callback,
error_callback,
)
}
pub(crate) fn return_closure<T: IpcResponse, F: FnOnce() -> Result<T, InvokeError>>(
webview: Webview<R>,
responder: Arc<Mutex<Option<Box<OwnedInvokeResponder<R>>>>>,
f: F,
cmd: String,
success_callback: CallbackFn,
error_callback: CallbackFn,
) {
Self::return_result(
webview,
responder,
f().into(),
cmd,
success_callback,
error_callback,
)
}
pub(crate) fn return_result(
webview: Webview<R>,
responder: Arc<Mutex<Option<Box<OwnedInvokeResponder<R>>>>>,
response: InvokeResponse,
cmd: String,
success_callback: CallbackFn,
error_callback: CallbackFn,
) {
(responder.lock().unwrap().take().expect("resolver consumed"))(
webview,
cmd,
response,
success_callback,
error_callback,
);
}
}
#[default_runtime(crate::Wry, wry)]
#[derive(Debug)]
pub struct InvokeMessage<R: Runtime> {
pub(crate) webview: Webview<R>,
pub(crate) state: Arc<StateManager>,
pub(crate) command: String,
pub(crate) payload: InvokeBody,
pub(crate) headers: HeaderMap,
}
impl<R: Runtime> Clone for InvokeMessage<R> {
fn clone(&self) -> Self {
Self {
webview: self.webview.clone(),
state: self.state.clone(),
command: self.command.clone(),
payload: self.payload.clone(),
headers: self.headers.clone(),
}
}
}
impl<R: Runtime> InvokeMessage<R> {
pub(crate) fn new(
webview: Webview<R>,
state: Arc<StateManager>,
command: String,
payload: InvokeBody,
headers: HeaderMap,
) -> Self {
Self {
webview,
state,
command,
payload,
headers,
}
}
#[inline(always)]
pub fn command(&self) -> &str {
&self.command
}
#[inline(always)]
pub fn webview(&self) -> Webview<R> {
self.webview.clone()
}
#[inline(always)]
pub fn webview_ref(&self) -> &Webview<R> {
&self.webview
}
#[inline(always)]
pub fn payload(&self) -> &InvokeBody {
&self.payload
}
#[inline(always)]
pub fn state(&self) -> Arc<StateManager> {
self.state.clone()
}
#[inline(always)]
pub fn state_ref(&self) -> &StateManager {
&self.state
}
#[inline(always)]
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct CallbackFn(pub u32);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserialize_invoke_body() {
let json = InvokeBody::Json(serde_json::Value::Array(vec![
serde_json::Value::Number(1.into()),
serde_json::Value::Number(123.into()),
serde_json::Value::Number(1231.into()),
]));
assert_eq!(json.deserialize::<Vec<u16>>().unwrap(), vec![1, 123, 1231]);
let json = InvokeBody::Json(serde_json::Value::String("string value".into()));
assert_eq!(json.deserialize::<String>().unwrap(), "string value");
let json = InvokeBody::Json(serde_json::Value::String("string value".into()));
assert!(json.deserialize::<Vec<u16>>().is_err());
let values = vec![1, 2, 3, 4, 5, 6, 1];
let raw = InvokeBody::Raw(values.clone());
assert_eq!(raw.deserialize::<Vec<u8>>().unwrap(), values);
}
}