1use crate::log;
4use camino::Utf8Path;
5use duct::Expression;
6use std::ffi::OsString;
7use std::fmt::Write;
8use std::io;
9use std::process::Output;
10use tracing::Level;
11
12pub fn cmd<T, U>(program: T, args: U) -> Expression
14where
15 T: duct::IntoExecutablePath + Clone,
16 U: IntoIterator + Clone,
17 <U as IntoIterator>::Item: Into<OsString>,
18{
19 cmd_log(Level::INFO, program, args)
20}
21
22pub fn cmd_log<T, U>(l: Level, program: T, args: U) -> Expression
24where
25 T: duct::IntoExecutablePath + Clone,
26 U: IntoIterator + Clone,
27 U::Item: Into<OsString>,
28{
29 let mut formatted_cmd = format!(
30 "Running command: {program}",
31 program = shell_escape::escape(program.clone().to_executable().to_string_lossy())
32 );
33 for arg in args.clone() {
34 write!(
35 formatted_cmd,
36 " {arg}",
37 arg = shell_escape::escape(arg.into().to_string_lossy())
38 )
39 .unwrap();
40 }
41
42 log!(l, "{formatted_cmd}");
43
44 duct::cmd(program, args)
45}
46
47#[macro_export]
50macro_rules! cmd {
51 ( $program:expr $(, $arg:expr )* $(,)? ) => {
52 {
53 use std::ffi::OsString;
54 let args: &[OsString] = &[$( Into::<OsString>::into($arg) ),*];
55 $crate::exec::cmd_log(tracing::Level::INFO, $program, args)
56 }
57 };
58}
59
60#[macro_export]
63macro_rules! cmd_debug {
64 ( $program:expr $(, $arg:expr )* $(,)? ) => {
65 {
66 use std::ffi::OsString;
67 let args: &[OsString] = &[$( Into::<OsString>::into($arg) ),*];
68 $crate::exec::cmd_log(tracing::Level::DEBUG, $program, args)
69 }
70 };
71}
72
73#[macro_export]
76macro_rules! cmd_if_wet {
77 ( $dry_run:expr, $program:expr $(, $arg:expr )* $(,)? ) => {
78 {
79 use std::ffi::OsString;
80 let mut actual_program = $program;
81 let args: Vec<OsString> = if $dry_run {
82 actual_program = "true";
83 vec![OsString::from("[Dry Run]"), Into::<OsString>::into($program), $( Into::<OsString>::into($arg) ),*]
84 } else {
85 vec![$( Into::<OsString>::into($arg) ),*]
86 };
87 $crate::exec::cmd_log(tracing::Level::INFO, actual_program, &args)
88 }
89 };
90}
91
92#[macro_export]
95macro_rules! cmd_debug_if_wet {
96 ( $dry_run:expr, $program:expr $(, $arg:expr )* $(,)? ) => {
97 {
98 use std::ffi::OsString;
99 let mut actual_program = $program;
100 let args: Vec<OsString> = if $dry_run {
101 actual_program = "true";
102 vec![OsString::from("[Dry Run]"), Into::<OsString>::into($program), $( Into::<OsString>::into($arg) ),*]
103 } else {
104 vec![$( Into::<OsString>::into($arg) ),*]
105 };
106 $crate::exec::cmd_log(tracing::Level::DEBUG, actual_program, &args)
107 }
108 };
109}
110
111pub trait UpDuct {
113 fn run_with(&self, stdout_fn: fn(&Expression) -> Expression) -> io::Result<Output>;
121
122 fn run_with_path(&self, path: &Utf8Path) -> io::Result<Output>;
128
129 fn run_with_inherit(&self) -> io::Result<Output>;
131}
132
133impl UpDuct for Expression {
134 fn run_with(&self, stdout_fn: fn(&Expression) -> Expression) -> io::Result<Output> {
136 #[allow(clippy::disallowed_methods)]
139 stdout_fn(self).run()
140 }
141
142 fn run_with_path(&self, path: &Utf8Path) -> io::Result<Output> {
144 #[allow(clippy::disallowed_methods)]
147 self.stdout_path(path).run()
148 }
149
150 fn run_with_inherit(&self) -> io::Result<Output> {
152 #[allow(clippy::disallowed_methods)]
155 self.run()
156 }
157}