Pārlūkot izejas kodu

Command support for specified character encoding, closes #4644 (#4772)

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
horochx 3 gadi atpakaļ
vecāks
revīzija
d8cf9f9fcd

+ 5 - 0
.changes/shell-encoding-api.md

@@ -0,0 +1,5 @@
+---
+"api": minor
+---
+
+Added the `encoding` option to the `Command` options.

+ 5 - 0
.changes/shell-encoding.md

@@ -0,0 +1,5 @@
+---
+"tauri": minor
+---
+
+Add `api::Command::encoding` method to set the stdout/stderr encoding.

+ 1 - 0
core/tauri/Cargo.toml

@@ -93,6 +93,7 @@ serialize-to-javascript = "=0.1.1"
 infer = { version = "0.8", optional = true }
 png = { version = "0.17", optional = true }
 ico = { version = "0.1", optional = true }
+encoding_rs = "0.8.31"
 
 [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
 gtk = { version = "0.15", features = [ "v3_20" ] }

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
core/tauri/scripts/bundle.js


+ 17 - 1
core/tauri/src/api/process/command.rs

@@ -20,6 +20,7 @@ use std::os::windows::process::CommandExt;
 const CREATE_NO_WINDOW: u32 = 0x0800_0000;
 
 use crate::async_runtime::{block_on as block_on_task, channel, Receiver, Sender};
+pub use encoding_rs::Encoding;
 use os_pipe::{pipe, PipeReader, PipeWriter};
 use serde::Serialize;
 use shared_child::SharedChild;
@@ -95,6 +96,7 @@ pub struct Command {
   env_clear: bool,
   env: HashMap<String, String>,
   current_dir: Option<PathBuf>,
+  encoding: Option<&'static Encoding>,
 }
 
 /// Spawned child process.
@@ -171,6 +173,7 @@ impl Command {
       env_clear: false,
       env: Default::default(),
       current_dir: None,
+      encoding: None,
     }
   }
 
@@ -216,6 +219,13 @@ impl Command {
     self
   }
 
+  /// Sets the character encoding for stdout/stderr.
+  #[must_use]
+  pub fn encoding(mut self, encoding: &'static Encoding) -> Self {
+    self.encoding.replace(encoding);
+    self
+  }
+
   /// Spawns the command.
   ///
   /// # Examples
@@ -264,12 +274,14 @@ impl Command {
       guard.clone(),
       stdout_reader,
       CommandEvent::Stdout,
+      self.encoding,
     );
     spawn_pipe_reader(
       tx.clone(),
       guard.clone(),
       stderr_reader,
       CommandEvent::Stderr,
+      self.encoding,
     );
 
     spawn(move || {
@@ -378,6 +390,7 @@ fn spawn_pipe_reader<F: Fn(String) -> CommandEvent + Send + Copy + 'static>(
   guard: Arc<RwLock<()>>,
   pipe_reader: PipeReader,
   wrapper: F,
+  character_encoding: Option<&'static Encoding>,
 ) {
   spawn(move || {
     let _lock = guard.read().unwrap();
@@ -392,7 +405,10 @@ fn spawn_pipe_reader<F: Fn(String) -> CommandEvent + Send + Copy + 'static>(
             break;
           }
           let tx_ = tx.clone();
-          let line = String::from_utf8(buf.clone());
+          let line = match character_encoding {
+            Some(encoding) => Ok(encoding.decode_with_bom_removal(&buf).0.into()),
+            None => String::from_utf8(buf.clone()),
+          };
           block_on_task(async move {
             let _ = match line {
               Ok(line) => tx_.send(wrapper(line)).await,

+ 10 - 0
core/tauri/src/endpoints/shell.rs

@@ -54,6 +54,8 @@ pub struct CommandOptions {
   // but the env is an `Option` so when it's `None` we clear the env.
   #[serde(default = "default_env")]
   env: Option<HashMap<String, String>>,
+  // Character encoding for stdout/stderr
+  encoding: Option<String>,
 }
 
 /// The API descriptor.
@@ -148,6 +150,13 @@ impl Cmd {
       } else {
         command = command.env_clear();
       }
+      if let Some(encoding) = options.encoding {
+        if let Some(encoding) = crate::api::process::Encoding::for_label(encoding.as_bytes()) {
+          command = command.encoding(encoding);
+        } else {
+          return Err(anyhow::anyhow!(format!("unknown encoding {}", encoding)));
+        }
+      }
       let (mut rx, child) = command.spawn()?;
 
       let pid = child.pid();
@@ -229,6 +238,7 @@ mod tests {
         sidecar: false,
         cwd: Option::arbitrary(g),
         env: Option::arbitrary(g),
+        encoding: Option::arbitrary(g),
       }
     }
   }

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

@@ -3132,7 +3132,7 @@ dependencies = [
 
 [[package]]
 name = "tauri"
-version = "1.0.2"
+version = "1.0.5"
 dependencies = [
  "anyhow",
  "attohttpc",
@@ -3142,6 +3142,7 @@ dependencies = [
  "cocoa",
  "dirs-next",
  "embed_plist",
+ "encoding_rs",
  "flate2",
  "futures",
  "futures-lite",
@@ -3194,7 +3195,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-build"
-version = "1.0.2"
+version = "1.0.4"
 dependencies = [
  "anyhow",
  "cargo_toml",
@@ -3209,7 +3210,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-codegen"
-version = "1.0.2"
+version = "1.0.4"
 dependencies = [
  "base64",
  "brotli",
@@ -3233,7 +3234,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-macros"
-version = "1.0.2"
+version = "1.0.4"
 dependencies = [
  "heck 0.4.0",
  "proc-macro2",
@@ -3245,7 +3246,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime"
-version = "0.10.1"
+version = "0.10.2"
 dependencies = [
  "gtk",
  "http",
@@ -3263,7 +3264,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-runtime-wry"
-version = "0.10.1"
+version = "0.10.2"
 dependencies = [
  "cocoa",
  "gtk",
@@ -3281,7 +3282,7 @@ dependencies = [
 
 [[package]]
 name = "tauri-utils"
-version = "1.0.2"
+version = "1.0.3"
 dependencies = [
  "aes-gcm",
  "brotli",

+ 8 - 1
examples/api/src/views/Shell.svelte

@@ -1,5 +1,6 @@
 <script>
   import { Command } from '@tauri-apps/api/shell'
+
   const windows = navigator.userAgent.includes('Windows')
   let cmd = windows ? 'cmd' : 'sh'
   let args = windows ? ['/C'] : ['-c']
@@ -9,6 +10,7 @@
   let script = 'echo "hello world"'
   let cwd = null
   let env = 'SOMETHING=value ANOTHER=2'
+  let encoding = ''
   let stdin = ''
   let child
 
@@ -26,7 +28,8 @@
     child = null
     const command = new Command(cmd, [...args, script], {
       cwd: cwd || null,
-      env: _getEnv()
+      env: _getEnv(),
+      encoding,
     })
 
     command.on('close', (data) => {
@@ -65,6 +68,10 @@
     Script:
     <input class="grow input" bind:value={script} />
   </div>
+  <div class="flex items-center gap-1">
+    Encoding:
+    <input class="grow input" bind:value={encoding} />
+  </div>
   <div class="flex items-center gap-1">
     Working directory:
     <input

+ 2 - 0
tooling/api/src/shell.ts

@@ -83,6 +83,8 @@ interface SpawnOptions {
   cwd?: string
   /** Environment variables. set to `null` to clear the process env. */
   env?: { [name: string]: string }
+  /** Character encoding for stdout/stderr */
+  encoding?: string
 }
 
 /** @ignore */

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels