Extract Shell handling into one method common to both windows and unix

This commit is contained in:
Félix Saparelli 2021-04-17 01:26:37 +12:00
parent a2078e3703
commit 1b44e7aa87
1 changed files with 63 additions and 60 deletions

View File

@ -52,6 +52,57 @@ impl Default for Shell {
} }
} }
impl Shell {
/// Obtain a [`Command`] given the cmd vec from [`Config`][crate::config::Config].
///
/// Behaves as described in the enum documentation.
///
/// # Panics
///
/// - Panics if `cmd` is empty.
/// - Panics if the string in the `Unix` variant is empty or only whitespace.
pub fn to_command(&self, cmd: &[String]) -> Command {
assert!(!cmd.is_empty(), "cmd was empty");
match self {
Shell::None => {
// UNWRAP: checked by assert
#[allow(clippy::unwrap_used)]
let (first, rest) = cmd.split_first().unwrap();
let mut c = Command::new(first);
c.args(rest);
c
}
#[cfg(windows)]
Shell::Cmd => {
let mut c = Command::new("cmd.exe");
c.arg("/C").arg(cmd.join(" "));
c
}
Shell::Powershell if cfg!(windows) => {
let mut c = Command::new("powershell.exe");
c.arg("-Command").arg(cmd.join(" "));
c
}
Shell::Powershell => {
let mut c = Command::new("pwsh");
c.arg("-Command").arg(cmd.join(" "));
c
}
Shell::Unix(name) => {
assert!(!name.is_empty(), "shell program was empty");
let mut c = Command::new(shprog);
c.arg("-c").arg(cmd.join(" "));
c
}
}
}
}
pub fn spawn( pub fn spawn(
cmd: &[String], cmd: &[String],
updated_paths: &[PathOp], updated_paths: &[PathOp],
@ -71,7 +122,6 @@ mod imp {
use nix::libc::*; use nix::libc::*;
use nix::{self, Error}; use nix::{self, Error};
use std::io::{self, Result}; use std::io::{self, Result};
use std::process::Command;
use std::sync::*; use std::sync::*;
pub struct Process { pub struct Process {
@ -100,30 +150,7 @@ mod imp {
use std::convert::TryInto; use std::convert::TryInto;
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
// Assemble command to run. let mut command = shell.to_command(&cmd);
// This is either the first argument from cmd (if shell == None) or "sh".
// Using "sh -c" gives us features like supporting pipes and redirects,
// but is a little less performant and can cause trouble when using custom signals
// (e.g. --signal SIGHUP)
let mut command = match shell {
Shell::None => {
let (head, tail) = cmd.split_first().expect("cmd was empty");
let mut c = Command::new(head);
c.args(tail);
c
}
Shell::Powershell => {
let mut c = Command::new("pwsh");
c.arg("-Command").arg(cmd.join(" "));
c
}
Shell::Unix(name) => {
let mut c = Command::new(name);
c.arg("-c").arg(cmd.join(" "));
c
}
};
debug!("Assembled command {:?}", command); debug!("Assembled command {:?}", command);
let command_envs = if !environment { let command_envs = if !environment {
@ -207,11 +234,16 @@ mod imp {
use super::Shell; use super::Shell;
use crate::pathop::PathOp; use crate::pathop::PathOp;
use crate::signal::Signal; use crate::signal::Signal;
use std::io; use std::{
use std::io::Result; io::{self, Result},
use std::mem; mem,
use std::process::Command; ptr,
use std::ptr; convert::TryInto,
os::windows::{
io::IntoRawHandle,
process::CommandExt,
},
};
use winapi::{ use winapi::{
shared::{ shared::{
basetsd::ULONG_PTR, basetsd::ULONG_PTR,
@ -257,10 +289,6 @@ mod imp {
shell: Shell, shell: Shell,
environment: bool, environment: bool,
) -> Result<Self> { ) -> Result<Self> {
use std::convert::TryInto;
use std::os::windows::io::IntoRawHandle;
use std::os::windows::process::CommandExt;
fn last_err() -> io::Error { fn last_err() -> io::Error {
io::Error::last_os_error() io::Error::last_os_error()
} }
@ -316,32 +344,7 @@ mod imp {
panic!("failed to set job info: {}", last_err()); panic!("failed to set job info: {}", last_err());
} }
let mut command = match shell { let mut command = shell.to_command(&cmd);
Shell::None => {
let (first, rest) = cmd.split_first().expect("command is empty");
let mut c = Command::new(first);
c.args(rest);
c
}
Shell::Cmd => {
let mut c = Command::new("cmd.exe");
c.arg("/C").arg(cmd.join(" "));
c
}
Shell::Powershell => {
let mut c = Command::new("powershell.exe");
c.arg("-Command").arg(cmd.join(" "));
c
}
// Using unixy shells on windows could be supported...
Shell::Unix(name) => {
let mut c = Command::new(name);
c.arg("-c").arg(cmd.join(" "));
c
}
};
command.creation_flags(CREATE_SUSPENDED); command.creation_flags(CREATE_SUSPENDED);
debug!("Assembled command {:?}", command); debug!("Assembled command {:?}", command);