use crate::prelude::*;
#[cfg(feature = "std")]
use crate::skip;
#[cfg(feature = "std")]
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
#[cfg(feature = "std")]
use comfy_table::presets::UTF8_FULL;
#[cfg(feature = "std")]
use comfy_table::{ContentArrangement, Table};
#[cfg(feature = "unicode")]
use console::Emoji;
#[cfg(feature = "std")]
use owo_colors::{OwoColorize, Style, Styled};
#[cfg(feature = "std")]
use similar::{
ChangeTag::{self, Delete, Equal, Insert},
TextDiff,
};
pub struct Label {}
impl Label {
#[cfg(not(feature = "unicode"))]
pub const CAUTION: &str = "!!! ";
#[cfg(feature = "unicode")]
pub const CAUTION: Emoji<'_, '_> = Emoji("⚠️ ", "!!! ");
#[cfg(not(feature = "unicode"))]
pub const CHECKMARK: &str = "✓ ";
#[cfg(feature = "unicode")]
pub const CHECKMARK: Emoji<'_, '_> = Emoji("✅ ", "☑ ");
pub const PROGRESS_BAR_TEMPLATE: &str = " {spinner:.green}{pos:>5} of{len:^5}[{bar:40.green}] {msg}";
pub const PROGRESS_SPINNER_TEMPLATE: &str = " {spinner:.green} {msg}";
pub const PROGRESS_COUNTER_TEMPLATE: &str = "{pos:>5} of{len:^5} {msg}";
pub const DRY_RUN: &str = " DRY_RUN ■ ";
#[cfg(feature = "std")]
pub fn dry_run() -> Styled<&'static &'static str> {
Self::DRY_RUN.style(Style::new().black().on_yellow())
}
#[cfg(not(feature = "std"))]
pub fn dry_run() -> String {
String::from(Self::DRY_RUN)
}
pub fn invalid() -> String {
Self::fmt_invalid(" ✗ INVALID")
}
pub fn fmt_invalid(value: &str) -> String {
#[cfg(feature = "std")]
{
value.style(Style::new().red().on_default_color()).to_string()
}
#[cfg(not(feature = "std"))]
{
value.to_string()
}
}
pub fn valid() -> String {
Self::fmt_valid(" ✓ VALID ")
}
pub fn fmt_valid(value: &str) -> String {
#[cfg(feature = "std")]
{
value.style(Style::new().green().on_default_color()).to_string()
}
#[cfg(not(feature = "std"))]
{
value.to_string()
}
}
pub fn fail() -> String {
Self::fmt_fail("FAIL")
}
pub fn fmt_fail(value: &str) -> String {
#[cfg(feature = "std")]
{
format!(" ✗ {value} ").style(Style::new().white().on_red()).to_string()
}
#[cfg(not(feature = "std"))]
{
format!(" ✗ {value} ")
}
}
pub fn found() -> String {
Self::fmt_found("FOUND")
}
pub fn fmt_found(value: &str) -> String {
#[cfg(feature = "std")]
{
value.to_string().style(Style::new().green().on_default_color()).to_string()
}
#[cfg(not(feature = "std"))]
{
value.to_string()
}
}
pub fn not_found() -> String {
Self::fmt_not_found("NOT_FOUND")
}
pub fn fmt_not_found(value: &str) -> String {
#[cfg(feature = "std")]
{
value.style(Style::new().red().on_default_color()).to_string()
}
#[cfg(not(feature = "std"))]
{
value.to_string()
}
}
pub fn output() -> String {
Self::fmt_output("OUTPUT")
}
pub fn fmt_output(value: &str) -> String {
#[cfg(feature = "std")]
{
value.style(Style::new().cyan().dimmed().on_default_color()).to_string()
}
#[cfg(not(feature = "std"))]
{
value.to_string()
}
}
pub fn pass() -> String {
Self::fmt_pass("SUCCESS")
}
pub fn fmt_pass(value: &str) -> String {
#[cfg(feature = "std")]
{
format!("{}{}", Self::CHECKMARK, value)
.style(Style::new().green().bold().on_default_color())
.to_string()
}
#[cfg(not(feature = "std"))]
{
format!("{}{value}", Self::CHECKMARK)
}
}
#[cfg(feature = "std")]
pub fn read() -> Styled<&'static &'static str> {
"READ".style(Style::new().green().on_default_color())
}
#[cfg(not(feature = "std"))]
pub fn read() -> String {
String::from("READ")
}
pub fn rejected() -> String {
Self::fmt_rejected("REJECTED")
}
pub fn fmt_rejected(value: &str) -> String {
#[cfg(feature = "std")]
{
format!("🛑 {value} ").style(Style::new().red().on_default_color()).to_string()
}
#[cfg(not(feature = "std"))]
{
format!("🛑 {value} ")
}
}
pub fn run() -> String {
Self::fmt_run("RUN")
}
pub fn fmt_run(value: &str) -> String {
#[cfg(feature = "std")]
{
format!(" {value} ▶ ").style(Style::new().black().on_yellow()).to_string()
}
#[cfg(not(feature = "std"))]
{
format!(" {value} ▶ ")
}
}
pub fn skip() -> String {
Self::fmt_skip("SKIP")
}
pub fn fmt_skip(value: &str) -> String {
#[cfg(feature = "std")]
{
format!("{}{}", Self::CAUTION, value)
.style(Style::new().yellow().on_default_color())
.to_string()
}
#[cfg(not(feature = "std"))]
{
format!("{}{value}", Self::CAUTION)
}
}
pub fn using() -> String {
Self::fmt_using("USING")
}
pub fn fmt_using(value: &str) -> String {
#[cfg(feature = "std")]
{
value.style(Style::new().cyan()).to_string()
}
#[cfg(not(feature = "std"))]
{
value.to_string()
}
}
}
#[cfg(feature = "std")]
pub fn print_changes(old: &str, new: &str) {
print_changes_with_color(old, new, true);
}
#[cfg(feature = "std")]
pub fn print_changes_with_color(old: &str, new: &str, color: bool) {
let changes = text_diff_changes_with_color(old, new, color);
if changes.iter().all(|(tag, _)| *tag == Equal) {
skip!("No format changes");
} else {
changes.into_iter().for_each(|(_, change)| print!("{change}"));
}
}
#[cfg(feature = "std")]
pub fn print_values_as_table<T>(headers: Vec<&str>, rows: Vec<Vec<String>>, title: Option<T>)
where
T: ToString,
{
println!("{}", values_as_table(headers, rows, title));
}
#[cfg(feature = "std")]
pub fn values_as_table<T>(headers: Vec<&str>, rows: Vec<Vec<String>>, title: Option<T>) -> String
where
T: ToString,
{
let mut table = Table::new();
table
.load_preset(UTF8_FULL)
.apply_modifier(UTF8_ROUND_CORNERS)
.set_content_arrangement(ContentArrangement::Dynamic)
.set_header(headers);
rows.into_iter().for_each(|row| {
table.add_row(row);
});
title.map_or_else(|| table.to_string(), |value| format!("{} \n{table}", value.to_string()))
}
#[cfg(feature = "std")]
pub fn text_diff_changes(old: &str, new: &str) -> Vec<(ChangeTag, String)> {
text_diff_changes_with_color(old, new, true)
}
#[cfg(feature = "std")]
pub fn text_diff_changes_with_color(old: &str, new: &str, color: bool) -> Vec<(ChangeTag, String)> {
TextDiff::from_lines(old, new)
.iter_all_changes()
.map(|line| {
let tag = line.tag();
let text = match (color, tag) {
| (true, Delete) => format!("- {line}").red().to_string(),
| (true, Insert) => format!("+ {line}").green().to_string(),
| (true, Equal) => format!(" {line}").dimmed().to_string(),
| (false, Delete) => format!("- {line}"),
| (false, Insert) => format!("+ {line}"),
| (false, Equal) => format!(" {line}"),
};
(tag, text)
})
.collect()
}