anes/
macros.rs

1/// Creates a control sequence.
2///
3/// This macro prepends provided sequence with the control sequence introducer `ESC [` (`\x1B[`).
4///
5/// # Examples
6///
7/// ```
8/// use anes::csi;
9///
10/// assert_eq!(csi!("?1049h"), "\x1B[?1049h");
11/// ```
12#[macro_export]
13macro_rules! csi {
14    ($($arg:expr),*) => { concat!("\x1B[", $($arg),*) };
15}
16
17/// Creates an escape sequence.
18///
19/// This macro prepends provided sequence with the `ESC` (`\x1B`) character.
20///
21/// # Examples
22///
23/// ```
24/// use anes::esc;
25///
26/// assert_eq!(esc!("7"), "\x1B7");
27/// ```
28#[macro_export]
29macro_rules! esc {
30    ($($arg:expr),*) => { concat!("\x1B", $($arg),*) };
31}
32
33/// Creates a select graphic rendition sequence.
34///
35/// This macro prepends provided sequence with the `ESC[` (`\x1B[`) character and appends `m` character.
36///
37/// Also known as Set Graphics Rendition on Linux.
38///
39/// # Examples
40///
41/// ```
42/// use anes::sgr;
43///
44/// assert_eq!(sgr!("0"), "\x1B[0m");
45/// ```
46#[macro_export]
47macro_rules! sgr {
48    ($($arg:expr),*) => { concat!("\x1B[", $($arg),* , "m") };
49}
50
51/// Creates an ANSI sequence.
52///
53/// You can use this macro to create your own ANSI sequence. All `anes` sequences are
54/// created with this macro.
55///
56/// # Examples
57///
58/// An unit struct:
59///
60/// ```
61/// use anes::{esc, sequence};
62///
63/// sequence!(
64///   /// Saves the cursor position.    
65///   struct SaveCursorPosition => esc!("7")    
66/// );
67///
68/// assert_eq!(&format!("{}", SaveCursorPosition), "\x1B7");
69/// ```
70///
71/// An enum:
72///
73/// ```
74/// use anes::{csi, sequence};
75///
76/// sequence!(
77///     /// Clears part of the buffer.
78///     enum ClearBuffer {
79///         /// Clears from the cursor position to end of the screen.
80///         Below => csi!("J"),
81///         /// Clears from the cursor position to beginning of the screen.
82///         Above => csi!("1J"),
83///         /// Clears the entire buffer.
84///         All => csi!("2J"),
85///         /// Clears the entire buffer and all saved lines in the scrollback buffer.
86///         SavedLines => csi!("3J"),
87///     }
88/// );
89///
90/// assert_eq!(&format!("{}", ClearBuffer::Below), "\x1B[J");
91/// assert_eq!(&format!("{}", ClearBuffer::Above), "\x1B[1J");
92/// assert_eq!(&format!("{}", ClearBuffer::All), "\x1B[2J");
93/// assert_eq!(&format!("{}", ClearBuffer::SavedLines), "\x1B[3J");
94/// ```
95///
96/// A struct:
97///
98/// ```
99/// use anes::{csi, sequence};
100///
101/// sequence!(
102///     /// Moves the cursor to the given location (column, row).
103///     ///
104///     /// # Notes
105///     ///
106///     /// Top/left cell is represented as `1, 1`.
107///     struct MoveCursorTo(u16, u16) =>
108///     |this, f| write!(f, csi!("{};{}H"), this.0, this.1)
109/// );
110///
111/// assert_eq!(&format!("{}", MoveCursorTo(10, 5)), "\x1B[10;5H");
112/// ```
113#[macro_export]
114macro_rules! sequence {
115    // Static unit struct
116    (
117        $(#[$meta:meta])*
118        struct $name:ident => $value:expr
119    ) => {
120        $(#[$meta])*
121        #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
122        pub struct $name;
123
124        impl ::std::fmt::Display for $name {
125            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
126                write!(f, $value)
127            }
128        }
129    };
130    // Static enum
131    (
132        $(#[$meta:meta])*
133        enum $name:ident {
134            $(
135                $(#[$variant_meta:meta])*
136                $variant:ident => $variant_value:expr
137            ),*
138            $(,)?
139        }
140    ) => {
141        $(#[$meta])*
142        #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
143        pub enum $name {
144            $(
145                $(#[$variant_meta])*
146                $variant,
147            )*
148        }
149
150        impl ::std::fmt::Display for $name {
151            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
152                write!(f, "{}", match self {
153                    $(
154                        $name::$variant => $variant_value,
155                    )*
156                })
157            }
158        }
159    };
160    // Dynamic struct
161    (
162        $(#[$meta:meta])*
163        struct $type:ident(
164            $($fields:ty),*
165            $(,)?
166        )
167        =>
168        $write:expr
169    ) => {
170        $(#[$meta])*
171        #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
172        pub struct $type($(pub $fields),*);
173
174        impl ::std::fmt::Display for $type {
175            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
176                let write: &dyn Fn(&Self, &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result =
177                    &$write;
178                write(self, f)
179            }
180        }
181    };
182}
183
184/// Queues ANSI escape sequence(s).
185///
186/// What does queue mean exactly? All sequences are queued with the
187/// `write!($dst, "{}", $sequence)` macro without calling the
188/// [`flush`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush) method.
189///
190/// Check the [`execute!`](macro.execute.html) macro if you'd like execute them
191/// immediately (call the `flush` method after all sequences were queued).
192///
193/// # Examples
194///
195/// ```no_run
196/// use std::io::{Result, Write};
197///
198/// use anes::queue;
199///
200/// fn main() -> Result<()> {
201///     let mut stdout = std::io::stdout();
202///     queue!(
203///         &mut stdout,
204///         anes::SaveCursorPosition,
205///         anes::MoveCursorTo(10, 10)
206///     )?;
207///
208///     queue!(&mut stdout, anes::RestoreCursorPosition,)?;
209///
210///     // ANSI sequences are not executed until you flush it!
211///     stdout.flush()
212/// }
213/// ```
214#[macro_export]
215macro_rules! queue {
216    ($dst:expr, $($sequence:expr),* $(,)?) => {{
217        let mut error = None;
218
219        $(
220            if let Err(e) = write!($dst, "{}", $sequence) {
221                error = Some(e);
222            }
223        )*
224
225        if let Some(error) = error {
226            Err(error)
227        } else {
228            Ok(())
229        }
230    }}
231}
232
233/// Executes ANSI escape sequence(s).
234///
235/// What does execute mean exactly? All sequences are queued with the
236/// `write!($dst, "{}", $sequence)` macro and then the
237/// [`flush`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush) method
238/// is called.
239///
240/// Check the [`queue!`](macro.queue.html) macro if you'd like queue sequences
241/// and execute them later.
242///
243/// ```no_run
244/// use std::io::{Result, Write};
245///
246/// use anes::execute;
247///
248/// fn main() -> Result<()> {
249///     let mut stdout = std::io::stdout();
250///     execute!(
251///         &mut stdout,
252///         anes::SaveCursorPosition,
253///         anes::MoveCursorTo(10, 10),
254///         anes::RestoreCursorPosition
255///     )?;
256///     Ok(())
257/// }
258/// ```
259#[macro_export]
260macro_rules! execute {
261    ($dst:expr, $($sequence:expr),* $(,)?) => {{
262        if let Err(e) = $crate::queue!($dst, $($sequence),*) {
263            Err(e)
264        } else {
265            $dst.flush()
266        }
267    }}
268}
269
270#[cfg(test)]
271macro_rules! test_sequences {
272    (
273        $(
274            $name:ident(
275                $($left:expr => $right:expr),*
276                $(,)?
277            )
278        ),*
279        $(,)?
280    ) => {
281        #[cfg(test)]
282        mod tests {
283            use super::*;
284
285            $(
286                #[test]
287                fn $name() {
288                    $(
289                        assert_eq!(&format!("{}", $left), $right);
290                    )*
291                }
292            )*
293        }
294    }
295}
296
297#[cfg(test)]
298mod tests {
299    use std::io::{Error, ErrorKind, Write};
300
301    #[test]
302    fn csi() {
303        assert_eq!(csi!("foo"), "\x1B[foo");
304    }
305
306    #[test]
307    fn esc() {
308        assert_eq!(esc!("bar"), "\x1Bbar");
309    }
310
311    #[test]
312    fn sgr() {
313        assert_eq!(sgr!("bar"), "\x1B[barm");
314    }
315
316    #[test]
317    fn static_struct_sequence() {
318        sequence!(
319            struct TestSeq => csi!("foo")
320        );
321
322        assert_eq!(&format!("{}", TestSeq), "\x1B[foo");
323    }
324
325    #[test]
326    fn static_enum_sequence() {
327        sequence!(
328            enum TestSeq {
329                Foo => csi!("foo"),
330                Bar => esc!("bar"),
331            }
332        );
333
334        assert_eq!(&format!("{}", TestSeq::Foo), "\x1B[foo");
335        assert_eq!(&format!("{}", TestSeq::Bar), "\x1Bbar");
336    }
337
338    #[test]
339    fn dynamic_struct_sequence() {
340        sequence!(
341            struct TestSeq(u16) =>
342            |this, f| write!(f, csi!("foo{}bar"), this.0)
343        );
344
345        assert_eq!(&format!("{}", TestSeq(10)), "\x1B[foo10bar");
346    }
347
348    #[test]
349    fn queue_allows_trailing_comma() {
350        let mut writer = Writer::default();
351
352        assert!(queue!(&mut writer, "foo",).is_ok());
353        assert_eq!(&writer.buffer, "foo");
354    }
355
356    #[test]
357    fn queue_writes_single_sequence() {
358        let mut writer = Writer::default();
359
360        assert!(queue!(&mut writer, "foo").is_ok());
361        assert_eq!(&writer.buffer, "foo");
362    }
363
364    #[test]
365    fn queue_writes_multiple_sequences() {
366        let mut writer = Writer::default();
367
368        assert!(queue!(&mut writer, "foo", "bar", "baz").is_ok());
369        assert_eq!(&writer.buffer, "foobarbaz");
370    }
371
372    #[test]
373    fn queue_does_not_flush() {
374        let mut writer = Writer::default();
375
376        assert!(queue!(&mut writer, "foo").is_ok());
377        assert!(!writer.flushed);
378        assert!(writer.flushed_buffer.is_empty());
379    }
380
381    #[test]
382    fn execute_allows_trailing_comma() {
383        let mut writer = Writer::default();
384
385        assert!(execute!(&mut writer, "foo",).is_ok());
386        assert_eq!(&writer.flushed_buffer, "foo");
387    }
388
389    #[test]
390    fn execute_writes_single_sequence() {
391        let mut writer = Writer::default();
392
393        assert!(execute!(&mut writer, "foo").is_ok());
394        assert_eq!(&writer.flushed_buffer, "foo");
395    }
396
397    #[test]
398    fn execute_writes_multiple_sequences() {
399        let mut writer = Writer::default();
400
401        assert!(execute!(&mut writer, "foo", "bar", "baz").is_ok());
402        assert_eq!(&writer.flushed_buffer, "foobarbaz");
403    }
404
405    #[test]
406    fn execute_does_flush() {
407        let mut writer = Writer::default();
408
409        assert!(execute!(&mut writer, "foo").is_ok());
410        assert!(writer.flushed);
411        assert_eq!(&writer.flushed_buffer, "foo");
412        assert!(writer.buffer.is_empty());
413    }
414
415    #[derive(Default)]
416    struct Writer {
417        buffer: String,
418        flushed_buffer: String,
419        flushed: bool,
420    }
421
422    impl Write for Writer {
423        fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
424            let s = std::str::from_utf8(buf).map_err(|_| ErrorKind::InvalidData)?;
425
426            self.buffer.push_str(s);
427            Ok(s.len())
428        }
429
430        fn flush(&mut self) -> Result<(), Error> {
431            self.flushed_buffer = self.buffer.clone();
432            self.buffer = String::new();
433            self.flushed = true;
434            Ok(())
435        }
436    }
437}