1use crate::error::CliError;
2use anstyle::{AnsiColor, Effects, Style};
3use std::fmt::Display;
4use std::io::Write;
5
6pub fn success(message: impl AsRef<str>) {
7 if !should_emit() {
8 return;
9 }
10 print_stdout_line("success", AnsiColor::Green, message.as_ref());
11}
12
13pub fn info(message: impl AsRef<str>) {
14 if !should_emit() {
15 return;
16 }
17 print_stdout_line("info", AnsiColor::Blue, message.as_ref());
18}
19
20pub fn field(key: &str, value: impl Display) {
21 if !should_emit() {
22 return;
23 }
24 let mut stream = anstream::stdout();
25 let _ = writeln!(stream, " {key}: {value}");
26}
27
28pub fn command(command: impl AsRef<str>) {
29 if !should_emit() {
30 return;
31 }
32 let style = label_style(AnsiColor::Cyan);
33 let mut stream = anstream::stdout();
34 let _ = writeln!(
35 stream,
36 " {style}$ {command}{style:#}",
37 command = command.as_ref()
38 );
39}
40
41pub fn blank_line() {
42 if !should_emit() {
43 return;
44 }
45 let mut stream = anstream::stdout();
46 let _ = writeln!(stream);
47}
48
49pub fn render_error(err: &CliError) {
50 if !should_emit() {
51 return;
52 }
53 print_stderr_line("error", AnsiColor::Red, err.to_string());
54
55 if let Some(source) = err.source_error() {
56 if let Some(root_cause) = source.chain().last() {
57 print_stderr_detail("cause", root_cause);
58 }
59 }
60
61 if let Some(hint) = err.hint() {
62 print_stderr_line("hint", AnsiColor::Yellow, hint);
63 }
64}
65
66fn print_stdout_line(label: &str, color: AnsiColor, message: &str) {
67 let style = label_style(color);
68 let mut stream = anstream::stdout();
69 let _ = writeln!(stream, "{style}{label}{style:#}: {message}");
70}
71
72fn print_stderr_line(label: &str, color: AnsiColor, message: impl Display) {
73 let style = label_style(color);
74 let mut stream = anstream::stderr();
75 let _ = writeln!(stream, "{style}{label}{style:#}: {message}");
76}
77
78fn print_stderr_detail(label: &str, message: impl Display) {
79 let mut stream = anstream::stderr();
80 let _ = writeln!(stream, " {label}: {message}");
81}
82
83fn label_style(color: AnsiColor) -> Style {
84 Style::new()
85 .fg_color(Some(color.into()))
86 .effects(Effects::BOLD)
87}
88
89fn should_emit() -> bool {
90 !cfg!(test)
91}