1use std::{env, io::IsTerminal};
2
3use owo_colors::Style;
4
5#[derive(Debug, Clone)]
17pub struct GraphicalTheme {
18 pub characters: ThemeCharacters,
20 pub styles: ThemeStyles,
22}
23
24fn force_color() -> bool {
25 env::var("CI").is_ok() || env::var("FORCE_COLOR").is_ok_and(|env| env != "0")
27}
28
29impl Default for GraphicalTheme {
30 fn default() -> Self {
31 if force_color() {
32 return Self::unicode();
33 }
34 match std::env::var("NO_COLOR") {
35 _ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => {
36 Self::none()
37 }
38 Ok(string) if string != "0" => Self::unicode_nocolor(),
39 _ => Self::unicode(),
40 }
41 }
42}
43
44impl GraphicalTheme {
45 pub fn new(is_terminal: bool) -> Self {
46 if force_color() {
47 return Self::unicode();
48 }
49 match std::env::var("NO_COLOR") {
50 _ if !is_terminal => Self::none(),
51 Ok(string) if string != "0" => Self::unicode_nocolor(),
52 _ => Self::unicode(),
53 }
54 }
55
56 pub fn ascii() -> Self {
58 Self { characters: ThemeCharacters::ascii(), styles: ThemeStyles::ansi() }
59 }
60
61 pub fn unicode() -> Self {
70 Self { characters: ThemeCharacters::unicode(), styles: ThemeStyles::rgb() }
71 }
72
73 pub fn unicode_nocolor() -> Self {
76 Self { characters: ThemeCharacters::unicode(), styles: ThemeStyles::none() }
77 }
78
79 pub fn none() -> Self {
85 Self { characters: ThemeCharacters::ascii(), styles: ThemeStyles::none() }
86 }
87}
88
89#[derive(Debug, Clone)]
94pub struct ThemeStyles {
95 pub error: Style,
97 pub warning: Style,
99 pub advice: Style,
101 pub help: Style,
103 pub note: Style,
105 pub link: Style,
107 pub linum: Style,
109 pub highlights: Vec<Style>,
112}
113
114fn style() -> Style {
115 Style::new()
116}
117
118impl ThemeStyles {
119 pub fn rgb() -> Self {
122 Self {
123 error: style().fg_rgb::<225, 80, 80>().bold(), warning: style().fg_rgb::<244, 191, 117>().bold(),
125 advice: style().fg_rgb::<106, 159, 181>(),
126 help: style().fg_rgb::<106, 159, 181>(),
127 note: style().fg_rgb::<106, 159, 181>(),
128 link: style().fg_rgb::<92, 157, 255>().bold(),
129 linum: style().dimmed(),
130 highlights: vec![
131 style().fg_rgb::<246, 87, 248>(),
132 style().fg_rgb::<30, 201, 212>(),
133 style().fg_rgb::<145, 246, 111>(),
134 ],
135 }
136 }
137
138 pub fn ansi() -> Self {
140 Self {
141 error: style().red(),
142 warning: style().yellow(),
143 advice: style().cyan(),
144 help: style().cyan(),
145 note: style().cyan(),
146 link: style().cyan().underline().bold(),
147 linum: style().dimmed(),
148 highlights: vec![
149 style().magenta().bold(),
150 style().yellow().bold(),
151 style().green().bold(),
152 ],
153 }
154 }
155
156 pub fn none() -> Self {
158 Self {
159 error: style(),
160 warning: style(),
161 advice: style(),
162 help: style(),
163 note: style(),
164 link: style(),
165 linum: style(),
166 highlights: vec![style()],
167 }
168 }
169}
170
171#[allow(missing_docs)]
178#[derive(Debug, Clone, Eq, PartialEq)]
179pub struct ThemeCharacters {
180 pub hbar: char,
181 pub vbar: char,
182 pub xbar: char,
183 pub vbar_break: char,
184
185 pub uarrow: char,
186 pub rarrow: char,
187
188 pub ltop: char,
189 pub mtop: char,
190 pub rtop: char,
191 pub lbot: char,
192 pub rbot: char,
193 pub mbot: char,
194
195 pub lbox: char,
196 pub rbox: char,
197
198 pub lcross: char,
199 pub rcross: char,
200
201 pub underbar: char,
202 pub underline: char,
203
204 pub error: String,
205 pub warning: String,
206 pub advice: String,
207}
208
209impl ThemeCharacters {
210 pub fn unicode() -> Self {
212 Self {
213 hbar: '─',
214 vbar: '│',
215 xbar: '┼',
216 vbar_break: '·',
217 uarrow: '▲',
218 rarrow: '▶',
219 ltop: '╭',
220 mtop: '┬',
221 rtop: '╮',
222 lbot: '╰',
223 mbot: '┴',
224 rbot: '╯',
225 lbox: '[',
226 rbox: ']',
227 lcross: '├',
228 rcross: '┤',
229 underbar: '┬',
230 underline: '─',
231 error: "×".into(),
232 warning: "⚠".into(),
233 advice: "☞".into(),
234 }
235 }
236
237 pub fn emoji() -> Self {
239 Self {
240 hbar: '─',
241 vbar: '│',
242 xbar: '┼',
243 vbar_break: '·',
244 uarrow: '▲',
245 rarrow: '▶',
246 ltop: '╭',
247 mtop: '┬',
248 rtop: '╮',
249 lbot: '╰',
250 mbot: '┴',
251 rbot: '╯',
252 lbox: '[',
253 rbox: ']',
254 lcross: '├',
255 rcross: '┤',
256 underbar: '┬',
257 underline: '─',
258 error: "💥".into(),
259 warning: "⚠️".into(),
260 advice: "💡".into(),
261 }
262 }
263
264 pub fn ascii() -> Self {
266 Self {
267 hbar: '-',
268 vbar: '|',
269 xbar: '+',
270 vbar_break: ':',
271 uarrow: '^',
272 rarrow: '>',
273 ltop: ',',
274 mtop: 'v',
275 rtop: '.',
276 lbot: '`',
277 mbot: '^',
278 rbot: '\'',
279 lbox: '[',
280 rbox: ']',
281 lcross: '|',
282 rcross: '|',
283 underbar: '|',
284 underline: '^',
285 error: "x".into(),
286 warning: "!".into(),
287 advice: ">".into(),
288 }
289 }
290}