Lucas Nogueira 1 жил өмнө
parent
commit
82567345d5

+ 6 - 6
core/tauri-macros/src/command/wrapper.rs

@@ -306,8 +306,8 @@ fn body_async(
       let span = tracing::debug_span!("ipc::request::run");
       #resolver.respond_async_serialized(async move {
         let result = $path(#(#args?),*);
-        let kind = (&result).async_kind();
-        kind.future(result).await
+        let kind = result.async_kind();
+        kind.future().await
       }
       .instrument(span));
       return true;
@@ -317,8 +317,8 @@ fn body_async(
     quote! {
       #resolver.respond_async_serialized(async move {
         let result = $path(#(#args?),*);
-        let kind = (&result).async_kind();
-        kind.future(result).await
+        let kind = result.async_kind();
+        kind.future().await
       });
       return true;
     }
@@ -358,8 +358,8 @@ fn body_blocking(
   Ok(quote! {
     #maybe_span
     let result = $path(#(match #args #match_body),*);
-    let kind = (&result).blocking_kind();
-    kind.block(result, #resolver);
+    let kind = result.blocking_kind();
+    kind.block(#resolver);
     return true;
   })
 }

+ 5 - 5
core/tauri/src/ipc/channel.rs

@@ -20,7 +20,7 @@ use crate::{
   Manager, Runtime, State, Webview,
 };
 
-use super::{CallbackFn, InvokeBody, InvokeError, IpcResponse, Request, Response};
+use super::{CallbackFn, InvokeError, InvokeResponseBody, IpcResponse, Request, Response};
 
 pub const IPC_PAYLOAD_PREFIX: &str = "__CHANNEL__:";
 pub const CHANNEL_PLUGIN_NAME: &str = "__TAURI_CHANNEL__";
@@ -33,13 +33,13 @@ static CHANNEL_DATA_COUNTER: AtomicU32 = AtomicU32::new(0);
 
 /// Maps a channel id to a pending data that must be send to the JavaScript side via the IPC.
 #[derive(Default, Clone)]
-pub struct ChannelDataIpcQueue(pub(crate) Arc<Mutex<HashMap<u32, InvokeBody>>>);
+pub struct ChannelDataIpcQueue(pub(crate) Arc<Mutex<HashMap<u32, InvokeResponseBody>>>);
 
 /// An IPC channel.
 #[derive(Clone)]
 pub struct Channel {
   id: u32,
-  on_message: Arc<dyn Fn(InvokeBody) -> crate::Result<()> + Send + Sync>,
+  on_message: Arc<dyn Fn(InvokeResponseBody) -> crate::Result<()> + Send + Sync>,
 }
 
 impl Serialize for Channel {
@@ -130,13 +130,13 @@ impl<'de> Deserialize<'de> for JavaScriptChannelId {
 
 impl Channel {
   /// Creates a new channel with the given message handler.
-  pub fn new<F: Fn(InvokeBody) -> crate::Result<()> + Send + Sync + 'static>(
+  pub fn new<F: Fn(InvokeResponseBody) -> crate::Result<()> + Send + Sync + 'static>(
     on_message: F,
   ) -> Self {
     Self::new_with_id(CHANNEL_COUNTER.fetch_add(1, Ordering::Relaxed), on_message)
   }
 
-  fn new_with_id<F: Fn(InvokeBody) -> crate::Result<()> + Send + Sync + 'static>(
+  fn new_with_id<F: Fn(InvokeResponseBody) -> crate::Result<()> + Send + Sync + 'static>(
     id: u32,
     on_message: F,
   ) -> Self {

+ 90 - 68
core/tauri/src/ipc/command.rs

@@ -183,145 +183,167 @@ impl<'de, R: Runtime> Deserializer<'de> for CommandItem<'de, R> {
 #[doc(hidden)]
 pub mod private {
   use crate::{
-    ipc::{InvokeBody, InvokeError, InvokeResolver, IpcResponse},
+    ipc::{InvokeError, InvokeResolver, InvokeResponse, InvokeResponseBody, IpcResponse},
     Runtime,
   };
   use futures_util::{FutureExt, TryFutureExt};
-  use std::future::Future;
+  use std::{future::Future, pin::Pin};
   #[cfg(feature = "tracing")]
   pub use tracing;
 
   // ===== impl IpcResponse =====
 
-  pub struct ResponseTag;
+  pub struct ResponseTag(InvokeResponse);
 
   pub trait ResponseKind {
+    fn blocking_kind(self) -> ResponseTag;
+
+    fn async_kind(self) -> ResponseTag;
+  }
+
+  impl ResponseKind for Vec<u8> {
     #[inline(always)]
-    fn blocking_kind(&self) -> ResponseTag {
-      ResponseTag
+    fn blocking_kind(self) -> ResponseTag {
+      ResponseTag(InvokeResponse::Ok(self.into()))
     }
 
     #[inline(always)]
-    fn async_kind(&self) -> ResponseTag {
-      ResponseTag
+    fn async_kind(self) -> ResponseTag {
+      ResponseTag(InvokeResponse::Ok(self.into()))
     }
   }
 
-  impl<T: IpcResponse> ResponseKind for &T {}
+  impl<T: IpcResponse + Clone> ResponseKind for &T {
+    #[inline(always)]
+    fn blocking_kind(self) -> ResponseTag {
+      ResponseTag(self.clone().body().map_err(InvokeError::from_error).into())
+    }
+
+    #[inline(always)]
+    fn async_kind(self) -> ResponseTag {
+      ResponseTag(self.clone().body().map_err(InvokeError::from_error).into())
+    }
+  }
 
   impl ResponseTag {
     #[inline(always)]
-    pub fn block<R, T>(self, value: T, resolver: InvokeResolver<R>)
+    pub fn block<R>(self, resolver: InvokeResolver<R>)
     where
       R: Runtime,
-      T: IpcResponse,
     {
-      resolver.respond(Ok(value))
+      resolver.respond(self.0)
     }
 
     #[inline(always)]
-    pub fn future<T>(self, value: T) -> impl Future<Output = Result<InvokeBody, InvokeError>>
+    pub fn future<T>(self) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>>
     where
       T: IpcResponse,
     {
-      std::future::ready(value.body().map_err(InvokeError::from_error))
+      std::future::ready(match self.0 {
+        InvokeResponse::Ok(b) => Ok(b),
+        InvokeResponse::Err(e) => Err(e),
+      })
     }
   }
 
   // ===== Result<impl Serialize, impl Into<InvokeError>> =====
 
-  pub struct ResultTag;
+  pub struct ResultTag(InvokeResponse);
 
   pub trait ResultKind {
+    fn blocking_kind(self) -> ResultTag;
+
+    fn async_kind(self) -> ResultTag;
+  }
+
+  impl<T: IpcResponse, E: Into<InvokeError>> ResultKind for Result<T, E> {
     #[inline(always)]
-    fn blocking_kind(&self) -> ResultTag {
-      ResultTag
+    fn blocking_kind(self) -> ResultTag {
+      ResultTag(
+        self
+          .map_err(Into::into)
+          .and_then(|r| r.body().map_err(InvokeError::from_error))
+          .into(),
+      )
     }
 
     #[inline(always)]
-    fn async_kind(&self) -> ResultTag {
-      ResultTag
+    fn async_kind(self) -> ResultTag {
+      ResultTag(
+        self
+          .map_err(Into::into)
+          .and_then(|r| r.body().map_err(InvokeError::from_error))
+          .into(),
+      )
     }
   }
 
-  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))
+    pub fn block<R: Runtime>(self, resolver: InvokeResolver<R>) {
+      resolver.respond(self.0)
     }
 
     #[inline(always)]
-    pub fn future<T, E>(
-      self,
-      value: Result<T, E>,
-    ) -> impl Future<Output = Result<InvokeBody, 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 fn future(self) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>> {
+      std::future::ready(match self.0 {
+        InvokeResponse::Ok(b) => Ok(b),
+        InvokeResponse::Err(e) => Err(e),
+      })
     }
   }
 
   // ===== Future<Output = impl IpcResponse> =====
 
-  pub struct FutureTag;
+  pub struct FutureTag<T: IpcResponse, F: Future<Output = T>>(Pin<Box<F>>);
 
-  pub trait FutureKind {
+  pub trait FutureKind<T: IpcResponse, F: Future<Output = T>> {
+    fn async_kind(self) -> FutureTag<T, F>;
+  }
+
+  impl<T: IpcResponse, F: Future<Output = T>> FutureKind<T, F> for F {
     #[inline(always)]
-    fn async_kind(&self) -> FutureTag {
-      FutureTag
+    fn async_kind(self) -> FutureTag<T, F> {
+      FutureTag(Box::pin(self))
     }
   }
-  impl<T: IpcResponse, F: Future<Output = T>> FutureKind for &F {}
 
-  impl FutureTag {
+  impl<T: IpcResponse, F: Future<Output = T>> FutureTag<T, F> {
     #[inline(always)]
-    pub fn future<T, F>(self, value: F) -> impl Future<Output = Result<InvokeBody, InvokeError>>
-    where
-      T: IpcResponse,
-      F: Future<Output = T> + Send + 'static,
-    {
-      value.map(|value| value.body().map_err(InvokeError::from_error))
+    pub fn future(self) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>> {
+      self
+        .0
+        .map(|value| value.body().map_err(InvokeError::from_error))
     }
   }
 
   // ===== Future<Output = Result<impl Serialize, impl Into<InvokeError>>> =====
 
-  pub struct ResultFutureTag;
+  pub struct ResultFutureTag<T: IpcResponse, E: Into<InvokeError>, F: Future<Output = Result<T, E>>>(
+    Pin<Box<F>>,
+  );
 
-  pub trait ResultFutureKind {
-    #[inline(always)]
-    fn async_kind(&self) -> ResultFutureTag {
-      ResultFutureTag
-    }
+  pub trait ResultFutureKind<T: IpcResponse, E: Into<InvokeError>, F: Future<Output = Result<T, E>>>
+  {
+    fn async_kind(self) -> ResultFutureTag<T, E, F>;
   }
 
-  impl<T: IpcResponse, E: Into<InvokeError>, F: Future<Output = Result<T, E>>> ResultFutureKind
-    for F
+  impl<T: IpcResponse, E: Into<InvokeError>, F: Future<Output = Result<T, E>>>
+    ResultFutureKind<T, E, F> for F
   {
+    #[inline(always)]
+    fn async_kind(self) -> ResultFutureTag<T, E, F> {
+      ResultFutureTag(Box::pin(self))
+    }
   }
 
-  impl ResultFutureTag {
+  impl<T: IpcResponse, E: Into<InvokeError>, F: Future<Output = Result<T, E>>>
+    ResultFutureTag<T, E, F>
+  {
     #[inline(always)]
-    pub fn future<T, E, F>(self, value: F) -> impl Future<Output = Result<InvokeBody, InvokeError>>
-    where
-      T: IpcResponse,
-      E: Into<InvokeError>,
-      F: Future<Output = Result<T, E>> + Send,
-    {
-      value
+    pub fn future(self) -> impl Future<Output = Result<InvokeResponseBody, InvokeError>> {
+      self
+        .0
         .err_into()
         .map(|result| result.and_then(|value| value.body().map_err(InvokeError::from_error)))
     }

+ 42 - 25
core/tauri/src/ipc/mod.rs

@@ -41,6 +41,33 @@ pub type InvokeResponder<R> =
 pub type OwnedInvokeResponder<R> =
   dyn FnOnce(Webview<R>, String, InvokeResponse, CallbackFn, CallbackFn) + Send + 'static;
 
+/// Possible values of an IPC payload.
+#[derive(Debug, Clone)]
+pub enum InvokeResponseBody {
+  /// Json response.
+  Json(String),
+  /// Bytes response.
+  Raw(Vec<u8>),
+}
+
+impl From<String> for InvokeResponseBody {
+  fn from(value: String) -> Self {
+    Self::Json(value)
+  }
+}
+
+impl From<Vec<u8>> for InvokeResponseBody {
+  fn from(value: Vec<u8>) -> Self {
+    Self::Raw(value)
+  }
+}
+
+impl IpcResponse for InvokeResponseBody {
+  fn body(self) -> crate::Result<Self> {
+    Ok(self)
+  }
+}
+
 /// Possible values of an IPC payload.
 #[derive(Debug, Clone)]
 pub enum InvokeBody {
@@ -68,12 +95,6 @@ impl From<Vec<u8>> for InvokeBody {
   }
 }
 
-impl IpcResponse for InvokeBody {
-  fn body(self) -> crate::Result<InvokeBody> {
-    Ok(self)
-  }
-}
-
 impl InvokeBody {
   #[allow(dead_code)]
   pub(crate) fn into_json(self) -> JsonValue {
@@ -126,12 +147,12 @@ impl<'a, R: Runtime> CommandArg<'a, R> for Request<'a> {
 /// Marks a type as a response to an IPC call.
 pub trait IpcResponse {
   /// Resolve the IPC response body.
-  fn body(self) -> crate::Result<InvokeBody>;
+  fn body(self) -> crate::Result<InvokeResponseBody>;
 }
 
 impl<T: Serialize> IpcResponse for T {
-  fn body(self) -> crate::Result<InvokeBody> {
-    serde_json::to_value(self)
+  fn body(self) -> crate::Result<InvokeResponseBody> {
+    serde_json::to_string(&self)
       .map(Into::into)
       .map_err(Into::into)
   }
@@ -139,18 +160,18 @@ impl<T: Serialize> IpcResponse for T {
 
 /// The IPC request.
 pub struct Response {
-  body: InvokeBody,
+  body: InvokeResponseBody,
 }
 
 impl IpcResponse for Response {
-  fn body(self) -> crate::Result<InvokeBody> {
+  fn body(self) -> crate::Result<InvokeResponseBody> {
     Ok(self.body)
   }
 }
 
 impl Response {
   /// Defines a response with the given body.
-  pub fn new(body: impl Into<InvokeBody>) -> Self {
+  pub fn new(body: impl Into<InvokeResponseBody>) -> Self {
     Self { body: body.into() }
   }
 }
@@ -206,19 +227,20 @@ impl From<crate::Error> for InvokeError {
 #[derive(Debug)]
 pub enum InvokeResponse {
   /// Resolve the promise.
-  Ok(InvokeBody),
+  Ok(InvokeResponseBody),
   /// Reject the promise.
   Err(InvokeError),
 }
 
+// TODO
 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::Ok(InvokeResponseBody::Json(j)) => j.serialize(serializer),
+      Self::Ok(InvokeResponseBody::Raw(b)) => b.serialize(serializer),
       Self::Err(e) => e.0.serialize(serializer),
     }
   }
@@ -304,7 +326,7 @@ impl<R: Runtime> InvokeResolver<R> {
   /// Reply to the invoke promise with an async task which is already serialized.
   pub fn respond_async_serialized<F>(self, task: F)
   where
-    F: Future<Output = Result<InvokeBody, InvokeError>> + Send + 'static,
+    F: Future<Output = Result<InvokeResponseBody, InvokeError>> + Send + 'static,
   {
     crate::async_runtime::spawn(async move {
       let response = match task.await {
@@ -322,29 +344,24 @@ impl<R: Runtime> InvokeResolver<R> {
     });
   }
 
-  /// Reply to the invoke promise with a serializable value.
-  pub fn respond<T: IpcResponse>(self, value: Result<T, InvokeError>) {
+  /// Reply to the invoke promise with a response value.
+  pub fn respond(self, response: InvokeResponse) {
     Self::return_result(
       self.webview,
       self.responder,
-      value.into(),
+      response,
       self.cmd,
       self.callback,
       self.error,
     )
   }
 
-  /// Resolve the invoke promise with a value.
-  pub fn resolve<T: IpcResponse>(self, value: T) {
-    self.respond(Ok(value))
-  }
-
   /// Reject the invoke promise with a value.
   pub fn reject<T: Serialize>(self, value: T) {
     Self::return_result(
       self.webview,
       self.responder,
-      Result::<(), _>::Err(value).into(),
+      Result::<&(), _>::Err(value).into(),
       self.cmd,
       self.callback,
       self.error,

+ 11 - 15
core/tauri/src/ipc/protocol.rs

@@ -14,7 +14,7 @@ use http::{
   HeaderValue, Method, StatusCode,
 };
 
-use super::{CallbackFn, InvokeBody, InvokeResponse};
+use super::{CallbackFn, InvokeResponse, InvokeResponseBody};
 
 const TAURI_CALLBACK_HEADER_NAME: &str = "Tauri-Callback";
 const TAURI_ERROR_HEADER_NAME: &str = "Tauri-Error";
@@ -80,11 +80,11 @@ pub fn get<R: Runtime>(manager: Arc<AppManager<R>>, label: String) -> UriSchemeP
                   .entered();
 
                   let (mut response, mime_type) = match response {
-                    InvokeResponse::Ok(InvokeBody::Json(v)) => (
+                    InvokeResponse::Ok(InvokeResponseBody::Json(v)) => (
                       http::Response::new(serde_json::to_vec(&v).unwrap().into()),
                       mime::APPLICATION_JSON,
                     ),
-                    InvokeResponse::Ok(InvokeBody::Raw(v)) => (
+                    InvokeResponse::Ok(InvokeResponseBody::Raw(v)) => (
                       http::Response::new(v.into()),
                       mime::APPLICATION_OCTET_STREAM,
                     ),
@@ -271,7 +271,6 @@ fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, labe
               Channel,
             };
             use crate::sealed::ManagerBase;
-            use serde_json::Value as JsonValue;
 
             #[cfg(feature = "tracing")]
             let _respond_span = tracing::trace_span!(
@@ -303,20 +302,18 @@ fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, labe
                 "ipc::request::response",
                 response = serde_json::to_string(&response).unwrap(),
                 mime_type = match &response {
-                  InvokeResponse::Ok(InvokeBody::Json(_)) => mime::APPLICATION_JSON,
-                  InvokeResponse::Ok(InvokeBody::Raw(_)) => mime::APPLICATION_OCTET_STREAM,
+                  InvokeResponse::Ok(InvokeResponseBody::Json(_)) => mime::APPLICATION_JSON,
+                  InvokeResponse::Ok(InvokeResponseBody::Raw(_)) => mime::APPLICATION_OCTET_STREAM,
                   InvokeResponse::Err(_) => mime::APPLICATION_JSON,
                 }
                 .essence_str()
               )
               .entered();
 
-              match &response {
-                InvokeResponse::Ok(InvokeBody::Json(v)) => {
-                  if !(cfg!(target_os = "macos") || cfg!(target_os = "ios"))
-                    && matches!(v, JsonValue::Object(_) | JsonValue::Array(_))
-                  {
-                    let _ = Channel::from_callback_fn(webview, callback).send(v);
+              match response {
+                InvokeResponse::Ok(InvokeResponseBody::Json(v)) => {
+                  if !(cfg!(target_os = "macos") || cfg!(target_os = "ios")) && v.len() > 4000 {
+                    let _ = Channel::from_callback_fn(webview, callback).send(&v);
                   } else {
                     responder_eval(
                       &webview,
@@ -325,7 +322,7 @@ fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, labe
                     )
                   }
                 }
-                InvokeResponse::Ok(InvokeBody::Raw(v)) => {
+                InvokeResponse::Ok(InvokeResponseBody::Raw(v)) => {
                   if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
                     responder_eval(
                       &webview,
@@ -333,8 +330,7 @@ fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, labe
                       error,
                     );
                   } else {
-                    let _ =
-                      Channel::from_callback_fn(webview, callback).send(InvokeBody::Raw(v.clone()));
+                    let _ = Channel::from_callback_fn(webview, callback).send(v);
                   }
                 }
                 InvokeResponse::Err(e) => responder_eval(

+ 12 - 12
examples/api/src-tauri/Cargo.lock

@@ -1892,9 +1892,9 @@ dependencies = [
 
 [[package]]
 name = "muda"
-version = "0.11.5"
+version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453"
+checksum = "a40c16e25abca53b401d2972e8ad344820e318cf7e00ea8a951a5ca265590295"
 dependencies = [
  "cocoa",
  "crossbeam-channel",
@@ -3152,7 +3152,7 @@ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
 
 [[package]]
 name = "tauri"
-version = "2.0.0-beta.11"
+version = "2.0.0-beta.13"
 dependencies = [
  "anyhow",
  "bytes",
@@ -3201,7 +3201,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-build"
-version = "2.0.0-beta.9"
+version = "2.0.0-beta.10"
 dependencies = [
  "anyhow",
  "cargo_toml",
@@ -3223,7 +3223,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-codegen"
-version = "2.0.0-beta.9"
+version = "2.0.0-beta.10"
 dependencies = [
  "base64 0.22.0",
  "brotli",
@@ -3248,7 +3248,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-macros"
-version = "2.0.0-beta.9"
+version = "2.0.0-beta.10"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -3260,7 +3260,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-plugin"
-version = "2.0.0-beta.9"
+version = "2.0.0-beta.10"
 dependencies = [
  "anyhow",
  "glob",
@@ -3286,7 +3286,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime"
-version = "2.0.0-beta.9"
+version = "2.0.0-beta.10"
 dependencies = [
  "gtk",
  "http",
@@ -3302,7 +3302,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime-wry"
-version = "2.0.0-beta.9"
+version = "2.0.0-beta.10"
 dependencies = [
  "cocoa",
  "gtk",
@@ -3324,7 +3324,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-utils"
-version = "2.0.0-beta.9"
+version = "2.0.0-beta.10"
 dependencies = [
  "aes-gcm",
  "brotli",
@@ -3644,9 +3644,9 @@ dependencies = [
 
 [[package]]
 name = "tray-icon"
-version = "0.11.3"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a4d9ddd4a7c0f3b6862af1c4911b529a49db4ee89310d3a258859c2f5053fdd"
+checksum = "454035ff34b8430638c894e6197748578d6b4d449c6edaf8ea854d94e2dd862b"
 dependencies = [
  "cocoa",
  "core-graphics",

+ 5 - 3
examples/api/src-tauri/build.rs

@@ -10,9 +10,11 @@ fn main() {
         "app-menu",
         tauri_build::InlinedPlugin::new().commands(&["toggle", "popup"]),
       )
-      .app_manifest(
-        tauri_build::AppManifest::new().commands(&["log_operation", "perform_request"]),
-      ),
+      .app_manifest(tauri_build::AppManifest::new().commands(&[
+        "download",
+        "log_operation",
+        "perform_request",
+      ])),
   )
   .expect("failed to run tauri-build");
 }

+ 1 - 0
examples/api/src-tauri/capabilities/run-app.json

@@ -16,6 +16,7 @@
       ]
     },
     "allow-perform-request",
+    "allow-download",
     "app-menu:default",
     "sample:allow-ping-scoped",
     "sample:global-scope",

+ 11 - 0
examples/api/src-tauri/permissions/autogenerated/download.toml

@@ -0,0 +1,11 @@
+# Automatically generated - DO NOT EDIT!
+
+[[permission]]
+identifier = "allow-download"
+description = "Enables the download command without any pre-configured scope."
+commands.allow = ["download"]
+
+[[permission]]
+identifier = "deny-download"
+description = "Denies the download command without any pre-configured scope."
+commands.deny = ["download"]

+ 6 - 1
examples/api/src-tauri/src/cmd.rs

@@ -33,7 +33,7 @@ pub fn log_operation(
   }
 }
 
-#[derive(Serialize)]
+#[derive(Clone, Serialize)]
 pub struct ApiResponse {
   message: String,
 }
@@ -45,3 +45,8 @@ pub fn perform_request(endpoint: String, body: RequestBody) -> ApiResponse {
     message: "message response".into(),
   }
 }
+
+#[command]
+pub fn download() -> Vec<u8> {
+  vec![1, 2, 3]
+}

+ 1 - 0
examples/api/src-tauri/src/lib.rs

@@ -142,6 +142,7 @@ pub fn run_app<R: Runtime, F: FnOnce(&App<R>) + Send + 'static>(
     .invoke_handler(tauri::generate_handler![
       cmd::log_operation,
       cmd::perform_request,
+      cmd::download
     ])
     .build(tauri::tauri_build_context!())
     .expect("error while building tauri application");