From 2a6f3a2dcfe6f6d9ccedc3d4b17953efdf6b234e Mon Sep 17 00:00:00 2001 From: kraktus Date: Thu, 19 May 2022 15:50:27 +0200 Subject: [PATCH] More verbose `Debug` implementation of `std::process:Command` Mainly based on commit: https://github.com/zackmdavis/rust/commit/ccc019aabfdd550944c049625e66c92c815ea1d0 from https://github.com/zackmdavis close https://github.com/rust-lang/rust/issues/42200 --- library/std/src/process.rs | 3 -- .../src/sys/unix/process/process_common.rs | 30 ++++++++++++++----- src/test/ui/command/command-argv0-debug.rs | 14 +++++++-- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 400d25beb26f3..6f2e1debf2643 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -1033,9 +1033,6 @@ impl Command { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Command { - /// Format the program and arguments of a Command for display. Any - /// non-utf8 data is lossily converted using the utf8 replacement - /// character. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.inner.fmt(f) } diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 848adca78c076..cd47090e56f9f 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -110,6 +110,7 @@ pub struct Command { } // Create a new type for argv, so that we can make it `Send` and `Sync` +#[derive(Debug)] struct Argv(Vec<*const c_char>); // It is safe to make `Argv` `Send` and `Sync`, because it contains @@ -144,6 +145,7 @@ pub enum ChildStdio { Null, } +#[derive(Debug)] pub enum Stdio { Inherit, Null, @@ -510,16 +512,30 @@ impl ChildStdio { } impl fmt::Debug for Command { + // show all attributes but `self.closures` which does not implement `Debug` fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.program != self.args[0] { - write!(f, "[{:?}] ", self.program)?; + let mut debug_command = f.debug_struct("Command"); + debug_command + .field("program", &self.program) + .field("args", &self.args) + .field("argv", &self.argv) + .field("env", &self.env) + .field("cwd", &self.cwd) + .field("uid", &self.uid) + .field("gid", &self.gid) + .field("saw_nul", &self.saw_nul) + .field("groups", &self.groups) + .field("stdin", &self.stdin) + .field("stdout", &self.stdout) + .field("stderr", &self.stderr) + .field("pgroup", &self.pgroup); + + #[cfg(target_os = "linux")] + { + debug_command.field("create_pidfd", &self.create_pidfd); } - write!(f, "{:?}", self.args[0])?; - for arg in &self.args[1..] { - write!(f, " {:?}", arg)?; - } - Ok(()) + debug_command.finish() } } diff --git a/src/test/ui/command/command-argv0-debug.rs b/src/test/ui/command/command-argv0-debug.rs index 4aba1229f29a8..6f42a45423cb1 100644 --- a/src/test/ui/command/command-argv0-debug.rs +++ b/src/test/ui/command/command-argv0-debug.rs @@ -3,19 +3,27 @@ // ignore-windows - this is a unix-specific test // ignore-emscripten no processes // ignore-sgx no processes +// ignore-tidy-linelength use std::os::unix::process::CommandExt; use std::process::Command; +// `argv` attribute changes each time the test is made so needs to be retrieved dynamically +// There is no public API to access it so we need to parse the debug output +// Example of returned String: "[0x600010bb8000, 0x600010bb80a0]" +fn get_argv(cmd: &Command) -> String { + format!("{cmd:?}").split_once("Argv(").and_then(|(_, after)| after.split_once(")")).unwrap().0.to_string() +} + fn main() { let mut command = Command::new("some-boring-name"); - assert_eq!(format!("{:?}", command), r#""some-boring-name""#); + assert_eq!(format!("{command:?}"), format!(r#"Command {{ program: "some-boring-name", args: ["some-boring-name"], argv: Argv({}), env: CommandEnv {{ clear: false, saw_path: false, vars: {{}} }}, cwd: None, uid: None, gid: None, saw_nul: false, groups: None, stdin: None, stdout: None, stderr: None, pgroup: None }}"#, get_argv(&command))); command.args(&["1", "2", "3"]); - assert_eq!(format!("{:?}", command), r#""some-boring-name" "1" "2" "3""#); + assert_eq!(format!("{command:?}"), format!(r#"Command {{ program: "some-boring-name", args: ["some-boring-name", "1", "2", "3"], argv: Argv({}), env: CommandEnv {{ clear: false, saw_path: false, vars: {{}} }}, cwd: None, uid: None, gid: None, saw_nul: false, groups: None, stdin: None, stdout: None, stderr: None, pgroup: None }}"#, get_argv(&command))); command.arg0("exciting-name"); - assert_eq!(format!("{:?}", command), r#"["some-boring-name"] "exciting-name" "1" "2" "3""#); + assert_eq!(format!("{command:?}"), format!(r#"Command {{ program: "some-boring-name", args: ["exciting-name", "1", "2", "3"], argv: Argv({}), env: CommandEnv {{ clear: false, saw_path: false, vars: {{}} }}, cwd: None, uid: None, gid: None, saw_nul: false, groups: None, stdin: None, stdout: None, stderr: None, pgroup: None }}"#, get_argv(&command))); }