Browse Source

feat(core): simplify scope definition for sidecars (#3574)

Lucas Fernandes Nogueira 3 years ago
parent
commit
9b3b163baa

+ 5 - 0
.changes/sidecar-scope-improvement.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch
+---
+
+The `cmd` field is no longer required on the shell scope for sidecars.

+ 34 - 1
core/tauri-utils/src/config.rs

@@ -877,7 +877,7 @@ impl Allowlist for WindowAllowlistConfig {
 }
 
 /// A command allowed to be executed by the webview API.
-#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
+#[derive(Debug, PartialEq, Clone, Serialize)]
 #[cfg_attr(feature = "schema", derive(JsonSchema))]
 pub struct ShellAllowedCommand {
   /// The name for this allowed shell command configuration.
@@ -903,6 +903,39 @@ pub struct ShellAllowedCommand {
   pub sidecar: bool,
 }
 
+impl<'de> Deserialize<'de> for ShellAllowedCommand {
+  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+  where
+    D: Deserializer<'de>,
+  {
+    #[derive(Deserialize)]
+    struct InnerShellAllowedCommand {
+      name: String,
+      #[serde(rename = "cmd")]
+      command: Option<PathBuf>,
+      #[serde(default)]
+      args: ShellAllowedArgs,
+      #[serde(default)]
+      sidecar: bool,
+    }
+
+    let config = InnerShellAllowedCommand::deserialize(deserializer)?;
+
+    if !config.sidecar && config.command.is_none() {
+      return Err(DeError::custom(
+        "The shell scope `command` value is required.",
+      ));
+    }
+
+    Ok(ShellAllowedCommand {
+      name: config.name,
+      command: config.command.unwrap_or_default(),
+      args: config.args,
+      sidecar: config.sidecar,
+    })
+  }
+}
+
 /// A set of command arguments allowed to be executed by the webview API.
 ///
 /// A value of `true` will allow any arguments to be passed to the command. `false` will disable all

+ 5 - 5
core/tauri/src/endpoints/shell.rs

@@ -97,7 +97,7 @@ impl Cmd {
         let program = PathBuf::from(program);
         let program_as_string = program.display().to_string();
         let program_no_ext_as_string = program.with_extension("").display().to_string();
-        let is_configured = context
+        let configured_sidecar = context
           .config
           .tauri
           .bundle
@@ -106,15 +106,15 @@ impl Cmd {
           .map(|bins| {
             bins
               .iter()
-              .any(|b| b == &program_as_string || b == &program_no_ext_as_string)
+              .find(|b| b == &&program_as_string || b == &&program_no_ext_as_string)
           })
           .unwrap_or_default();
-        if is_configured {
+        if let Some(sidecar) = configured_sidecar {
           context
             .window
             .state::<Scopes>()
             .shell
-            .prepare(&program.to_string_lossy(), args, true)
+            .prepare_sidecar(&program.to_string_lossy(), sidecar, args)
             .map_err(crate::error::into_anyhow)?
         } else {
           return Err(crate::Error::SidecarNotAllowed(program).into_anyhow());
@@ -128,7 +128,7 @@ impl Cmd {
         .window
         .state::<Scopes>()
         .shell
-        .prepare(&program, args, false)
+        .prepare(&program, args)
       {
         Ok(cmd) => cmd,
         Err(e) => {

+ 31 - 4
core/tauri/src/scope/shell.rs

@@ -197,20 +197,37 @@ impl Scope {
     Self(scope)
   }
 
+  /// Validates argument inputs and creates a Tauri sidecar [`Command`].
+  #[cfg(shell_sidecar)]
+  pub fn prepare_sidecar(
+    &self,
+    command_name: &str,
+    command_script: &str,
+    args: ExecuteArgs,
+  ) -> Result<Command, ScopeError> {
+    self._prepare(command_name, args, Some(command_script))
+  }
+
+  /// Validates argument inputs and creates a Tauri [`Command`].
+  #[cfg(shell_execute)]
+  pub fn prepare(&self, command_name: &str, args: ExecuteArgs) -> Result<Command, ScopeError> {
+    self._prepare(command_name, args, None)
+  }
+
   /// Validates argument inputs and creates a Tauri [`Command`].
   #[cfg(any(shell_execute, shell_sidecar))]
-  pub fn prepare(
+  pub fn _prepare(
     &self,
     command_name: &str,
     args: ExecuteArgs,
-    sidecar: bool,
+    sidecar: Option<&str>,
   ) -> Result<Command, ScopeError> {
     let command = match self.0.scopes.get(command_name) {
       Some(command) => command,
       None => return Err(ScopeError::NotFound(command_name.into())),
     };
 
-    if command.sidecar != sidecar {
+    if command.sidecar != sidecar.is_some() {
       return Err(ScopeError::BadSidecarFlag);
     }
 
@@ -250,7 +267,17 @@ impl Scope {
       (Some(_), _) => Err(ScopeError::InvalidInput(command_name.into())),
     }?;
 
-    let command_s = command.command.to_string_lossy();
+    let command_s = sidecar
+      .map(|s| {
+        std::path::PathBuf::from(s)
+          .components()
+          .last()
+          .unwrap()
+          .as_os_str()
+          .to_string_lossy()
+          .into_owned()
+      })
+      .unwrap_or_else(|| command.command.to_string_lossy().into_owned());
     let command = if command.sidecar {
       Command::new_sidecar(command_s).map_err(ScopeError::Sidecar)?
     } else {

+ 45 - 17
examples/sidecar/index.html

@@ -1,21 +1,49 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="UTF-8" />
-    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>Sidecar</title>
-  </head>
 
-  <body>
-    <div></div>
-    <script>
-      const div = document.querySelector('div')
-      window.__TAURI__.event.listen('message', (event) => {
-        const p = document.createElement('p')
-        p.innerText = event.payload
-        div.appendChild(p)
-      })
-    </script>
-  </body>
+<head>
+  <meta charset="UTF-8" />
+  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <title>Sidecar</title>
+  <style>
+    .container {
+      display: flex;
+    }
+    .container > div {
+      flex: 1;
+    }
+  </style>
+</head>
+
+<body>
+  <div class="container">
+    <div id="backend"></div>
+    <div id="frontend"></div>
+  </div>
+  <script>
+    function addMessage(div, message) {
+      const p = document.createElement('p')
+      p.innerText = message
+      div.appendChild(p)
+    }
+
+    const backendDiv = document.getElementById('backend')
+    window.__TAURI__.event.listen('message', (event) => {
+      addMessage(backendDiv, event.payload)
+    })
+
+    const frontendDiv = document.getElementById('frontend')
+    const { Command } = window.__TAURI__.shell
+    const command = Command.sidecar('binaries/app')
+    command.on('close', data => {
+      addMessage(frontendDiv, `command finished with code ${data.code} and signal ${data.signal}`)
+    })
+    command.on('error', error => addMessage(frontendDiv, `command error: "${error}"`))
+    command.stdout.on('data', line => addMessage(frontendDiv, `command stdout: "${line}"`))
+    command.stderr.on('data', line => addMessage(frontendDiv, `command stderr: "${line}"`))
+    command.spawn()
+  </script>
+</body>
+
 </html>

+ 8 - 7
examples/sidecar/src-tauri/Cargo.lock

@@ -2574,7 +2574,7 @@ dependencies = [
 
 [[package]]
 name = "tauri"
-version = "1.0.0-rc.2"
+version = "1.0.0-rc.3"
 dependencies = [
  "anyhow",
  "bincode",
@@ -2590,6 +2590,7 @@ dependencies = [
  "gtk",
  "http",
  "ignore",
+ "memchr",
  "once_cell",
  "os_pipe",
  "percent-encoding",
@@ -2618,7 +2619,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-build"
-version = "1.0.0-rc.2"
+version = "1.0.0-rc.3"
 dependencies = [
  "anyhow",
  "cargo_toml",
@@ -2631,7 +2632,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-codegen"
-version = "1.0.0-rc.1"
+version = "1.0.0-rc.2"
 dependencies = [
  "base64",
  "blake3",
@@ -2650,7 +2651,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-macros"
-version = "1.0.0-rc.1"
+version = "1.0.0-rc.2"
 dependencies = [
  "heck 0.4.0",
  "proc-macro2",
@@ -2662,7 +2663,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime"
-version = "0.3.1"
+version = "0.3.2"
 dependencies = [
  "gtk",
  "http",
@@ -2679,7 +2680,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime-wry"
-version = "0.3.1"
+version = "0.3.2"
 dependencies = [
  "gtk",
  "ico",
@@ -2695,7 +2696,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-utils"
-version = "1.0.0-rc.1"
+version = "1.0.0-rc.2"
 dependencies = [
  "ctor",
  "glob",

+ 1 - 1
examples/sidecar/src-tauri/Cargo.toml

@@ -11,7 +11,7 @@ tauri-build = { path = "../../../core/tauri-build", features = ["codegen"] }
 [dependencies]
 serde_json = "1.0"
 serde = { version = "1.0", features = [ "derive" ] }
-tauri = { path = "../../../core/tauri", features = ["shell-execute"] }
+tauri = { path = "../../../core/tauri", features = ["shell-sidecar"] }
 
 [features]
 default = [ "custom-protocol" ]

+ 7 - 1
examples/sidecar/src-tauri/tauri.conf.json

@@ -38,7 +38,13 @@
     "allowlist": {
       "all": false,
       "shell": {
-        "execute": true
+        "sidecar": true,
+        "scope": [
+          {
+            "name": "binaries/app",
+            "sidecar": true
+          }
+        ]
       }
     },
     "windows": [

+ 1 - 1
tooling/api/src/shell.ts

@@ -42,7 +42,7 @@
  *
  * - `name`: the unique identifier of the command, passed to the [[Command.constructor | Command constructor]].
  * If it's a sidecar, this must be the value defined on `tauri.conf.json > tauri > bundle > externalBin`.
- * - `cmd`: the program that is executed on this configuration. If it's a sidecar, it must be the same as `name`.
+ * - `cmd`: the program that is executed on this configuration. If it's a sidecar, this value is ignored.
  * - `sidecar`: whether the object configures a sidecar or a system program.
  * - `args`: the arguments that can be passed to the program. By default no arguments are allowed.
  *   - `true` means that any argument list is allowed.