use super::env::{CommandEnv, CommandEnvs}; pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::NonZero; use crate::path::Path; use crate::process::StdioPipes; use crate::sys::fs::File; use crate::sys::pipe::AnonPipe; use crate::sys::unsupported; use crate::{fmt, io}; //////////////////////////////////////////////////////////////////////////////// // Command //////////////////////////////////////////////////////////////////////////////// pub struct Command { program: OsString, args: Vec, env: CommandEnv, cwd: Option, stdin: Option, stdout: Option, stderr: Option, } #[derive(Debug)] pub enum Stdio { Inherit, Null, MakePipe, ParentStdout, ParentStderr, #[allow(dead_code)] // This variant exists only for the Debug impl InheritFile(File), } impl Command { pub fn new(program: &OsStr) -> Command { Command { program: program.to_owned(), args: vec![program.to_owned()], env: Default::default(), cwd: None, stdin: None, stdout: None, stderr: None, } } pub fn arg(&mut self, arg: &OsStr) { self.args.push(arg.to_owned()); } pub fn env_mut(&mut self) -> &mut CommandEnv { &mut self.env } pub fn cwd(&mut self, dir: &OsStr) { self.cwd = Some(dir.to_owned()); } pub fn stdin(&mut self, stdin: Stdio) { self.stdin = Some(stdin); } pub fn stdout(&mut self, stdout: Stdio) { self.stdout = Some(stdout); } pub fn stderr(&mut self, stderr: Stdio) { self.stderr = Some(stderr); } pub fn get_program(&self) -> &OsStr { &self.program } pub fn get_args(&self) -> CommandArgs<'_> { let mut iter = self.args.iter(); iter.next(); CommandArgs { iter } } pub fn get_envs(&self) -> CommandEnvs<'_> { self.env.iter() } pub fn get_current_dir(&self) -> Option<&Path> { self.cwd.as_ref().map(|cs| Path::new(cs)) } pub fn spawn( &mut self, _default: Stdio, _needs_stdin: bool, ) -> io::Result<(Process, StdioPipes)> { unsupported() } } pub fn output(_cmd: &mut Command) -> io::Result<(ExitStatus, Vec, Vec)> { unsupported() } impl From for Stdio { fn from(pipe: AnonPipe) -> Stdio { pipe.diverge() } } impl From for Stdio { fn from(_: io::Stdout) -> Stdio { Stdio::ParentStdout } } impl From for Stdio { fn from(_: io::Stderr) -> Stdio { Stdio::ParentStderr } } impl From for Stdio { fn from(file: File) -> Stdio { Stdio::InheritFile(file) } } impl fmt::Debug for Command { // show all attributes fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { let mut debug_command = f.debug_struct("Command"); debug_command.field("program", &self.program).field("args", &self.args); if !self.env.is_unchanged() { debug_command.field("env", &self.env); } if self.cwd.is_some() { debug_command.field("cwd", &self.cwd); } if self.stdin.is_some() { debug_command.field("stdin", &self.stdin); } if self.stdout.is_some() { debug_command.field("stdout", &self.stdout); } if self.stderr.is_some() { debug_command.field("stderr", &self.stderr); } debug_command.finish() } else { if let Some(ref cwd) = self.cwd { write!(f, "cd {cwd:?} && ")?; } if self.env.does_clear() { write!(f, "env -i ")?; // Altered env vars will be printed next, that should exactly work as expected. } else { // Removed env vars need the command to be wrapped in `env`. let mut any_removed = false; for (key, value_opt) in self.get_envs() { if value_opt.is_none() { if !any_removed { write!(f, "env ")?; any_removed = true; } write!(f, "-u {} ", key.to_string_lossy())?; } } } // Altered env vars can just be added in front of the program. for (key, value_opt) in self.get_envs() { if let Some(value) = value_opt { write!(f, "{}={value:?} ", key.to_string_lossy())?; } } if self.program != self.args[0] { write!(f, "[{:?}] ", self.program)?; } write!(f, "{:?}", self.args[0])?; for arg in &self.args[1..] { write!(f, " {:?}", arg)?; } Ok(()) } } } #[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] #[non_exhaustive] pub struct ExitStatus(); impl ExitStatus { pub fn exit_ok(&self) -> Result<(), ExitStatusError> { Ok(()) } pub fn code(&self) -> Option { Some(0) } } impl fmt::Display for ExitStatus { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "") } } pub struct ExitStatusError(!); impl Clone for ExitStatusError { fn clone(&self) -> ExitStatusError { self.0 } } impl Copy for ExitStatusError {} impl PartialEq for ExitStatusError { fn eq(&self, _other: &ExitStatusError) -> bool { self.0 } } impl Eq for ExitStatusError {} impl fmt::Debug for ExitStatusError { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0 } } impl Into for ExitStatusError { fn into(self) -> ExitStatus { self.0 } } impl ExitStatusError { pub fn code(self) -> Option> { self.0 } } #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct ExitCode(u8); impl ExitCode { pub const SUCCESS: ExitCode = ExitCode(0); pub const FAILURE: ExitCode = ExitCode(1); pub fn as_i32(&self) -> i32 { self.0 as i32 } } impl From for ExitCode { fn from(code: u8) -> Self { Self(code) } } pub struct Process(!); impl Process { pub fn id(&self) -> u32 { self.0 } pub fn kill(&mut self) -> io::Result<()> { self.0 } pub fn wait(&mut self) -> io::Result { self.0 } pub fn try_wait(&mut self) -> io::Result> { self.0 } } pub struct CommandArgs<'a> { iter: crate::slice::Iter<'a, OsString>, } impl<'a> Iterator for CommandArgs<'a> { type Item = &'a OsStr; fn next(&mut self) -> Option<&'a OsStr> { self.iter.next().map(|os| &**os) } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a> ExactSizeIterator for CommandArgs<'a> { fn len(&self) -> usize { self.iter.len() } fn is_empty(&self) -> bool { self.iter.is_empty() } } impl<'a> fmt::Debug for CommandArgs<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.iter.clone()).finish() } }