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}
438