foundry_common/io/
macros.rs1#[macro_export]
17macro_rules! prompt {
18 () => {
19 $crate::stdin::parse_line()
20 };
21
22 ($($tt:tt)+) => {{
23 let _ = $crate::sh_eprint!($($tt)+);
24 match ::std::io::Write::flush(&mut ::std::io::stderr()) {
25 ::core::result::Result::Ok(()) => $crate::prompt!(),
26 ::core::result::Result::Err(e) => ::core::result::Result::Err(::eyre::eyre!("Could not flush stderr: {e}"))
27 }
28 }};
29}
30
31#[macro_export]
35macro_rules! sh_err {
36 ($($args:tt)*) => {
37 $crate::__sh_dispatch!(error $($args)*)
38 };
39}
40
41#[macro_export]
45macro_rules! sh_warn {
46 ($($args:tt)*) => {
47 $crate::__sh_dispatch!(warn $($args)*)
48 };
49}
50
51#[macro_export]
55macro_rules! sh_print {
56 ($($args:tt)*) => {
57 $crate::__sh_dispatch!(print_out $($args)*)
58 };
59
60 ($shell:expr, $($args:tt)*) => {
61 $crate::__sh_dispatch!(print_out $shell, $($args)*)
62 };
63}
64
65#[macro_export]
69macro_rules! sh_eprint {
70 ($($args:tt)*) => {
71 $crate::__sh_dispatch!(print_err $($args)*)
72 };
73
74 ($shell:expr, $($args:tt)*) => {
75 $crate::__sh_dispatch!(print_err $shell, $($args)*)
76 };
77}
78
79#[macro_export]
83macro_rules! sh_println {
84 () => {
85 $crate::sh_print!("\n")
86 };
87
88 ($fmt:literal $($args:tt)*) => {
89 $crate::sh_print!("{}\n", ::core::format_args!($fmt $($args)*))
90 };
91
92 ($shell:expr $(,)?) => {
93 $crate::sh_print!($shell, "\n").expect("failed to write newline")
94 };
95
96 ($shell:expr, $($args:tt)*) => {
97 $crate::sh_print!($shell, "{}\n", ::core::format_args!($($args)*))
98 };
99
100 ($($args:tt)*) => {
101 $crate::sh_print!("{}\n", ::core::format_args!($($args)*))
102 };
103}
104
105#[macro_export]
110macro_rules! sh_status {
111 ($($args:tt)*) => {
112 $crate::sh_eprintln!($($args)*)
113 };
114}
115
116#[macro_export]
126macro_rules! sh_progress {
127 ($($args:tt)*) => {{
128 if $crate::shell::is_err_tty() && !$crate::shell::is_quiet() {
129 let _ = $crate::sh_eprintln!($($args)*);
130 }
131 ::core::result::Result::<(), ::eyre::Report>::Ok(())
132 }};
133}
134
135#[macro_export]
139macro_rules! sh_eprintln {
140 () => {
141 $crate::sh_eprint!("\n")
142 };
143
144 ($fmt:literal $($args:tt)*) => {
145 $crate::sh_eprint!("{}\n", ::core::format_args!($fmt $($args)*))
146 };
147
148 ($shell:expr $(,)?) => {
149 $crate::sh_eprint!($shell, "\n")
150 };
151
152 ($shell:expr, $($args:tt)*) => {
153 $crate::sh_eprint!($shell, "{}\n", ::core::format_args!($($args)*))
154 };
155
156 ($($args:tt)*) => {
157 $crate::sh_eprint!("{}\n", ::core::format_args!($($args)*))
158 };
159}
160
161#[doc(hidden)]
162#[macro_export]
163macro_rules! __sh_dispatch {
164 ($f:ident $fmt:literal $($args:tt)*) => {
165 $crate::__sh_dispatch!(@impl $f &mut *$crate::Shell::get(), $fmt $($args)*)
166 };
167
168 ($f:ident $shell:expr, $($args:tt)*) => {
169 $crate::__sh_dispatch!(@impl $f $shell, $($args)*)
170 };
171
172 ($f:ident $($args:tt)*) => {
173 $crate::__sh_dispatch!(@impl $f &mut *$crate::Shell::get(), $($args)*)
174 };
175
176 (@impl $f:ident $shell:expr, $($args:tt)*) => {
179 match format!($($args)*) {
180 fmt => $crate::Shell::$f($shell, fmt),
181 }
182 };
183}
184
185#[cfg(test)]
186mod tests {
187 #[test]
188 fn macros() -> eyre::Result<()> {
189 sh_err!("err")?;
190 sh_err!("err {}", "arg")?;
191
192 sh_warn!("warn")?;
193 sh_warn!("warn {}", "arg")?;
194
195 sh_print!("print -")?;
196 sh_print!("print {} -", "arg")?;
197
198 sh_println!()?;
199 sh_println!("println")?;
200 sh_println!("println {}", "arg")?;
201
202 sh_eprint!("eprint -")?;
203 sh_eprint!("eprint {} -", "arg")?;
204
205 sh_eprintln!()?;
206 sh_eprintln!("eprintln")?;
207 sh_eprintln!("eprintln {}", "arg")?;
208
209 sh_status!("status")?;
210 sh_status!("status {}", "arg")?;
211
212 sh_progress!("progress")?;
213 sh_progress!("progress {}", "arg")?;
214
215 sh_println!("{:?}", {
216 sh_println!("hi")?;
217 solar::data_structures::fmt::from_fn(|f| {
218 let _ = sh_println!("even more nested");
219 write!(f, "hi 2")
220 })
221 })?;
222
223 Ok(())
224 }
225
226 #[test]
227 fn macros_with_shell() -> eyre::Result<()> {
228 let shell = &mut crate::Shell::new();
229 sh_eprintln!(shell)?;
230 sh_eprintln!(shell,)?;
231 sh_eprintln!(shell, "shelled eprintln")?;
232 sh_eprintln!(shell, "shelled eprintln {}", "arg")?;
233 sh_eprintln!(&mut crate::Shell::new(), "shelled eprintln {}", "arg")?;
234
235 Ok(())
236 }
237
238 #[test]
241 fn routing_contract() -> eyre::Result<()> {
242 let mut shell = crate::Shell::captured();
243
244 sh_print!(&mut shell, "out-print")?;
246 sh_println!(&mut shell, "out-println")?;
247
248 sh_eprint!(&mut shell, "err-print")?;
250 sh_eprintln!(&mut shell, "err-println")?;
251 crate::Shell::warn(&mut shell, "warn-msg")?;
252 crate::Shell::error(&mut shell, "err-msg")?;
253
254 let stdout = std::str::from_utf8(shell.captured_stdout().unwrap()).unwrap();
255 let stderr = std::str::from_utf8(shell.captured_stderr().unwrap()).unwrap();
256
257 assert_eq!(stdout, "out-printout-println\n");
259
260 assert!(stderr.contains("err-print"), "stderr missing eprint: {stderr:?}");
262 assert!(stderr.contains("err-println"), "stderr missing eprintln: {stderr:?}");
263 assert!(stderr.contains("warn-msg"), "stderr missing warn: {stderr:?}");
264 assert!(stderr.contains("err-msg"), "stderr missing error: {stderr:?}");
265 assert!(!stderr.contains("out-print"), "stdout content leaked to stderr: {stderr:?}");
266 assert!(!stderr.contains("out-println"), "stdout content leaked to stderr: {stderr:?}");
267
268 Ok(())
269 }
270
271 #[test]
276 fn quiet_contract() -> eyre::Result<()> {
277 let mut shell = crate::Shell::captured();
278 shell.set_output_mode(crate::shell::OutputMode::Quiet);
279
280 sh_println!(&mut shell, "result")?;
281 sh_eprintln!(&mut shell, "diag")?;
282 crate::Shell::warn(&mut shell, "warned")?;
283 crate::Shell::error(&mut shell, "boom")?;
284
285 let stdout = std::str::from_utf8(shell.captured_stdout().unwrap()).unwrap();
286 let stderr = std::str::from_utf8(shell.captured_stderr().unwrap()).unwrap();
287
288 assert!(stdout.is_empty(), "stdout leaked through --quiet: {stdout:?}");
291 assert!(!stderr.contains("diag"), "eprintln leaked through --quiet: {stderr:?}");
292 assert!(!stderr.contains("warned"), "warn leaked through --quiet: {stderr:?}");
293 assert!(stderr.contains("boom"), "sh_err was suppressed by --quiet: {stderr:?}");
294
295 Ok(())
296 }
297}