term_lab/
styles.rs

1//! ## Important
2//! *Please check if you're printing your text on a terminal, otherwise it probably won't work.*
3//! This module is only for terminals that support ANSI escape codes.
4
5use std::fmt;
6use crate::def_strings;
7
8#[cps::cps]
9/// Pass a file to a macro.
10/// As this 
11macro_rules! pass_file {
12  ($macro:ident!($source:expr)) => 
13    let $($file:tt)* = cps::include!($source) in
14  {
15    $macro! { $($file)* }
16  };
17}
18
19def_strings! {
20  #[doc = "Reset all styles"]
21  RESET = "0";
22  #[doc = "Increase the font weight"]
23  BOLD = "1";
24  #[doc = "Decrease the font weight"]
25  BOLD_OFF = "22";
26  #[doc = "Decrease the intensity"]
27  FAINT = "2";
28  #[doc = "Reset the intensity"]
29  FAINT_OFF = "22";
30  ITALICS = "3";
31  ITALICS_OFF = "23";
32  UNDERLINE = "4";
33  DOUBLE_UNDERLINE = "21";
34  UNDERLINE_OFF = "24";
35  #[doc = "Make the text blink"]
36  SLOW_BLINK = "5";
37  #[doc = "Make the text blink"]
38  FAST_BLINK = "6";
39  #[doc = "Reset the blink"]
40  BLINK_OFF = "25";
41  INVERSE = "7";
42  INVERSE_OFF = "27";
43  INVISIBLE = "8";
44  INVISIBLE_OFF = "28";
45  STRIKETHROUGH = "9";
46  STRIKETHROUGH_OFF = "29";
47  OVERLINE = "53";
48  OVERLINE_OFF = "55";
49
50  BLACK = "30";
51  RED = "31";
52  GREEN = "32";
53  YELLOW = "33";
54  BLUE = "34";
55  MAGENTA = "35";
56  CYAN = "36";
57  WHITE = "37";
58  BRIGHT_BLACK = "90";
59  BRIGHT_RED = "91";
60  BRIGHT_GREEN = "92";
61  BRIGHT_YELLOW = "93";
62  BRIGHT_BLUE = "94";
63  BRIGHT_MAGENTA = "95";
64  BRIGHT_CYAN = "96";
65  BRIGHT_WHITE = "97";
66  FOREGROUND_OFF = "39";
67
68  BLACK_BG = "40";
69  RED_BG = "41";
70  GREEN_BG = "42";
71  YELLOW_BG = "43";
72  BLUE_BG = "44";
73  MAGENTA_BG = "45";
74  CYAN_BG = "46";
75  WHITE_BG = "47";
76  BRIGHT_BLACK_BG = "100";
77  BRIGHT_RED_BG = "101";
78  BRIGHT_GREEN_BG = "102";
79  BRIGHT_YELLOW_BG = "103";
80  BRIGHT_BLUE_BG = "104";
81  BRIGHT_MAGENTA_BG = "105";
82  BRIGHT_CYAN_BG = "106";
83  BRIGHT_WHITE_BG = "107";
84  END_BG = "49";
85
86  ORANGE = "38;2;230;126;20";
87  ORANGE_BG = "48;2;230;126;20";
88  BLUEBERRY = "38;2;80;80;255";
89  BLUEBERRY_BG = "48;2;80;80;255";
90  PINK = "38;2;255;0;200";
91  PINK_BG = "48;2;255;0;200";
92}
93
94#[inline(always)]
95/// Resets the text to its default style.
96pub fn reset() -> String {
97  format!("\x1b[{}m", RESET)
98}
99
100#[derive(Debug, Clone, Copy)]
101pub struct Rgb(pub u8, pub u8, pub u8);
102
103macro_rules! style_fn {
104  ($($(#[$tag:meta])? fn $fn_name:ident() -> ($start:ident, $end:ident);)*) => {
105    $(
106      $(#[$tag])?
107      #[inline(always)]
108      /// Displays the text based on the corresponding style predefined by the terminal.
109      fn $fn_name(&self) -> String {
110        format!("\x1b[{}m{}\x1b[{}m", $start, self, $end)
111      }
112    )*
113  }
114}
115
116/// A trait that provides styling for terminals that support ANSI escape codes.
117pub trait Stylize: fmt::Display {
118  pass_file!(style_fn!("src/styles.in"));
119
120  #[inline(always)]
121  /// Applies a foreground color to the string.
122  fn rgb(&self, Rgb(red, green, blue): Rgb) -> String {
123    format!(
124      "\x1b[38;2;{};{};{}m{}\x1b[{}m",
125      red, green, blue,
126      self,
127      FOREGROUND_OFF
128    )
129  }
130  #[inline(always)]
131  /// Applies a background color to the string.
132  fn rgb_bg(&self, Rgb(red, green, blue): Rgb) -> String {
133    format!(
134      "\x1b[48;2;{};{};{}m{}\x1b[{}m",
135      red, green, blue,
136      self,
137      END_BG
138    )
139  }
140
141  #[inline(always)]
142  /// Creates a `StyleBuilder`, use it to combine multiple styles. Don't forget to call `build`!
143  fn style(&self) -> StyleBuilder {
144    StyleBuilder::new(self.to_string())
145  }
146
147  #[inline(always)]
148  /// Applies the italics and strikethrough styles.
149  fn deprecated(&self) -> String {
150    format!("\x1b[3;9m{}\x1b[23;29m", self)
151  }
152  #[inline(always)]
153  /// Makes the text bold and red.
154  fn error(&self) -> String {
155    self.style().red().bold().build()
156  }
157  #[inline(always)]
158  /// Makes the text bold and yellow.
159  fn warning(&self) -> String {
160    self.style().yellow().bold().build()
161  }
162  #[inline(always)]
163  /// Makes the text bold and green.
164  fn success(&self) -> String {
165    self.style().green().bold().build()
166  }
167}
168
169impl Stylize for String {}
170impl Stylize for &str {}
171impl Stylize for Box<str> {}
172
173#[derive(Debug)]
174pub struct StyleBuilder {
175  text: String,
176  start: Vec<Box<str>>,
177  end: Vec<Box<str>>,
178}
179
180macro_rules! lazy_style_fn {
181  ($($(#[$tag:meta])? fn $fn_name:ident() -> ($start:ident, $end:ident);)*) => {
182    $(
183      $(#[$tag])?
184      #[inline(always)]
185      /// <br>
186      /// *Remember that applying a color overwrites the previous one.*
187      pub fn $fn_name(mut self) -> Self {
188        self.start.push($start.into());
189        self.end.push($end.into());
190        self
191      }
192    )*
193  }
194}
195
196impl StyleBuilder {
197  #[inline]
198  fn new(text: String) -> Self {
199    Self {
200      text,
201      start: vec![],
202      end: vec![],
203    }
204  }
205  #[inline(always)]
206  pub fn build(self) -> String {
207    format!("\x1b[{}m{}\x1b[{}m", self.start.join(";"), self.text, self.end.join(";"))
208  }
209  pass_file!(lazy_style_fn!("src/styles.in"));
210
211  pub fn rgb(mut self, Rgb(red, green, blue): Rgb) -> Self {
212    self.start.push(format!("38;2;{};{};{}", red, green, blue).into_boxed_str());
213    self.end.push(FOREGROUND_OFF.into());
214    self
215  }
216  pub fn rgb_bg(mut self, Rgb(red, green, blue): Rgb) -> Self {
217    self.start.push(format!("48;2;{};{};{}", red, green, blue).into_boxed_str());
218    self.end.push(END_BG.into());
219    self
220  }
221}