command_ext/lib.rs
1//! # CommandExt
2//!
3//! `CommandExt` is a set of convenient extension traits for `std::process::Command` which
4//! make it easier to use, particularly in cargo scripts where many commands may be executed
5//! with different requirements for error checking, logging, and so forth.
6//!
7//! ## CommandExtCheck
8//!
9//! `CommandExtCheck` allows you to check the result of a command and get a nicely packaged
10//! error containing printable output and error streams.
11//!
12//! ```rust
13//! use std::process::Command;
14//! use command_ext::CommandExtCheck;
15//! # fn main() -> anyhow::Result<()> {
16//! Command::new("echo").arg("x").check()?; // Ok!
17//! Command::new("noexistcmd").arg("badarg").check().map_err(|e| {
18//! // StdIoError(Os { code: 2, kind: NotFound, message: "No such file or directory" })
19//! eprintln!("{}", e);
20//! }).ok();
21//! Command::new("false").check().map_err(|e| {
22//! // Command failed with status (exit status: 1), stdout (), stderr ()
23//! eprintln!("{}", e);
24//! }).ok();
25//! # Ok(())
26//! # }
27//! ```
28//!
29//! Usually, scripts probably will just use `Command::new("cmd").args(["arg1", "arg2"]).check()?`.
30//!
31//! ## CommandExtLog
32//!
33//! `CommandExtLog` allows you to add customizable logging to your commands.
34//!
35//! ```rust
36//! use std::process::Command;
37//! use command_ext::{CommandExtCheck, CommandExtLog};
38//! use env_logger::Builder;
39//! use log::{LevelFilter, Level};
40//! # fn main() -> anyhow::Result<()> {
41//! Builder::new().filter_level(LevelFilter::max()).init();
42//! Command::new("bash")
43//! .args(["-c", "echo err >&2; echo ok"])
44//! .log_args(Level::Debug)
45//! .log_status(Level::Info)
46//! .log_stdout(Level::Trace)
47//! .log_stderr(Level::Warn)
48//! .check()?;
49//! # Ok(())
50//! # }
51//! ```
52//!
53//! This logs:
54//!
55//! ```txt
56//! [2023-12-13T21:04:17Z DEBUG command_ext::log] args: bash -c echo err >&2; echo ok
57//! [2023-12-13T21:04:17Z INFO command_ext::log] status: exit status: 0
58//! [2023-12-13T21:04:17Z TRACE command_ext::log] stdout: ok
59//! [2023-12-13T21:04:17Z WARN command_ext::log] stderr: err
60//! ```
61//!
62//! ## CommandExtTrace
63//!
64//! `CommandExtTrace` works very similarly to `CommandExtLog`
65//!
66//! ```rust
67//! use command_ext::{CommandExtCheck, CommandExtTrace};
68//! use std::io::stdout;
69//! use std::process::Command;
70//! use tracing::{metadata::LevelFilter, Level};
71//! use tracing_subscriber::{fmt, prelude::*, registry, Layer};
72//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
73//! registry()
74//! .with(
75//! fmt::layer()
76//! .with_writer(stdout)
77//! .with_filter(LevelFilter::TRACE),
78//! )
79//! .try_init()?;
80//! Command::new("bash")
81//! .args(["-c", "echo err >&2; echo ok"])
82//! .trace_args(Level::DEBUG)
83//! .trace_status(Level::INFO)
84//! .trace_stdout(Level::TRACE)
85//! .trace_stderr(Level::WARN)
86//! .check()?;
87//! # Ok(())
88//! # }
89//! ```
90//!
91//! This traces:
92//!
93//! ```txt
94//! 2023-12-13T21:06:31.739932Z DEBUG command_ext::trace: args: bash -c echo err >&2; echo ok
95//! 2023-12-13T21:06:31.741100Z INFO command_ext::trace: status: exit status: 0
96//! 2023-12-13T21:06:31.741138Z TRACE command_ext::trace: stdout: ok
97//! 2023-12-13T21:06:31.741147Z WARN command_ext::trace: stderr: err
98//! ```
99//!
100//! ## CommandWrap
101//!
102//! For other cases where you might want to hook into what `Command` is doing, you can use
103//! `CommandWrap` to implement your own wrappers. See the examples for more details.
104
105pub mod error;
106pub use error::CommandExtError;
107
108pub mod wrap;
109pub use wrap::{CommandWrap, HasCommand};
110
111#[cfg(feature = "check")]
112pub mod check;
113#[cfg(feature = "check")]
114pub use check::CommandExtCheck;
115
116#[cfg(feature = "log")]
117pub mod log;
118#[cfg(feature = "log")]
119pub use log::CommandExtLog;
120
121#[cfg(feature = "print")]
122pub mod print;
123#[cfg(feature = "print")]
124pub use print::CommandExtPrint;
125
126#[cfg(feature = "tracing")]
127pub mod trace;
128#[cfg(feature = "tracing")]
129pub use trace::CommandExtTrace;
130
131#[cfg(all(feature = "check", feature = "log", feature = "print", feature = "tracing"))]
132pub trait CommandExt: CommandExtCheck + CommandExtLog + CommandExtPrint + CommandExtTrace {}
133
134#[cfg(all(feature = "check", feature = "log", feature = "print", not(feature = "tracing")))]
135pub trait CommandExt: CommandExtCheck + CommandExtLog + CommandExtPrint {}
136
137#[cfg(all(feature = "check", not(feature = "log"), feature = "print", feature = "tracing"))]
138pub trait CommandExt: CommandExtCheck + CommandExtPrint + CommandExtTrace {}
139
140#[cfg(all(feature = "check", feature = "log", not(feature = "print"), feature = "tracing"))]
141pub trait CommandExt: CommandExtCheck + CommandExtLog + CommandExtTrace {}
142
143#[cfg(all(not(feature = "check"), feature = "log", feature = "print", feature = "tracing"))]
144pub trait CommandExt: CommandExtLog + CommandExtPrint + CommandExtTrace {}
145
146#[cfg(all(feature = "check", not(feature = "log"), not(feature = "print"), not(feature = "tracing")))]
147pub trait CommandExt: CommandExtCheck {}
148
149#[cfg(all(not(feature = "check"), feature = "log", not(feature = "print"), not(feature = "tracing")))]
150pub trait CommandExt: CommandExtLog {}
151
152#[cfg(all(not(feature = "check"), not(feature = "log"), feature = "print", not(feature = "tracing")))]
153pub trait CommandExt: CommandExtPrint {}
154
155#[cfg(all(not(feature = "check"), not(feature = "log"), not(feature = "print"), feature = "tracing"))]
156pub trait CommandExt: CommandExtTrace {}
157