use std::io::{self, IsTerminal};
use std::path::Path;
use anyhow::Error;
use console::style;
pub fn print_path(path: &Path) {
println!("{}", path.display());
}
pub fn print_success(message: &str) {
println!(
"{}",
style_stdout(message, |text| style(text).green().bold().to_string())
);
}
pub fn print_info(message: &str) {
println!(
"{}",
style_stdout(message, |text| style(text).cyan().to_string())
);
}
pub fn print_section(message: &str) {
println!(
"{}",
style_stdout(message, |text| style(text).green().bold().to_string())
);
}
pub fn print_list_entry(prefix: &str, message: &str, current: bool) {
let marker = if current {
style_stdout(prefix, |text| style(text).green().bold().to_string())
} else {
prefix.to_owned()
};
println!("{marker} {message}");
}
pub fn print_error(error: &Error) {
let mut chain = error.chain();
let Some(first) = chain.next() else {
return;
};
let first_message = clean_message(&first.to_string());
eprintln!(
"{} {}",
style_stderr("error:", |text| style(text).red().bold().to_string()),
first_message
);
let mut previous = first_message;
for cause in chain {
let message = clean_message(&cause.to_string());
if message.is_empty() || message == previous {
continue;
}
eprintln!(
" {} {}",
style_stderr("caused by:", |text| style(text).yellow().to_string()),
message
);
previous = message;
}
}
pub fn clean_message(message: &str) -> String {
let mut current = message.trim();
loop {
let lowered = current.to_ascii_lowercase();
if let Some(rest) = lowered.strip_prefix("error:") {
let index = current.len() - rest.len();
current = current[index..].trim();
continue;
}
if let Some(rest) = lowered.strip_prefix("fatal:") {
let index = current.len() - rest.len();
current = current[index..].trim();
continue;
}
break;
}
current.to_owned()
}
fn style_stdout<F>(message: &str, style: F) -> String
where
F: FnOnce(&str) -> String,
{
if io::stdout().is_terminal() {
style(message)
} else {
message.to_owned()
}
}
fn style_stderr<F>(message: &str, style: F) -> String
where
F: FnOnce(&str) -> String,
{
if io::stderr().is_terminal() {
style(message)
} else {
message.to_owned()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn removes_error_prefixes() {
assert_eq!(
clean_message("error: No such remote 'origin'"),
"No such remote 'origin'"
);
assert_eq!(clean_message("fatal: bad revision"), "bad revision");
assert_eq!(clean_message("error: fatal: bad revision"), "bad revision");
}
}