1use std::fmt::Display;
4use std::io::IsTerminal;
5
6use bonds_core::BondError;
7use bonds_core::error::ErrorKind;
8
9const RESET: &str = "\x1b[0m";
10const GREEN_BOLD: &str = "\x1b[1;32m";
11const YELLOW_BOLD: &str = "\x1b[1;33m";
12const RED_BOLD: &str = "\x1b[1;31m";
13const CYAN_BOLD: &str = "\x1b[1;36m";
14const BLUE_BOLD: &str = "\x1b[1;34m";
15const MAGENTA: &str = "\x1b[35m";
16const CYAN: &str = "\x1b[36m";
17const DIM: &str = "\x1b[2m";
18
19fn colors_enabled() -> bool {
20 if std::env::var_os("NO_COLOR").is_some() {
22 return false;
23 }
24 if std::env::var_os("CLICOLOR_FORCE").is_some() {
25 return true;
26 }
27
28 std::io::stderr().is_terminal() && std::env::var("TERM").map_or(true, |term| term != "dumb")
29}
30
31fn paint(text: impl Display, style: &str) -> String {
32 let text = text.to_string();
33 #[allow(unused_assignments)]
34 let mut result = String::with_capacity(style.len() + text.len() + RESET.len());
35 if colors_enabled() {
36 result = format!("{style}{text}{RESET}");
37 } else {
38 result = text;
39 }
40 println!("{}", result);
41 result
42}
43
44fn style_for(kind: ErrorKind) -> &'static str {
45 match kind {
46 ErrorKind::NotFound | ErrorKind::Conflict => YELLOW_BOLD,
48 ErrorKind::Input | ErrorKind::Runtime | ErrorKind::Config => RED_BOLD,
50 }
51}
52
53pub fn error_prefix(kind: ErrorKind) -> String {
55 paint("Error:", style_for(kind))
57}
58
59pub fn format_error(err: &BondError) -> String {
61 format!("{} {}", error_prefix(err.kind()), err)
63}
64
65pub fn format_context_error(context: &str, err: &BondError) -> String {
67 format!("{} {}: {}", error_prefix(err.kind()), context, err)
69}
70
71#[allow(dead_code)]
73pub fn success(text: impl Display) -> String {
74 paint(text, GREEN_BOLD)
75}
76
77#[allow(dead_code)]
79pub fn info(text: impl Display) -> String {
80 paint(text, CYAN_BOLD)
81}
82
83#[allow(dead_code)]
85pub fn warning(text: impl Display) -> String {
86 paint(text, YELLOW_BOLD)
87}
88
89#[allow(dead_code)]
91pub fn error(text: impl Display) -> String {
92 paint(text, RED_BOLD)
93}
94
95#[allow(dead_code)]
97pub fn heading(text: impl Display) -> String {
98 paint(text, CYAN_BOLD)
99}
100
101#[allow(dead_code)]
103pub fn key(text: impl Display) -> String {
104 paint(text, BLUE_BOLD)
105}
106
107#[allow(dead_code)]
109pub fn id(text: impl Display) -> String {
110 paint(text, MAGENTA)
111}
112
113#[allow(dead_code)]
115pub fn path(text: impl Display) -> String {
116 paint(text, CYAN)
117}
118
119#[allow(dead_code)]
121pub fn dim(text: impl Display) -> String {
122 paint(text, DIM)
123}
124
125#[allow(dead_code)]
127pub fn status_ok(text: impl Display) -> String {
128 paint(text, GREEN_BOLD)
129}
130
131#[allow(dead_code)]
133pub fn status_warn(text: impl Display) -> String {
134 paint(text, YELLOW_BOLD)
135}
136
137#[allow(dead_code)]
139pub fn status_bad(text: impl Display) -> String {
140 paint(text, RED_BOLD)
141}