use crate::{
ipc::{InvokeBody, InvokeError, InvokeMessage},
Runtime,
};
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer,
};
use tauri_utils::acl::resolved::ResolvedCommand;
pub struct CommandItem<'a, R: Runtime> {
pub plugin: Option<&'static str>,
pub name: &'static str,
pub key: &'static str,
pub message: &'a InvokeMessage<R>,
pub acl: &'a Option<Vec<ResolvedCommand>>,
}
pub trait CommandArg<'de, R: Runtime>: Sized {
fn from_command(command: CommandItem<'de, R>) -> Result<Self, InvokeError>;
}
impl<'de, D: Deserialize<'de>, R: Runtime> CommandArg<'de, R> for D {
fn from_command(command: CommandItem<'de, R>) -> Result<D, InvokeError> {
let name = command.name;
let arg = command.key;
#[cfg(feature = "tracing")]
let _span = tracing::trace_span!("ipc::request::deserialize_arg", arg = arg).entered();
Self::deserialize(command).map_err(|e| crate::Error::InvalidArgs(name, arg, e).into())
}
}
macro_rules! pass {
($fn:ident, $($arg:ident: $argt:ty),+) => {
fn $fn<V: Visitor<'de>>(self, $($arg: $argt),*) -> Result<V::Value, Self::Error> {
if self.key.is_empty() {
return Err(serde_json::Error::custom(format!(
"command {} has an argument with no name with a non-optional value",
self.name
)))
}
match &self.message.payload {
InvokeBody::Raw(_body) => {
Err(serde_json::Error::custom(format!(
"command {} expected a value for key {} but the IPC call used a bytes payload",
self.name, self.key
)))
}
InvokeBody::Json(v) => {
match v.get(self.key) {
Some(value) => value.$fn($($arg),*),
None => {
Err(serde_json::Error::custom(format!(
"command {} missing required key {}",
self.name, self.key
)))
}
}
}
}
}
}
}
impl<'de, R: Runtime> Deserializer<'de> for CommandItem<'de, R> {
type Error = serde_json::Error;
pass!(deserialize_any, visitor: V);
pass!(deserialize_bool, visitor: V);
pass!(deserialize_i8, visitor: V);
pass!(deserialize_i16, visitor: V);
pass!(deserialize_i32, visitor: V);
pass!(deserialize_i64, visitor: V);
pass!(deserialize_u8, visitor: V);
pass!(deserialize_u16, visitor: V);
pass!(deserialize_u32, visitor: V);
pass!(deserialize_u64, visitor: V);
pass!(deserialize_f32, visitor: V);
pass!(deserialize_f64, visitor: V);
pass!(deserialize_char, visitor: V);
pass!(deserialize_str, visitor: V);
pass!(deserialize_string, visitor: V);
pass!(deserialize_bytes, visitor: V);
pass!(deserialize_byte_buf, visitor: V);
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
match &self.message.payload {
InvokeBody::Raw(_body) => Err(serde_json::Error::custom(format!(
"command {} expected a value for key {} but the IPC call used a bytes payload",
self.name, self.key
))),
InvokeBody::Json(v) => match v.get(self.key) {
Some(value) => value.deserialize_option(visitor),
None => visitor.visit_none(),
},
}
}
pass!(deserialize_unit, visitor: V);
pass!(deserialize_unit_struct, name: &'static str, visitor: V);
pass!(deserialize_newtype_struct, name: &'static str, visitor: V);
pass!(deserialize_seq, visitor: V);
pass!(deserialize_tuple, len: usize, visitor: V);
pass!(
deserialize_tuple_struct,
name: &'static str,
len: usize,
visitor: V
);
pass!(deserialize_map, visitor: V);
pass!(
deserialize_struct,
name: &'static str,
fields: &'static [&'static str],
visitor: V
);
pass!(
deserialize_enum,
name: &'static str,
fields: &'static [&'static str],
visitor: V
);
pass!(deserialize_identifier, visitor: V);
pass!(deserialize_ignored_any, visitor: V);
}
#[doc(hidden)]
pub mod private {
use crate::{
ipc::{InvokeError, InvokeResolver, InvokeResponseBody, IpcResponse},
Runtime,
};
use futures_util::{FutureExt, TryFutureExt};
use std::future::Future;
#[cfg(feature = "tracing")]
pub use tracing;
pub struct ResponseTag;
pub trait ResponseKind {
#[inline(always)]
fn blocking_kind(&self) -> ResponseTag {
ResponseTag
}
#[inline(always)]
fn async_kind(&self) -> ResponseTag {
ResponseTag
}
}
impl<T: IpcResponse> ResponseKind for &T {}
impl ResponseTag {
#[inline(always)]
pub fn block<R, T>(self, value: T, resolver: InvokeResolver<R>)
where
R: Runtime,
T: IpcResponse,
{
resolver.respond(Ok(value))
}
#[inline(always)]
pub fn future<T>(
self,
value: T,
) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>>
where
T: IpcResponse,
{
std::future::ready(value.body().map_err(InvokeError::from_error))
}
}
pub struct ResultTag;
pub trait ResultKind {
#[inline(always)]
fn blocking_kind(&self) -> ResultTag {
ResultTag
}
#[inline(always)]
fn async_kind(&self) -> ResultTag {
ResultTag
}
}
impl<T: IpcResponse, E: Into<InvokeError>> ResultKind for Result<T, E> {}
impl ResultTag {
#[inline(always)]
pub fn block<R, T, E>(self, value: Result<T, E>, resolver: InvokeResolver<R>)
where
R: Runtime,
T: IpcResponse,
E: Into<InvokeError>,
{
resolver.respond(value.map_err(Into::into))
}
#[inline(always)]
pub fn future<T, E>(
self,
value: Result<T, E>,
) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>>
where
T: IpcResponse,
E: Into<InvokeError>,
{
std::future::ready(
value
.map_err(Into::into)
.and_then(|value| value.body().map_err(InvokeError::from_error)),
)
}
}
pub struct FutureTag;
pub trait FutureKind {
#[inline(always)]
fn async_kind(&self) -> FutureTag {
FutureTag
}
}
impl<T: IpcResponse, F: Future<Output = T>> FutureKind for &F {}
impl FutureTag {
#[inline(always)]
pub fn future<T, F>(
self,
value: F,
) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>>
where
T: IpcResponse,
F: Future<Output = T> + Send + 'static,
{
value.map(|value| value.body().map_err(InvokeError::from_error))
}
}
pub struct ResultFutureTag;
pub trait ResultFutureKind {
#[inline(always)]
fn async_kind(&self) -> ResultFutureTag {
ResultFutureTag
}
}
impl<T: IpcResponse, E: Into<InvokeError>, F: Future<Output = Result<T, E>>> ResultFutureKind
for F
{
}
impl ResultFutureTag {
#[inline(always)]
pub fn future<T, E, F>(
self,
value: F,
) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>>
where
T: IpcResponse,
E: Into<InvokeError>,
F: Future<Output = Result<T, E>> + Send,
{
value
.err_into()
.map(|result| result.and_then(|value| value.body().map_err(InvokeError::from_error)))
}
}
}