1use std::default::Default;
7use term::{self, Terminal};
8
9#[derive(Copy, Clone, Default, PartialEq, Eq)]
10pub struct Style {
11 bits: u64,
12}
13
14macro_rules! declare_styles {
15 ($($style:ident,)*) => {
16 #[derive(Copy, Clone)]
17 #[allow(non_camel_case_types)]
18 #[allow(clippy::upper_case_acronyms)]
19 enum StyleBit {
20 $($style,)*
21 }
22
23 $(
24 pub const $style: Style = Style { bits: 1 << (StyleBit::$style as u64) };
25 )*
26 }
27}
28
29pub const DEFAULT: Style = Style { bits: 0 };
30
31declare_styles! {
32 FG_BLACK,
34 FG_BLUE,
35 FG_BRIGHT_BLACK,
36 FG_BRIGHT_BLUE,
37 FG_BRIGHT_CYAN,
38 FG_BRIGHT_GREEN,
39 FG_BRIGHT_MAGENTA,
40 FG_BRIGHT_RED,
41 FG_BRIGHT_WHITE,
42 FG_BRIGHT_YELLOW,
43 FG_CYAN,
44 FG_GREEN,
45 FG_MAGENTA,
46 FG_RED,
47 FG_WHITE,
48 FG_YELLOW,
49
50 BG_BLACK,
52 BG_BLUE,
53 BG_BRIGHT_BLACK,
54 BG_BRIGHT_BLUE,
55 BG_BRIGHT_CYAN,
56 BG_BRIGHT_GREEN,
57 BG_BRIGHT_MAGENTA,
58 BG_BRIGHT_RED,
59 BG_BRIGHT_WHITE,
60 BG_BRIGHT_YELLOW,
61 BG_CYAN,
62 BG_GREEN,
63 BG_MAGENTA,
64 BG_RED,
65 BG_WHITE,
66 BG_YELLOW,
67
68 BOLD,
70 DIM,
71 ITALIC,
72 UNDERLINE,
73 BLINK,
74 STANDOUT,
75 REVERSE,
76 SECURE,
77}
78
79impl Style {
80 pub fn new() -> Style {
81 Style::default()
82 }
83
84 pub fn with(self, other_style: Style) -> Style {
85 Style {
86 bits: self.bits | other_style.bits,
87 }
88 }
89
90 pub fn contains(self, other_style: Style) -> bool {
91 self.with(other_style) == self
92 }
93
94 pub fn apply<T: Terminal + ?Sized>(self, term: &mut T) -> term::Result<()> {
98 term.reset()?;
99
100 macro_rules! fg_color {
101 ($color:expr, $term_color:ident) => {
102 if self.contains($color) {
103 if term.supports_color() {
104 term.fg(term::color::$term_color)?;
105 }
106 }
107 };
108 }
109
110 fg_color!(FG_BLACK, BLACK);
111 fg_color!(FG_BLUE, BLUE);
112 fg_color!(FG_BRIGHT_BLACK, BRIGHT_BLACK);
113 fg_color!(FG_BRIGHT_BLUE, BRIGHT_BLUE);
114 fg_color!(FG_BRIGHT_CYAN, BRIGHT_CYAN);
115 fg_color!(FG_BRIGHT_GREEN, BRIGHT_GREEN);
116 fg_color!(FG_BRIGHT_MAGENTA, BRIGHT_MAGENTA);
117 fg_color!(FG_BRIGHT_RED, BRIGHT_RED);
118 fg_color!(FG_BRIGHT_WHITE, BRIGHT_WHITE);
119 fg_color!(FG_BRIGHT_YELLOW, BRIGHT_YELLOW);
120 fg_color!(FG_CYAN, CYAN);
121 fg_color!(FG_GREEN, GREEN);
122 fg_color!(FG_MAGENTA, MAGENTA);
123 fg_color!(FG_RED, RED);
124 fg_color!(FG_WHITE, WHITE);
125 fg_color!(FG_YELLOW, YELLOW);
126
127 macro_rules! bg_color {
128 ($color:expr, $term_color:ident) => {
129 if self.contains($color) {
130 if term.supports_color() {
131 term.bg(term::color::$term_color)?;
132 }
133 }
134 };
135 }
136
137 bg_color!(BG_BLACK, BLACK);
138 bg_color!(BG_BLUE, BLUE);
139 bg_color!(BG_BRIGHT_BLACK, BRIGHT_BLACK);
140 bg_color!(BG_BRIGHT_BLUE, BRIGHT_BLUE);
141 bg_color!(BG_BRIGHT_CYAN, BRIGHT_CYAN);
142 bg_color!(BG_BRIGHT_GREEN, BRIGHT_GREEN);
143 bg_color!(BG_BRIGHT_MAGENTA, BRIGHT_MAGENTA);
144 bg_color!(BG_BRIGHT_RED, BRIGHT_RED);
145 bg_color!(BG_BRIGHT_WHITE, BRIGHT_WHITE);
146 bg_color!(BG_BRIGHT_YELLOW, BRIGHT_YELLOW);
147 bg_color!(BG_CYAN, CYAN);
148 bg_color!(BG_GREEN, GREEN);
149 bg_color!(BG_MAGENTA, MAGENTA);
150 bg_color!(BG_RED, RED);
151 bg_color!(BG_WHITE, WHITE);
152 bg_color!(BG_YELLOW, YELLOW);
153
154 macro_rules! attr {
155 ($attr:expr, $term_attr:expr) => {
156 if self.contains($attr) {
157 let attr = $term_attr;
158 if term.supports_attr(attr) {
159 term.attr(attr)?;
160 }
161 }
162 };
163 }
164
165 attr!(BOLD, term::Attr::Bold);
166 attr!(DIM, term::Attr::Dim);
167 attr!(ITALIC, term::Attr::Italic(true));
168 attr!(UNDERLINE, term::Attr::Underline(true));
169 attr!(BLINK, term::Attr::Blink);
170 attr!(STANDOUT, term::Attr::Standout(true));
171 attr!(REVERSE, term::Attr::Reverse);
172 attr!(SECURE, term::Attr::Secure);
173
174 Ok(())
175 }
176}
177
178pub struct StyleCursor<'term, T: ?Sized + Terminal> {
181 current_style: Style,
182 term: &'term mut T,
183}
184
185impl<'term, T: ?Sized + Terminal> StyleCursor<'term, T> {
186 pub fn new(term: &'term mut T) -> term::Result<StyleCursor<'term, T>> {
187 let current_style = Style::default();
188 current_style.apply(term)?;
189 Ok(StyleCursor {
190 current_style,
191 term,
192 })
193 }
194
195 pub fn term(&mut self) -> &mut T {
196 self.term
197 }
198
199 pub fn set_style(&mut self, style: Style) -> term::Result<()> {
200 if style != self.current_style {
201 style.apply(self.term)?;
202 self.current_style = style;
203 }
204 Ok(())
205 }
206}