1/// Append a the first few characters of an ANSI escape code to the given string.
2#[macro_export]
3#[doc(hidden)]
4macro_rules! csi {
5 ($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) };
6}
7
8/// Queues one or more command(s) for further execution.
9///
10/// Queued commands must be flushed to the underlying device to be executed.
11/// This generally happens in the following cases:
12///
13/// * When `flush` is called manually on the given type implementing `io::Write`.
14/// * The terminal will `flush` automatically if the buffer is full.
15/// * Each line is flushed in case of `stdout`, because it is line buffered.
16///
17/// # Arguments
18///
19/// - [std::io::Writer](std::io::Write)
20///
21/// ANSI escape codes are written on the given 'writer', after which they are flushed.
22///
23/// - [Command](./trait.Command.html)
24///
25/// One or more commands
26///
27/// # Examples
28///
29/// ```rust
30/// use std::io::{Write, stdout};
31/// use crossterm::{queue, style::Print};
32///
33/// let mut stdout = stdout();
34///
35/// // `Print` will executed executed when `flush` is called.
36/// queue!(stdout, Print("foo".to_string()));
37///
38/// // some other code (no execution happening here) ...
39///
40/// // when calling `flush` on `stdout`, all commands will be written to the stdout and therefore executed.
41/// stdout.flush();
42///
43/// // ==== Output ====
44/// // foo
45/// ```
46///
47/// Have a look over at the [Command API](./index.html#command-api) for more details.
48///
49/// # Notes
50///
51/// In case of Windows versions lower than 10, a direct WinAPI call will be made.
52/// The reason for this is that Windows versions lower than 10 do not support ANSI codes,
53/// and can therefore not be written to the given `writer`.
54/// Therefore, there is no difference between [execute](macro.execute.html)
55/// and [queue](macro.queue.html) for those old Windows versions.
56///
57#[macro_export]
58macro_rules! queue {
59 ($writer:expr $(, $command:expr)* $(,)?) => {{
60 use ::std::io::Write;
61
62 // This allows the macro to take both mut impl Write and &mut impl Write.
63 Ok($writer.by_ref())
64 $(.and_then(|writer| $crate::QueueableCommand::queue(writer, $command)))*
65 .map(|_| ())
66 }}
67}
68
69/// Executes one or more command(s).
70///
71/// # Arguments
72///
73/// - [std::io::Writer](std::io::Write)
74///
75/// ANSI escape codes are written on the given 'writer', after which they are flushed.
76///
77/// - [Command](./trait.Command.html)
78///
79/// One or more commands
80///
81/// # Examples
82///
83/// ```rust
84/// use std::io::{Write, stdout};
85/// use crossterm::{execute, style::Print};
86///
87/// // will be executed directly
88/// execute!(stdout(), Print("sum:\n".to_string()));
89///
90/// // will be executed directly
91/// execute!(stdout(), Print("1 + 1= ".to_string()), Print((1+1).to_string()));
92///
93/// // ==== Output ====
94/// // sum:
95/// // 1 + 1 = 2
96/// ```
97///
98/// Have a look over at the [Command API](./index.html#command-api) for more details.
99///
100/// # Notes
101///
102/// * In the case of UNIX and Windows 10, ANSI codes are written to the given 'writer'.
103/// * In case of Windows versions lower than 10, a direct WinAPI call will be made.
104/// The reason for this is that Windows versions lower than 10 do not support ANSI codes,
105/// and can therefore not be written to the given `writer`.
106/// Therefore, there is no difference between [execute](macro.execute.html)
107/// and [queue](macro.queue.html) for those old Windows versions.
108#[macro_export]
109macro_rules! execute {
110 ($writer:expr $(, $command:expr)* $(,)? ) => {{
111 use ::std::io::Write;
112
113 // Queue each command, then flush
114 $crate::queue!($writer $(, $command)*)
115 .and_then(|()| {
116 ::std::io::Write::flush($writer.by_ref())
117 })
118 }}
119}
120
121#[doc(hidden)]
122#[macro_export]
123macro_rules! impl_display {
124 (for $($t:ty),+) => {
125 $(impl ::std::fmt::Display for $t {
126 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
127 $crate::command::execute_fmt(f, self)
128 }
129 })*
130 }
131}
132
133#[doc(hidden)]
134#[macro_export]
135macro_rules! impl_from {
136 ($from:path, $to:expr) => {
137 impl From<$from> for ErrorKind {
138 fn from(e: $from) -> Self {
139 $to(e)
140 }
141 }
142 };
143}
144
145#[cfg(test)]
146mod tests {
147 use std::io;
148 use std::str;
149
150 // Helper for execute tests to confirm flush
151 #[derive(Default, Debug, Clone)]
152 pub(self) struct FakeWrite {
153 buffer: String,
154 flushed: bool,
155 }
156
157 impl io::Write for FakeWrite {
158 fn write(&mut self, content: &[u8]) -> io::Result<usize> {
159 let content = str::from_utf8(content)
160 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
161 self.buffer.push_str(content);
162 self.flushed = false;
163 Ok(content.len())
164 }
165
166 fn flush(&mut self) -> io::Result<()> {
167 self.flushed = true;
168 Ok(())
169 }
170 }
171
172 #[cfg(not(windows))]
173 mod unix {
174 use std::fmt;
175
176 use super::FakeWrite;
177 use crate::command::Command;
178
179 pub struct FakeCommand;
180
181 impl Command for FakeCommand {
182 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
183 f.write_str("cmd")
184 }
185 }
186
187 #[test]
188 fn test_queue_one() {
189 let mut result = FakeWrite::default();
190 queue!(&mut result, FakeCommand).unwrap();
191 assert_eq!(&result.buffer, "cmd");
192 assert!(!result.flushed);
193 }
194
195 #[test]
196 fn test_queue_many() {
197 let mut result = FakeWrite::default();
198 queue!(&mut result, FakeCommand, FakeCommand).unwrap();
199 assert_eq!(&result.buffer, "cmdcmd");
200 assert!(!result.flushed);
201 }
202
203 #[test]
204 fn test_queue_trailing_comma() {
205 let mut result = FakeWrite::default();
206 queue!(&mut result, FakeCommand, FakeCommand,).unwrap();
207 assert_eq!(&result.buffer, "cmdcmd");
208 assert!(!result.flushed);
209 }
210
211 #[test]
212 fn test_execute_one() {
213 let mut result = FakeWrite::default();
214 execute!(&mut result, FakeCommand).unwrap();
215 assert_eq!(&result.buffer, "cmd");
216 assert!(result.flushed);
217 }
218
219 #[test]
220 fn test_execute_many() {
221 let mut result = FakeWrite::default();
222 execute!(&mut result, FakeCommand, FakeCommand).unwrap();
223 assert_eq!(&result.buffer, "cmdcmd");
224 assert!(result.flushed);
225 }
226
227 #[test]
228 fn test_execute_trailing_comma() {
229 let mut result = FakeWrite::default();
230 execute!(&mut result, FakeCommand, FakeCommand,).unwrap();
231 assert_eq!(&result.buffer, "cmdcmd");
232 assert!(result.flushed);
233 }
234 }
235
236 #[cfg(windows)]
237 mod windows {
238 use std::fmt;
239
240 use std::cell::RefCell;
241
242 use super::FakeWrite;
243 use crate::command::Command;
244
245 // We need to test two different APIs: WinAPI and the write api. We
246 // don't know until runtime which we're supporting (via
247 // Command::is_ansi_code_supported), so we have to test them both. The
248 // CI environment hopefully includes both versions of windows.
249
250 // WindowsEventStream is a place for execute_winapi to push strings,
251 // when called.
252 type WindowsEventStream = Vec<&'static str>;
253
254 struct FakeCommand<'a> {
255 // Need to use a refcell because we want execute_winapi to be able
256 // push to the vector, but execute_winapi take &self.
257 stream: RefCell<&'a mut WindowsEventStream>,
258 value: &'static str,
259 }
260
261 impl<'a> FakeCommand<'a> {
262 fn new(stream: &'a mut WindowsEventStream, value: &'static str) -> Self {
263 Self {
264 value,
265 stream: RefCell::new(stream),
266 }
267 }
268 }
269
270 impl<'a> Command for FakeCommand<'a> {
271 fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
272 f.write_str(self.value)
273 }
274
275 fn execute_winapi(&self) -> std::io::Result<()> {
276 self.stream.borrow_mut().push(self.value);
277 Ok(())
278 }
279 }
280
281 // Helper function for running tests against either WinAPI or an
282 // io::Write.
283 //
284 // This function will execute the `test` function, which should
285 // queue some commands against the given FakeWrite and
286 // WindowsEventStream. It will then test that the correct data sink
287 // was populated. It does not currently check is_ansi_code_supported;
288 // for now it simply checks that one of the two streams was correctly
289 // populated.
290 //
291 // If the stream was populated, it tests that the two arrays are equal.
292 // If the writer was populated, it tests that the contents of the
293 // write buffer are equal to the concatenation of `stream_result`.
294 fn test_harness(
295 stream_result: &[&'static str],
296 test: impl FnOnce(&mut FakeWrite, &mut WindowsEventStream) -> std::io::Result<()>,
297 ) {
298 let mut stream = WindowsEventStream::default();
299 let mut writer = FakeWrite::default();
300
301 if let Err(err) = test(&mut writer, &mut stream) {
302 panic!("Error returned from test function: {:?}", err);
303 }
304
305 // We need this for type inference, for whatever reason.
306 const EMPTY_RESULT: [&str; 0] = [];
307
308 // TODO: confirm that the correct sink was used, based on
309 // is_ansi_code_supported
310 match (writer.buffer.is_empty(), stream.is_empty()) {
311 (true, true) if stream_result == EMPTY_RESULT => {}
312 (true, true) => panic!(
313 "Neither the event stream nor the writer were populated. Expected {:?}",
314 stream_result
315 ),
316
317 // writer is populated
318 (false, true) => {
319 // Concat the stream result to find the string result
320 let result: String = stream_result.iter().copied().collect();
321 assert_eq!(result, writer.buffer);
322 assert_eq!(&stream, &EMPTY_RESULT);
323 }
324
325 // stream is populated
326 (true, false) => {
327 assert_eq!(stream, stream_result);
328 assert_eq!(writer.buffer, "");
329 }
330
331 // Both are populated
332 (false, false) => panic!(
333 "Both the writer and the event stream were written to.\n\
334 Only one should be used, based on is_ansi_code_supported.\n\
335 stream: {stream:?}\n\
336 writer: {writer:?}",
337 stream = stream,
338 writer = writer,
339 ),
340 }
341 }
342
343 #[test]
344 fn test_queue_one() {
345 test_harness(&["cmd1"], |writer, stream| {
346 queue!(writer, FakeCommand::new(stream, "cmd1"))
347 })
348 }
349
350 #[test]
351 fn test_queue_some() {
352 test_harness(&["cmd1", "cmd2"], |writer, stream| {
353 queue!(
354 writer,
355 FakeCommand::new(stream, "cmd1"),
356 FakeCommand::new(stream, "cmd2"),
357 )
358 })
359 }
360
361 #[test]
362 fn test_many_queues() {
363 test_harness(&["cmd1", "cmd2", "cmd3"], |writer, stream| {
364 queue!(writer, FakeCommand::new(stream, "cmd1"))?;
365 queue!(writer, FakeCommand::new(stream, "cmd2"))?;
366 queue!(writer, FakeCommand::new(stream, "cmd3"))
367 })
368 }
369 }
370}
371