Skip to main content

termal/
lib.rs

1//! Library for working with ansi codes to create beutiful terminal outputs.
2//!
3//! The main focus of this library are the macros [`formatc`], [`printc`],
4//! [`printcln`], [`eprintc`], [`eprintcln`], [`writecln`] adn [`writecln`].
5//! They can be used in the same way as you would use the standard rust macros
6//! [`format`], [`print`], [`println`], [`eprint`], [`eprintln`], [`write`] and
7//! [`writeln`]. In addition the macros in this crate have special syntax for
8//! encoding terminal commands.
9//!
10//! ## The macros
11//! For all these macros the following applies:
12//!
13//! If content braces `{}` starts with `'` (e.g. `"{'command}hello"`) than the
14//! content is interpreted by this crate, otherwised it is interpreted by the
15//! [`format`] macro.
16//!
17//! The content can contain one or more commands that will expand directly to a
18//! string literal.
19//!
20//! For most of the color commands the folowing is true:
21//! - The commands have short aliases (e.g. `w` is alias for `white`)
22//! - Some of the commands can be reset, the resetting command has the same
23//!   name but it is prepended with `_` (e.g. use `italic` to set font style to
24//!   italic and than use `_italic` to unset the italic font style)
25//! - Some commands take arguments and some of the arguments are optional. The
26//!   arguments are passed to the command by typing them directly after the
27//!   command and separate them by `,` (e.g. `move_to0,0` moves the cursor to
28//!   0, 0. This is also the default value so you can just use `move_to,`).
29//!
30//! ### RGB color commands
31//! The colors can be set either by the [color commands](#color-commands) or
32//! by hex color:
33//!
34//! The hex color starts with `#` and can contain either 1, 2, 3 or 6 hex
35//! digits. They are interpreted as follows:
36//! - **6 digits:** normal 6 digit RGB color (e.g `#FF0000` is pure red)
37//! - **3 digits:** 3 digit RGB color, each digit is repeated twice (e.g.
38//!   `#ABC` has the same result as `#AABBCC`)
39//! - **2 digits:** the two digits are repeated 3 times to form one of 256
40//!   shades of gray (e.g. `#AB` has the same result as `#ABABAB`)
41//! - **1 digit:** the digit is repeated 6 times to form one of 16 shades of
42//!   gray (e.g. `#A` has the same result as `#AAAAAA`)
43//!
44//! If you want to set the foregorund color, you just type the hex (e.g.
45//! `#FF0000` will set the foreground color to pure red). In order to set the
46//! background color, you can append `_` to the color (e.g. `#FF0000_` will set
47//! the background color to pure red).
48//!
49//! If you want to set the underline color, just type the same as background
50//! color, but use `u` instead of the `_`.
51//!
52//! ### Ascii commands
53//! - `bell`: console bell (create sound)
54//! - `backspace`: move left by one
55//! - `htab`, `tab`: horizontal tabulator
56//! - `move_down_scrl`, `mds`: move down by one line scrolling if needed
57//! - `newline`, `nl`: move to the start of the next line
58//! - `vtab`: vertical tabulator
59//! - `carriage_return` | `cr`: move to the start of the current line
60//!
61//! ### Commands for moving the cursor
62//! - `move_to`, `mt`: moves the cursor to the given position, has two
63//!   arguments, default values are `0`.
64//! - `move_up`, `mu`: moves the cursor up by the given amount, has one
65//!   argument, default value is `1`
66//! - `move_down`, `md`: moves the cursor down by the given amount, has one
67//!   argument, default value is `1`
68//! - `move_right`, `mr`: moves the cursor right by the given amount, has one
69//!   argument, default value is `1`
70//! - `move_left`, `ml`: moves the cursor left by the given amount, has one
71//!   argument, default value is `1`
72//! - `set_down`, `sd`: moves the cursor to the start of line n lines down, has
73//!   one argument, default value is `1`
74//! - `set_up`, `su`: moves the cursor to the start of line n lines up, has one
75//!   argument, default value is `1`
76//! - `move_to_column`, `mc`: moves the cursor to the given x coordinate, has
77//!   one argument, default value is `0`
78//! + `move_up_scrl`, `mus`: moves the cursor up by one line, scrolling if
79//!   needed
80//! + `save_cur`, `save`, `s`: saves the current cursor position (single slot,
81//!   not stack)
82//! + `load_cur`, `load`, `l`: loads the last saved cursor position
83//!
84//! ### Erase commands
85//! - `erase_to_end`, `e_`: erases from the cursor to the end of the screen
86//! - `erase_from_start`, `_e`: erases from the start of the screen to the cursor
87//! - `erase_screen`, `_e_`: erases the whole screen
88//! - `erase_all`, `e`: erases the whole screen and the scroll buffer
89//! - `erase_ln_end`, `el_`: erases from the cursor to the end of the line
90//! - `erase_ln_start`, `_el`: erases from the start of the line to the cursor
91//! - `erase_line`, `erase_ln`, `_el_`, `el`: erases the current line
92//!
93//! ### Font style and color command
94//! + `reset`, `_`: resets all colors and styles
95//!
96//! ### Font style commands
97//! - `bold`: sets style to bold
98//! - `faint`, `f`: sets style to faint
99//! - `italic`, `i`: sets style to italic
100//! - `underline`, `u`: sets style to underline
101//! - `blinking`, `blink`: sets style to blinking
102//! - `inverse`: sets style to inverse (swap background and foreground)
103//! - `invisible`, `invis`: sets the style to invisible (foreground and
104//!   background are same)
105//! - `striketrough`, `strike`: sets the style to striketrough
106//! - `double_underline`, `dunderline`, `dun`: sets the style to double
107//!   underline
108//! - `overline`, `ol`: sets the style to overline
109//! + `_bold`: resets bold and faint
110//! + `_italic`, `_i`: resets italic
111//! + `_underline`, `_u`: resets underline and double underline
112//! + `_blinking`, `_blink`: resets blinking
113//! + `_inverse`: resets inverse
114//! + `_invisible`, `_invis`: resets invisible
115//! + `_striketrough`, `_strike`: resets striketrough
116//! + `_overline`, `_ol`: resets overline
117//!
118//! ### Color commands
119//! - `black_fg`, `black`, `bl`: sets the foreground to black
120//! - `white_fg`, `white`, `w`: sets the foreground to white
121//! - `gray_fg`, `gray`, `gr`: sets the foreground to green
122//! - `bright_gray_fg`, `bgray`, `bgr`: sets the foreground to bright gray
123//! + `red_fg`, `red`, `r`: sets the foreground to red
124//! + `green_fg`, `green`, `g`: sets the foreground to green
125//! + `yellow_fg`, `yellow`, `y`: sets the foreground to yellow
126//! + `magenta_fg`, `magenta`, `m`: sets the foreground to magenta
127//! + `cyan_fg`, `cyan`, `c`: sets the foreground to cyan
128//! - `dark_red_fg`, `dred`, `dr`: sets the foreground to dark red
129//! - `dark_green_fg`, `dgreen`, `dg`: sets the foreground to dark green
130//! - `dark_yellow_fg`, `dyellow`, `dy`: sets the foreground to dark yellow
131//! - `dark_magenta_fg`, `dmagenta`, `dm`: sets the foreground to dark magenta
132//! - `dark_cyan_fg`, `dcyan`, `dc`: sets the foreground to dark cyan
133//! + `_fg`: resets the foreground color
134//! - `black_bg`, `blackb`, `blb`: sets the background to black
135//! - `white_bg`, `whiteb`, `wb`: sets the background to white
136//! - `gray_bg`, `grayb`, `grb`: sets the background to green
137//! - `bright_gray_bg`, `bgrayb`, `bgrb`: sets the background to bright gray
138//! + `red_bg`, `redb`, `rb`: sets the background to red
139//! + `green_bg`, `greenb`, `gb`: sets the background to green
140//! + `yellow_bg`, `yellowb`, `yb`: sets the background to yellow
141//! + `magenta_bg`, `magentab`, `mb`: sets the background to magenta
142//! + `cyan_bg`, `cyanb`, `cb`: sets the background to cyan
143//! - `dark_red_bg`, `dredb`, `drb`: sets the background to dark red
144//! - `dark_green_bg`, `dgreenb`, `dgb`: sets the background to dark green
145//! - `dark_yellow_bg`, `dyellowb`, `dyb`: sets the background to dark yellow
146//! - `dark_magenta_bg`, `dmagentab`, `dmb`: sets the background to dark
147//!   magenta
148//! - `dark_cyan_bg`, `dcyanb`, `dcb`: sets the background to dark cyan
149//! + `_bg`: resets the background
150//! + `_ucolor`, `_uc`: resets the underline color
151//! - `fg`: sets the foreground color to one of the 256 colors, has one
152//!   argument
153//! - `bg`: sets the background color to one of the 256 colors, has one
154//!   argument
155//! - `ucolor`, `uc`: sets the underline color to one of the 256 colors, has
156//!   one argument.
157//!
158//! ### Other
159//! - `line_wrap`, `wrap`: enable line wrapping
160//! - `_line_wrap`, `_wrap`: disable line wrapping
161//! + `hide_cursor`, `nocur`: hide the cursor
162//! + `show_cursor`, `_nocur`: show the cursor
163//! + `save_screen`, `sscr`: saves the screen view (single slot, not stack)
164//! + `load_screen`, `lscr`: restores the last saved screen view
165//! + `alt_buf`, `abuf`: enable alternative buffer
166//! + `_alt_buf`, `_abuf`: disable alternative buffer
167//!
168//! ### Compound
169//! - `clear`, `cls`: erases the screen and the buffer and moves the cursor to the
170//!   topleft position (equivalent to `e mt,`)
171//!
172//! ## The uncoloring macros
173//! There are also macros that will skip the terminal commands. These can be
174//! useful when you need to conditionaly print with colors or without colors.
175//!
176//! The macros have the same names but they have `n` before the `c` to signify
177//! *no color*: [`formatnc`], [`printnc`], [`printncln`], [`eprintnc`],
178//! [`eprintncln`], [`writenc`] and [`writencln`].
179//!
180//! ## The conditionally coloring macros
181//! Theese are same as the normal coloring macros except they take additional
182//! first argument that tells whether the output should be colored or not.
183//!
184//! They have the same names as the uncoloring macros but they have `m` instead
185//! of the `n` to signify *maybe color*:[`formatmc`], [`printmc`],
186//! [`printmcln`], [`eprintmc`], [`eprintmcln`], [`writemc`] and [`writemcln`].
187//!
188//! ## Automatically coloring macros.
189//! Theese are same as the normal coloring macros except the will not color the
190//! output if it detects that the output stream is not terminal.
191//!
192//! They have the same name as the normal macros, but they have `a` before `c`
193//! to signify *automatic coloring*: [`printac`], [`printacln`], [`eprintac`]
194//! and [`eprintacln`].
195//!
196//! ## Examples
197//! ### With macro
198//! ```rust
199//! use termal::*;
200//!
201//! // you can use a special macro to inline the color codes, this will write
202//! // italic text with yellow foreground and reset at the end.
203//! printcln!("{'yellow italic}hello{'reset}");
204//!
205//! // the macro also supports standard formatting
206//! printcln!("{'yellow italic}{}{'reset}", "hello");
207//!
208//! // you can also use short versions of the codes
209//! printcln!("{'y i}{}{'_}", "hello");
210//!
211//! // you can also use true colors with their hex codes
212//! printcln!("{'#dd0 i}{}{'_}", "hello");
213//! ```
214//!
215//! ### Without macro
216//! ```rust
217//! // Move cursor to position column 5 on line 7 and write 'hello' in italic
218//! // yellow
219//!
220//! use termal::codes::*;
221//! use termal::*;
222//!
223//! println!("{}{YELLOW_FG}{ITALIC}hello{RESET}", move_to!(5, 7));
224//! ```
225//!
226//! ## Other macros
227//!
228//! The macros such as `move_to!` can accept either literals or dynamic values.
229//! Its main feature is that if you supply literals, it expands to a string
230//! literal with the ansi code.
231//! If you however supply dynamic values it expands to a `format!` macro:
232//! ```rust
233//! use termal::*;
234//!
235//! let a = move_to!(5, 7);
236//! // expands to:
237//! let a = "\x1b[5;7H";
238//!
239//! let b = move_to!(2 + 3, 7);
240//! // expands to:
241//! let b = format!("\x1b[{};{}H", 2 + 3, 7);
242//! ```
243//!
244//! If you know the values for the arguments you can also use the `*c` macros:
245//! ```rust
246//! use termal::formatc;
247//!
248//! // the spaces, or the lack of them is important
249//! let a = formatc!("{'move_to5,7}");
250//! ```
251//!
252//! ### Gradients
253//! Youn can create gradients with the function `termal::gradient`:
254//! ```rust
255//! use termal::*;
256//!
257//! // This will create foreground gradient from the rgb color `(250, 50, 170)`
258//! // to the rgb color `(180, 50, 240)`
259//! printcln!("{}{'_}",gradient("BonnyAD9", (250, 50, 170), (180, 50, 240)));
260//! ```
261
262#![cfg_attr(docsrs, feature(doc_cfg))]
263
264pub use termal_core::*;
265pub use termal_proc as proc;
266
267/// Works as [`println!`], in addition can generate ansi escape codes.
268/// To generate the ansi codes use `"{'...}"`.
269///
270/// # Examples
271/// ```
272/// use termal::*;
273/// // Print 'hello' in yellow:
274/// printcln!("{'yellow}hello{'reset}");
275/// ```
276#[macro_export]
277macro_rules! printcln {
278    ($l:literal $(,)?) => {
279        println!("{}", $crate::proc::colorize!($l));
280    };
281    ($l:literal, $($e:expr),+ $(,)?) => {
282        println!("{}", $crate::proc::colorize!($l, $($e),+));
283    };
284}
285
286/// Works as [`print!`], in addition can generate ansi escape codes.
287/// To generate the ansi codes use `"{'...}"`.
288///
289/// # Examples
290/// ```
291/// use termal::*;
292/// // Print 'hello' in yellow:
293/// printc!("{'yellow}hello{'reset}");
294/// ```
295#[macro_export]
296macro_rules! printc {
297    ($l:literal $(,)?) => {
298        print!("{}", $crate::proc::colorize!($l));
299    };
300    ($l:literal, $($e:expr),+ $(,)?) => {
301        print!("{}", $crate::proc::colorize!($l, $($e),+));
302    };
303}
304
305/// Works as [`eprintln!`], in addition can generate ansi escape codes.
306/// To generate the ansi codes use `"{'...}"`.
307///
308/// # Examples
309/// ```
310/// use termal::*;
311/// // Print 'hello' in yellow:
312/// eprintcln!("{'yellow}hello{'reset}");
313/// ```
314#[macro_export]
315macro_rules! eprintcln {
316    ($l:literal $(,)?) => {
317        eprintln!("{}", $crate::proc::colorize!($l));
318    };
319    ($l:literal, $($e:expr),+ $(,)?) => {
320        eprintln!("{}", $crate::proc::colorize!($l, $($e),+));
321    };
322}
323
324/// Works as [`eprint!`], in addition can generate ansi escape codes.
325/// To generate the ansi codes use `"{'...}"`.
326///
327/// # Examples
328/// ```
329/// use termal::*;
330/// // Print 'hello' in yellow:
331/// eprintc!("{'yellow}hello{'reset}");
332/// ```
333#[macro_export]
334macro_rules! eprintc {
335    ($l:literal $(,)?) => {
336        eprint!("{}", $crate::proc::colorize!($l));
337    };
338    ($l:literal, $($e:expr),+ $(,)?) => {
339        eprint!("{}", $crate::proc::colorize!($l, $($e),+));
340    };
341}
342
343/// Works as [`format!`], in addition can generate ansi escape codes.
344/// To generate the ansi codes use `"{'...}"`.
345///
346/// # Examples
347/// ```
348/// use termal::*;
349/// // Generate 'hello' in yellow:
350/// formatc!("{'yellow}hello{'reset}");
351/// ```
352#[macro_export]
353macro_rules! formatc {
354    ($l:literal $(,)?) => {
355        $crate::proc::colorize!($l)
356    };
357    ($l:literal, $($e:expr),+ $(,)?) => {
358        $crate::proc::colorize!($l, $($e),+)
359    };
360}
361
362/// Works as [`writeln!`], in addition can generate ansi escape codes.
363/// To generate the ansi codes use `"{'...}"`.
364#[macro_export]
365macro_rules! writecln {
366    ($f:expr, $l:literal $(,)?) => {
367        writeln!($f, "{}", $crate::proc::colorize!($l))
368    };
369    ($f:expr, $l:literal, $($e:expr),+ $(,)?) => {
370        writeln!($f, "{}", $crate::proc::colorize!($l, $($e),+))
371    };
372}
373
374/// Works as [`write!`], in addition can generate ansi escape codes.
375/// To generate the ansi codes use `"{'...}"`.
376#[macro_export]
377macro_rules! writec {
378    ($f:expr, $l:literal $(,)?) => {
379        write!($f, "{}", $crate::proc::colorize!($l))
380    };
381    ($f:expr, $l:literal, $($e:expr),+ $(,)?) => {
382        write!($f, "{}", $crate::proc::colorize!($l, $($e),+))
383    };
384}
385
386/// Works as [`println!`], skips terminal commands in `"{'...}"`.
387///
388/// # Examples
389/// ```
390/// use termal::*;
391/// // Print 'hello' (not in yellow, the terminal commands are skipped):
392/// printncln!("{'yellow}hello{'reset}");
393/// ```
394#[macro_export]
395macro_rules! printncln {
396    ($l:literal $(,)?) => {
397        println!("{}", $crate::proc::uncolor!($l));
398    };
399    ($l:literal, $($e:expr),+ $(,)?) => {
400        println!("{}", $crate::proc::uncolor!($l, $($e),+));
401    };
402}
403
404/// Works as [`print!`], skips terminal commands in `"{'...}"`.
405///
406/// # Examples
407/// ```
408/// use termal::*;
409/// // Print 'hello' (not in yellow, the terminal commands are skipped):
410/// printnc!("{'yellow}hello{'reset}");
411/// ```
412#[macro_export]
413macro_rules! printnc {
414    ($l:literal $(,)?) => {
415        print!("{}", $crate::proc::uncolor!($l));
416    };
417    ($l:literal, $($e:expr),+ $(,)?) => {
418        print!("{}", $crate::proc::uncolor!($l, $($e),+));
419    };
420}
421
422/// Works as [`eprintln!`], skips terminal commands in `"{'...}"`.
423///
424/// # Examples
425/// ```
426/// use termal::*;
427/// // Print 'hello' (not in yellow, the terminal commands are skipped):
428/// eprintncln!("{'yellow}hello{'reset}");
429/// ```
430#[macro_export]
431macro_rules! eprintncln {
432    ($l:literal $(,)?) => {
433        eprintln!("{}", $crate::proc::uncolor!($l));
434    };
435    ($l:literal, $($e:expr),+ $(,)?) => {
436        eprintln!("{}", $crate::proc::uncolor!($l, $($e),+));
437    };
438}
439
440/// Works as [`eprint!`], skips terminal commands in `"{'...}"`.
441///
442/// # Examples
443/// ```
444/// use termal::*;
445/// // Print 'hello' (not in yellow, the terminal commands are skipped):
446/// printnc!("{'yellow}hello{'reset}");
447/// ```
448#[macro_export]
449macro_rules! eprintnc {
450    ($l:literal $(,)?) => {
451        eprint!("{}", $crate::proc::uncolor!($l));
452    };
453    ($l:literal, $($e:expr),+ $(,)?) => {
454        eprint!("{}", $crate::proc::uncolor!($l, $($e),+));
455    };
456}
457
458/// Works as [`format!`], skips terminal commands in `"{'...}"`.
459///
460/// # Examples
461/// ```
462/// use termal::*;
463/// // Generate 'hello' (not in yellow, the terminal commands are skipped):
464/// printcln!("{'yellow}hello{'reset}");
465/// ```
466#[macro_export]
467macro_rules! formatnc {
468    ($l:literal $(,)?) => {
469        $crate::proc::uncolor!($l)
470    };
471    ($l:literal, $($e:expr),+ $(,)?) => {
472        $crate::proc::uncolor!($l, $($e),+)
473    };
474}
475
476/// Works as [`writeln!`], skips terminal commands in `"{'...}"`.
477#[macro_export]
478macro_rules! writencln {
479    ($f:expr, $l:literal $(,)?) => {
480        writeln!($f, "{}", $crate::proc::uncolor!($l))
481    };
482    ($f:expr, $l:literal, $($e:expr),+ $(,)?) => {
483        writeln!($f, "{}", $crate::proc::uncolor!($l, $($e),+))
484    };
485}
486
487/// Works as [`write!`], skips terminal commands in `"{'...}"`.
488#[macro_export]
489macro_rules! writenc {
490    ($f:expr, $l:literal $(,)?) => {
491        write!($f, "{}", $crate::proc::uncolor!($l))
492    };
493    ($f:expr, $l:literal, $($e:expr),+ $(,)?) => {
494        write!($f, "{}", $crate::proc::uncolor!($l, $($e),+))
495    };
496}
497
498/// Works as [`println!`], conditionally skips terminal commands in `"{'...}"`.
499///
500/// # Examples
501/// ```
502/// use termal::*;
503/// // Print 'hello' (not in yellow, the terminal commands are skipped):
504/// printmcln!(false, "{'yellow}hello{'reset}");
505/// ```
506#[macro_export]
507macro_rules! printmcln {
508    ($cond:expr, $l:literal $(,)?) => {
509        if $cond {
510            println!("{}", $crate::proc::colorize!($l));
511        } else {
512            println!("{}", $crate::proc::uncolor!($l));
513        }
514    };
515    ($cond:expr, $l:literal, $($e:expr),+ $(,)?) => {
516        if $cond {
517            println!("{}", $crate::proc::colorize!($l, $($e),+));
518        } else {
519            println!("{}", $crate::proc::uncolor!($l, $($e),+));
520        }
521    };
522}
523
524/// Works as [`print!`], conditionally skips terminal commands in `"{'...}"`.
525///
526/// # Examples
527/// ```
528/// use termal::*;
529/// // Print 'hello' (not in yellow, the terminal commands are skipped):
530/// printmc!(false, "{'yellow}hello{'reset}");
531/// ```
532#[macro_export]
533macro_rules! printmc {
534    ($cond:expr, $l:literal $(,)?) => {
535        if $cond {
536            print!("{}", $crate::proc::colorize!($l));
537        } else {
538            print!("{}", $crate::proc::uncolor!($l));
539        }
540    };
541    ($cond:expr, $l:literal, $($e:expr),+ $(,)?) => {
542        if $cond {
543            print!("{}", $crate::proc::colorize!($l, $($e),+));
544        } else {
545            print!("{}", $crate::proc::uncolor!($l, $($e),+));
546        }
547    };
548}
549
550/// Works as [`eprintln!`], conditionally skips terminal commands in
551/// `"{'...}"`.
552///
553/// # Examples
554/// ```
555/// use termal::*;
556/// // Print 'hello' (not in yellow, the terminal commands are skipped):
557/// eprintmcln!(false, "{'yellow}hello{'reset}");
558/// ```
559#[macro_export]
560macro_rules! eprintmcln {
561    ($cond:expr, $l:literal $(,)?) => {
562        if $cond {
563            eprintln!("{}", $crate::proc::colorize!($l));
564        } else {
565            eprintln!("{}", $crate::proc::uncolor!($l));
566        }
567    };
568    ($cond:expr, $l:literal, $($e:expr),+ $(,)?) => {
569        if $cond {
570            eprintln!("{}", $crate::proc::colorize!($l, $($e),+));
571        } else {
572            eprintln!("{}", $crate::proc::uncolor!($l, $($e),+));
573        }
574    };
575}
576
577/// Works as [`eprint!`], conditionally skips terminal commands in `"{'...}"`.
578///
579/// # Examples
580/// ```
581/// use termal::*;
582/// // Print 'hello' (not in yellow, the terminal commands are skipped):
583/// printmc!(false, "{'yellow}hello{'reset}");
584/// ```
585#[macro_export]
586macro_rules! eprintmc {
587    ($cond:expr, $l:literal $(,)?) => {
588        if $cond {
589            eprint!("{}", $crate::proc::colorize!($l));
590        } else {
591            eprint!("{}", $crate::proc::uncolor!($l));
592        }
593    };
594    ($cond:expr, $l:literal, $($e:expr),+ $(,)?) => {
595        if $cond {
596            eprint!("{}", $crate::proc::colorize!($l, $($e),+));
597        } else {
598            eprint!("{}", $crate::proc::uncolor!($l, $($e),+));
599        }
600    };
601}
602
603/// Works as [`format!`], conditionally skips terminal commands in `"{'...}"`.
604///
605/// # Examples
606/// ```
607/// use termal::*;
608/// // Generate 'hello' (not in yellow, the terminal commands are skipped):
609/// printmcln!(false, "{'yellow}hello{'reset}");
610/// ```
611#[macro_export]
612macro_rules! formatmc {
613    ($cond:expr, $l:literal $(,)?) => {
614        if $cond {
615            $crate::proc::colorize!($l)
616        } else {
617            $crate::proc::uncolor!($l)
618        }
619    };
620    ($cond:expr, $l:literal, $($e:expr),+ $(,)?) => {
621        if $cond {
622            $crate::proc::colorize!($l, $($e),+)
623        } else {
624            $crate::proc::uncolor!($l, $($e),+)
625        }
626    };
627}
628
629/// Works as [`writeln!`], conditionally skips terminal commands in `"{'...}"`.
630#[macro_export]
631macro_rules! writemcln {
632    ($f:expr, $cond:expr, $l:literal $(,)?) => {
633        if $cond {
634            writeln!($f, "{}", $crate::proc::colorize!($l))
635        } else {
636            writeln!($f, "{}", $crate::proc::uncolor!($l))
637        }
638    };
639    ($f:expr, $cond:expr, $l:literal, $($e:expr),+ $(,)?) => {
640        if $cond {
641            writeln!($f, "{}", $crate::proc::colorize!($l, $($e),+))
642        } else {
643            writeln!($f, "{}", $crate::proc::uncolor!($l, $($e),+))
644        }
645    };
646}
647
648/// Works as [`write!`], conditionally skips terminal commands in `"{'...}"`.
649#[macro_export]
650macro_rules! writemc {
651    ($f:expr, $cond:expr, $l:literal $(,)?) => {
652        if $cond {
653            write!($f, "{}", $crate::proc::colorize!($l))
654        } else {
655            write!($f, "{}", $crate::proc::uncolor!($l))
656        }
657    };
658    ($f:expr, $cond:expr, $l:literal, $($e:expr),+ $(,)?) => {
659        if $cond {
660            write!($f, "{}", $crate::proc::colorize!($l, $($e),+))
661        } else {
662            write!($f, "{}", $crate::proc::uncolor!($l, $($e),+))
663        }
664    };
665}
666
667/// Works as [`println!`], in addition can generate ansi escape codes.
668/// To generate the ansi codes use `"{'...}"`. This will not use the ansi codes
669/// if stdout is not terminal.
670///
671/// # Examples
672/// ```
673/// use termal::*;
674/// // Print 'hello' in yellow:
675/// printcln!("{'yellow}hello{'reset}");
676/// ```
677#[macro_export]
678macro_rules! printacln {
679    ($l:literal $(,)?) => {
680        $crate::printmcln!(
681            std::io::IsTerminal::is_terminal(&std::io::stdout()),
682            $l,
683        );
684    };
685    ($l:literal, $($e:expr),+ $(,)?) => {
686        $crate::printmcln!(
687            std::io::IsTerminal::is_terminal(&std::io::stdout()),
688            $l,
689            $($e),+,
690        );
691    };
692}
693
694/// Works as [`print!`], in addition can generate ansi escape codes.
695/// To generate the ansi codes use `"{'...}"`. This will not use the ansi codes
696/// if stdout is not terminal.
697///
698/// # Examples
699/// ```
700/// use termal::*;
701/// // Print 'hello' in yellow:
702/// printc!("{'yellow}hello{'reset}");
703/// ```
704#[macro_export]
705macro_rules! printac {
706    ($l:literal $(,)?) => {
707        $crate::printmc!(
708            std::io::IsTerminal::is_terminal(&std::io::stdout()),
709            $l,
710        );
711    };
712    ($l:literal, $($e:expr),+ $(,)?) => {
713        $crate::printmc!(
714            std::io::IsTerminal::is_terminal(&std::io::stdout()),
715            $l,
716            $($e),+,
717        );
718    };
719}
720
721/// Works as [`eprintln!`], in addition can generate ansi escape codes.
722/// To generate the ansi codes use `"{'...}"`. This will not use the ansi codes
723/// if stdout is not terminal.
724///
725/// # Examples
726/// ```
727/// use termal::*;
728/// // Print 'hello' in yellow:
729/// eprintcln!("{'yellow}hello{'reset}");
730/// ```
731#[macro_export]
732macro_rules! eprintacln {
733    ($l:literal $(,)?) => {
734        $crate::eprintmcln!(
735            std::io::IsTerminal::is_terminal(&std::io::stderr()),
736            $l,
737        );
738    };
739    ($l:literal, $($e:expr),+ $(,)?) => {
740        $crate::eprintmcln!(
741            std::io::IsTerminal::is_terminal(&std::io::stderr()),
742            $l,
743            $($e),+,
744        );
745    };
746}
747
748/// Works as [`eprint!`], in addition can generate ansi escape codes.
749/// To generate the ansi codes use `"{'...}"`. This will not use the ansi codes
750/// if stdout is not terminal.
751///
752/// # Examples
753/// ```
754/// use termal::*;
755/// // Print 'hello' in yellow:
756/// eprintc!("{'yellow}hello{'reset}");
757/// ```
758#[macro_export]
759macro_rules! eprintac {
760    ($l:literal $(,)?) => {
761        $crate::eprintmc!(
762            std::io::IsTerminal::is_terminal(&std::io::stderr()),
763            $l,
764        );
765    };
766    ($l:literal, $($e:expr),+ $(,)?) => {
767        $crate::eprintmc!(
768            std::io::IsTerminal::is_terminal(&std::io::stderr()),
769            $l,
770            $($e),+,
771        );
772    };
773}
774
775#[cfg(test)]
776mod tests {
777    use std::{
778        fmt::Display,
779        io::{Write, stdout},
780    };
781
782    use super::*;
783
784    #[test]
785    fn test_gradient() {
786        print!("Expect 'BonnyAD9' as pink to magenta gradient: ");
787        printcln!(
788            "{}{'_}",
789            gradient("BonnyAD9", (250, 50, 170), (180, 50, 240)),
790        );
791        _ = stdout().flush();
792    }
793
794    #[test]
795    fn test_printacln() {
796        let s = "Hello";
797        let num = 4;
798        print!("Expect 'Hello 4' in yellow: ");
799        printacln!("{'y}{s} {num}{'_}");
800        _ = stdout().flush();
801    }
802
803    #[test]
804    fn test_formatnc() {
805        let s = "Hello";
806        let num = 4;
807        let r = formatnc!("{'y}{s} {num}{'_}");
808        assert_eq!(r, "Hello 4");
809    }
810
811    #[test]
812    fn test_m() {
813        let s = "Hello";
814        let num = 4;
815        assert_eq!(
816            formatmc!(true, "{'y}{s} {num}{'_}"),
817            formatc!("{'y}{s} {num}{'_}")
818        );
819        assert_eq!(
820            formatmc!(false, "{'y}{s} {num}{'_}"),
821            formatnc!("{'y}{s} {num}{'_}")
822        );
823    }
824
825    #[cfg(feature = "term_text")]
826    #[test]
827    fn term_text() {
828        use term_text::TermText;
829
830        let txt = TermText::new(gradient("Hello", (0, 0, 0), (255, 255, 255)));
831
832        assert_eq!(5, txt.display_char_cnt());
833        assert_eq!(5, txt.display_bytes_cnt());
834        assert_eq!("Hello", txt.strip_control());
835    }
836
837    #[test]
838    fn test_write() {
839        struct Lol {}
840
841        impl Display for Lol {
842            fn fmt(
843                &self,
844                f: &mut std::fmt::Formatter<'_>,
845            ) -> std::fmt::Result {
846                writec!(f, "{'y}hello{'_}")
847            }
848        }
849
850        assert_eq!(format!("{}", Lol {}), formatc!("{'y}hello{'_}"))
851    }
852}