Coverage Report

Created: 2026-04-14 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/anes-0.1.6/src/macros.rs
Line
Count
Source
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]
13
macro_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]
29
macro_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]
47
macro_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]
114
macro_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
0
            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
126
0
                write!(f, $value)
127
0
            }
Unexecuted instantiation: <anes::sequences::buffer::SwitchBufferToAlternate as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::SaveCursorPosition as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::ReportCursorPosition as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::buffer::SwitchBufferToNormal as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::RestoreCursorPosition as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::terminal::EnableMouseEvents as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::DisableCursorBlinking as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::attribute::ResetAttributes as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::HideCursor as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::terminal::DisableMouseEvents as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::ShowCursor as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::EnableCursorBlinking as core::fmt::Display>::fmt
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
0
            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
152
0
                write!(f, "{}", match self {
153
                    $(
154
                        $name::$variant => $variant_value,
155
                    )*
156
                })
157
0
            }
Unexecuted instantiation: <anes::sequences::buffer::ClearBuffer as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::buffer::ClearLine as core::fmt::Display>::fmt
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
0
            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
176
0
                let write: &dyn Fn(&Self, &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result =
177
0
                    &$write;
178
0
                write(self, f)
179
0
            }
Unexecuted instantiation: <anes::sequences::terminal::ResizeTextArea as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::MoveCursorDown as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::MoveCursorRight as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::MoveCursorToColumn as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::MoveCursorLeft as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::MoveCursorToNextLine as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::MoveCursorToPreviousLine as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::color::SetForegroundColor as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::MoveCursorTo as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::cursor::MoveCursorUp as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::color::SetBackgroundColor as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::buffer::ScrollBufferUp as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::attribute::SetAttribute as core::fmt::Display>::fmt
Unexecuted instantiation: <anes::sequences::buffer::ScrollBufferDown as core::fmt::Display>::fmt
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]
215
macro_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]
260
macro_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)]
271
macro_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)]
298
mod 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
}