From 0ae5de8e42cf1b1985c863b864feca44931c1618 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 31 Jul 2023 14:24:11 -0300 Subject: [PATCH] refactor(shell): Command adhere to std::process::Command --- .changes/shell-command-std-like.md | 5 ++ plugins/shell/src/commands.rs | 6 +- plugins/shell/src/lib.rs | 6 +- plugins/shell/src/process/mod.rs | 99 ++++++++++-------------------- plugins/shell/src/scope.rs | 6 +- 5 files changed, 47 insertions(+), 75 deletions(-) create mode 100644 .changes/shell-command-std-like.md diff --git a/.changes/shell-command-std-like.md b/.changes/shell-command-std-like.md new file mode 100644 index 00000000..12596a7d --- /dev/null +++ b/.changes/shell-command-std-like.md @@ -0,0 +1,5 @@ +--- +"shell": "patch" +--- + +Refactor the `Command` API adhere to `std::process::Command`. diff --git a/plugins/shell/src/commands.rs b/plugins/shell/src/commands.rs index e4b63e24..0548fe0c 100644 --- a/plugins/shell/src/commands.rs +++ b/plugins/shell/src/commands.rs @@ -133,12 +133,12 @@ pub fn execute( } }; if let Some(cwd) = options.cwd { - command = command.current_dir(cwd); + command.current_dir(cwd); } if let Some(env) = options.env { - command = command.envs(env); + command.envs(env); } else { - command = command.env_clear(); + command.env_clear(); } let encoding = match options.encoding { Option::None => EncodingWrapper::Text(None), diff --git a/plugins/shell/src/lib.rs b/plugins/shell/src/lib.rs index fbc2683f..70caad6c 100644 --- a/plugins/shell/src/lib.rs +++ b/plugins/shell/src/lib.rs @@ -13,6 +13,8 @@ use std::{ collections::HashMap, + ffi::OsStr, + path::Path, sync::{Arc, Mutex}, }; @@ -45,7 +47,7 @@ pub struct Shell { impl Shell { /// Creates a new Command for launching the given program. - pub fn command(&self, program: impl Into) -> Command { + pub fn command(&self, program: impl AsRef) -> Command { Command::new(program) } @@ -53,7 +55,7 @@ impl Shell { /// /// A sidecar program is a embedded external binary in order to make your application work /// or to prevent users having to install additional dependencies (e.g. Node.js, Python, etc). - pub fn sidecar(&self, program: impl Into) -> Result { + pub fn sidecar(&self, program: impl AsRef) -> Result { Command::new_sidecar(program) } diff --git a/plugins/shell/src/process/mod.rs b/plugins/shell/src/process/mod.rs index d677d636..c75f10c6 100644 --- a/plugins/shell/src/process/mod.rs +++ b/plugins/shell/src/process/mod.rs @@ -3,9 +3,10 @@ // SPDX-License-Identifier: MIT use std::{ - collections::HashMap, + ffi::OsStr, io::{BufReader, Write}, - path::PathBuf, + ops::{Deref, DerefMut}, + path::{Path, PathBuf}, process::{Command as StdCommand, Stdio}, sync::{Arc, RwLock}, thread::spawn, @@ -53,12 +54,20 @@ pub enum CommandEvent { /// The type to spawn commands. #[derive(Debug)] -pub struct Command { - program: String, - args: Vec, - env_clear: bool, - env: HashMap, - current_dir: Option, +pub struct Command(StdCommand); + +impl Deref for Command { + type Target = StdCommand; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Command { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } } /// Spawned child process. @@ -116,83 +125,37 @@ pub struct Output { pub stderr: Vec, } -fn relative_command_path(command: String) -> crate::Result { +fn relative_command_path(command: &Path) -> crate::Result { match platform::current_exe()?.parent() { #[cfg(windows)] - Some(exe_dir) => Ok(format!("{}\\{command}.exe", exe_dir.display())), + Some(exe_dir) => Ok(exe_dir.join(format!("{command}.exe"))), #[cfg(not(windows))] - Some(exe_dir) => Ok(format!("{}/{command}", exe_dir.display())), + Some(exe_dir) => Ok(exe_dir.join(command)), None => Err(crate::Error::CurrentExeHasNoParent), } } impl From for StdCommand { fn from(cmd: Command) -> StdCommand { - let mut command = StdCommand::new(cmd.program); - command.args(cmd.args); - command.stdout(Stdio::piped()); - command.stdin(Stdio::piped()); - command.stderr(Stdio::piped()); - if cmd.env_clear { - command.env_clear(); - } - command.envs(cmd.env); - if let Some(current_dir) = cmd.current_dir { - command.current_dir(current_dir); - } - #[cfg(windows)] - command.creation_flags(CREATE_NO_WINDOW); - command + cmd.0 } } impl Command { - pub(crate) fn new>(program: S) -> Self { - Self { - program: program.into(), - args: Default::default(), - env_clear: false, - env: Default::default(), - current_dir: None, - } - } - - pub(crate) fn new_sidecar>(program: S) -> crate::Result { - Ok(Self::new(relative_command_path(program.into())?)) - } - - /// Appends arguments to the command. - #[must_use] - pub fn args(mut self, args: I) -> Self - where - I: IntoIterator, - S: AsRef, - { - for arg in args { - self.args.push(arg.as_ref().to_string()); - } - self - } + pub(crate) fn new>(program: S) -> Self { + let mut command = StdCommand::new(program); - /// Clears the entire environment map for the child process. - #[must_use] - pub fn env_clear(mut self) -> Self { - self.env_clear = true; - self - } + command.stdout(Stdio::piped()); + command.stdin(Stdio::piped()); + command.stderr(Stdio::piped()); + #[cfg(windows)] + command.creation_flags(CREATE_NO_WINDOW); - /// Adds or updates multiple environment variable mappings. - #[must_use] - pub fn envs(mut self, env: HashMap) -> Self { - self.env = env; - self + Self(command) } - /// Sets the working directory for the child process. - #[must_use] - pub fn current_dir(mut self, current_dir: PathBuf) -> Self { - self.current_dir.replace(current_dir); - self + pub(crate) fn new_sidecar>(program: S) -> crate::Result { + Ok(Self::new(relative_command_path(program.as_ref())?)) } /// Spawns the command. diff --git a/plugins/shell/src/scope.rs b/plugins/shell/src/scope.rs index c184e922..0bcde6e3 100644 --- a/plugins/shell/src/scope.rs +++ b/plugins/shell/src/scope.rs @@ -237,13 +237,15 @@ impl Scope { .into_owned() }) .unwrap_or_else(|| command.command.to_string_lossy().into_owned()); - let command = if command.sidecar { + let mut command = if command.sidecar { Command::new_sidecar(command_s).map_err(|e| Error::Sidecar(e.to_string()))? } else { Command::new(command_s) }; - Ok(command.args(args)) + command.args(args); + + Ok(command) } /// Open a path in the default (or specified) browser.