foundry_common/io/
macros.rs

1/// Prints a message to [`stdout`][std::io::stdout] and reads a line from stdin into a String.
2///
3/// Returns `Result<T>`, so sometimes `T` must be explicitly specified, like in `str::parse`.
4///
5/// # Examples
6///
7/// ```no_run
8/// use foundry_common::prompt;
9///
10/// let response: String = prompt!("Would you like to continue? [y/N] ")?;
11/// if !matches!(response.as_str(), "y" | "Y") {
12///     return Ok(())
13/// }
14/// # Ok::<(), Box<dyn std::error::Error>>(())
15/// ```
16#[macro_export]
17macro_rules! prompt {
18    () => {
19        $crate::stdin::parse_line()
20    };
21
22    ($($tt:tt)+) => {{
23        let _ = $crate::sh_print!($($tt)+);
24        match ::std::io::Write::flush(&mut ::std::io::stdout()) {
25            ::core::result::Result::Ok(()) => $crate::prompt!(),
26            ::core::result::Result::Err(e) => ::core::result::Result::Err(::eyre::eyre!("Could not flush stdout: {e}"))
27        }
28    }};
29}
30
31/// Prints a formatted error to stderr.
32///
33/// **Note**: will log regardless of the verbosity level.
34#[macro_export]
35macro_rules! sh_err {
36    ($($args:tt)*) => {
37        $crate::__sh_dispatch!(error $($args)*)
38    };
39}
40
41/// Prints a formatted warning to stderr.
42///
43/// **Note**: if `verbosity` is set to `Quiet`, this is a no-op.
44#[macro_export]
45macro_rules! sh_warn {
46    ($($args:tt)*) => {
47        $crate::__sh_dispatch!(warn $($args)*)
48    };
49}
50
51/// Prints a raw formatted message to stdout.
52///
53/// **Note**: if `verbosity` is set to `Quiet`, this is a no-op.
54#[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/// Prints a raw formatted message to stderr.
66///
67/// **Note**: if `verbosity` is set to `Quiet`, this is a no-op.
68#[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/// Prints a raw formatted message to stdout, with a trailing newline.
80///
81/// **Note**: if `verbosity` is set to `Quiet`, this is a no-op.
82#[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/// Prints a raw formatted message to stderr, with a trailing newline.
106///
107/// **Note**: if `verbosity` is set to `Quiet`, this is a no-op.
108#[macro_export]
109macro_rules! sh_eprintln {
110    () => {
111        $crate::sh_eprint!("\n")
112    };
113
114    ($fmt:literal $($args:tt)*) => {
115        $crate::sh_eprint!("{}\n", ::core::format_args!($fmt $($args)*))
116    };
117
118    ($shell:expr $(,)?) => {
119        $crate::sh_eprint!($shell, "\n")
120    };
121
122    ($shell:expr, $($args:tt)*) => {
123        $crate::sh_eprint!($shell, "{}\n", ::core::format_args!($($args)*))
124    };
125
126    ($($args:tt)*) => {
127        $crate::sh_eprint!("{}\n", ::core::format_args!($($args)*))
128    };
129}
130
131#[doc(hidden)]
132#[macro_export]
133macro_rules! __sh_dispatch {
134    ($f:ident $fmt:literal $($args:tt)*) => {
135        $crate::__sh_dispatch!(@impl $f &mut *$crate::Shell::get(), $fmt $($args)*)
136    };
137
138    ($f:ident $shell:expr, $($args:tt)*) => {
139        $crate::__sh_dispatch!(@impl $f $shell, $($args)*)
140    };
141
142    ($f:ident $($args:tt)*) => {
143        $crate::__sh_dispatch!(@impl $f &mut *$crate::Shell::get(), $($args)*)
144    };
145
146    // Ensure that the global shell lock is held for as little time as possible.
147    // Also avoids deadlocks in case of nested calls.
148    (@impl $f:ident $shell:expr, $($args:tt)*) => {
149        match ::core::format_args!($($args)*) {
150            fmt => $crate::Shell::$f($shell, fmt),
151        }
152    };
153}
154
155#[cfg(test)]
156mod tests {
157    #[test]
158    fn macros() -> eyre::Result<()> {
159        sh_err!("err")?;
160        sh_err!("err {}", "arg")?;
161
162        sh_warn!("warn")?;
163        sh_warn!("warn {}", "arg")?;
164
165        sh_print!("print -")?;
166        sh_print!("print {} -", "arg")?;
167
168        sh_println!()?;
169        sh_println!("println")?;
170        sh_println!("println {}", "arg")?;
171
172        sh_eprint!("eprint -")?;
173        sh_eprint!("eprint {} -", "arg")?;
174
175        sh_eprintln!()?;
176        sh_eprintln!("eprintln")?;
177        sh_eprintln!("eprintln {}", "arg")?;
178
179        sh_println!("{:?}", {
180            sh_println!("hi")?;
181            "nested"
182        })?;
183
184        Ok(())
185    }
186
187    #[test]
188    fn macros_with_shell() -> eyre::Result<()> {
189        let shell = &mut crate::Shell::new();
190        sh_eprintln!(shell)?;
191        sh_eprintln!(shell,)?;
192        sh_eprintln!(shell, "shelled eprintln")?;
193        sh_eprintln!(shell, "shelled eprintln {}", "arg")?;
194        sh_eprintln!(&mut crate::Shell::new(), "shelled eprintln {}", "arg")?;
195
196        Ok(())
197    }
198}