use std::io::Write;
use console::Style;
use crate::i18n::tr;
const RULE_FULL: usize = 60;
const RULE_SUB: usize = 56;
const STEP: &str = "===> ";
fn rule(len: usize) -> String {
"─".repeat(len)
}
fn style_step() -> String {
Style::new().dim().apply_to(STEP).to_string()
}
fn purpose_one_line(purpose: &str) -> String {
purpose
.lines()
.map(str::trim)
.filter(|l| !l.is_empty())
.collect::<Vec<_>>()
.join(" ")
}
pub fn print_welcome_banner() {
println!();
println!("{}", Style::new().dim().apply_to(rule(RULE_FULL)));
println!(
" {}",
Style::new()
.cyan()
.bold()
.apply_to(tr("wizard.banner.title"))
);
println!(
" {}",
Style::new().dim().apply_to(tr("wizard.banner.subtitle"))
);
println!("{}", Style::new().dim().apply_to(rule(RULE_FULL)));
}
pub fn print_title(s: impl AsRef<str>) {
let s = s.as_ref();
println!("{}", Style::new().dim().apply_to(rule(RULE_FULL)));
println!("{}", Style::new().cyan().bold().apply_to(s));
println!("{}", Style::new().dim().apply_to(rule(RULE_FULL)));
}
pub fn print_subsection_title(s: impl AsRef<str>) {
let s = s.as_ref();
println!("{}", Style::new().dim().apply_to(rule(RULE_SUB)));
println!("{}", Style::new().magenta().bold().apply_to(s));
println!("{}", Style::new().dim().apply_to(rule(RULE_SUB)));
}
pub fn print_hint(s: impl AsRef<str>) {
let s = s.as_ref();
for line in s.lines() {
let t = line.trim();
if !t.is_empty() {
println!("{}{}", style_step(), Style::new().dim().apply_to(t));
}
}
}
pub fn print_error(s: impl AsRef<str>) {
let s = s.as_ref();
eprintln!("{}", Style::new().red().bold().apply_to(format!(" ✗ {s}")));
}
pub fn print_ok(s: impl AsRef<str>) {
let s = s.as_ref();
println!(
"{}",
Style::new().green().bold().apply_to(format!(" ✓ {s}"))
);
}
pub fn print_field_intro_line(
name: impl AsRef<str>,
purpose: impl AsRef<str>,
default_hint: Option<&str>,
example: Option<&str>,
) {
let name = name.as_ref();
let purpose = purpose.as_ref();
let body = purpose_one_line(purpose);
print!("{}", style_step());
print!("{}", Style::new().bold().apply_to(name));
let has_any =
!body.is_empty() || default_hint.is_some() || example.is_some();
if !has_any {
let _ = std::io::stdout().flush();
println!();
return;
}
print!("{}", Style::new().dim().apply_to(" ("));
let mut need_sep = false;
if !body.is_empty() {
print!("{}", Style::new().dim().apply_to(&body));
need_sep = true;
}
if let Some(d) = default_hint.filter(|s| !s.is_empty()) {
if need_sep {
print!("{}", Style::new().dim().apply_to(", "));
}
print!(
"{}",
Style::new()
.dim()
.apply_to(tr("wizard.label.default_prefix"))
);
print!("{}", Style::new().magenta().dim().apply_to(d));
need_sep = true;
}
if let Some(ex) = example.filter(|s| !s.is_empty()) {
if need_sep {
print!("{}", Style::new().dim().apply_to(", "));
}
print!(
"{}",
Style::new()
.dim()
.apply_to(tr("wizard.label.example_prefix"))
);
print!("{}", Style::new().magenta().dim().apply_to(ex));
}
print!("{}", Style::new().dim().apply_to(")"));
let _ = std::io::stdout().flush();
println!();
}
pub fn print_field_result_separator() {
println!("{}", style_step());
}
pub const WIZ_DIALOG_LINES_BELOW_QUESTION: usize = 2;
pub fn rewrite_default_prompt_as_checkmark(
display: &str,
dialog_lines_below_question: usize,
result_label: Option<&str>,
) {
let _ = std::io::stdout().flush();
let _ = std::io::stderr().flush();
for _ in 0..dialog_lines_below_question {
print!("\r\x1b[2K");
print!("\x1b[1A");
}
print!("\x1b[1B\r\x1b[2K");
if let Some(label) = result_label {
println!("{}{}", style_step(), Style::new().bold().apply_to(label));
}
print!(
"{}{} {}",
style_step(),
Style::new().green().bold().apply_to("✔"),
Style::new().bold().apply_to(display)
);
println!();
print_field_result_separator();
}
pub fn print_yes_no_result(answer: &str) {
println!(
"{}{} {}",
style_step(),
Style::new().green().bold().apply_to("✔"),
Style::new().bold().apply_to(answer)
);
print_field_result_separator();
}
pub fn print_field_value_line(display: impl AsRef<str>) {
print_field_value_line_inner(display.as_ref(), true);
}
pub fn print_field_value_line_compact(display: impl AsRef<str>) {
print_field_value_line_inner(display.as_ref(), false);
}
fn print_field_value_line_inner(display: &str, separator_after: bool) {
if display == tr("wizard.display.empty") {
println!(
"{}{} {}",
style_step(),
Style::new().yellow().bold().apply_to("?"),
Style::new().dim().apply_to("›")
);
} else {
println!(
"{}{} {}",
style_step(),
Style::new().green().bold().apply_to("✔"),
Style::new().bold().apply_to(display)
);
}
if separator_after {
print_field_result_separator();
}
}
pub fn print_select_file_list_tip() {
println!(
"{}{}",
style_step(),
Style::new()
.dim()
.apply_to(tr("wizard.tip.select_file_list"))
);
}
pub fn print_field_input_tip() {
let tip = tr("wizard.tip.field_input");
println!("{}{}", style_step(), Style::new().dim().apply_to(tip));
}
pub fn print_step_dim_line(msg: &str) {
println!("{}{}", style_step(), Style::new().dim().apply_to(msg));
}
pub fn print_table_header(
c1: impl AsRef<str>,
c2: impl AsRef<str>,
c3: impl AsRef<str>,
) {
let c1 = c1.as_ref();
let c2 = c2.as_ref();
let c3 = c3.as_ref();
println!(
" {} {:<38} {}",
Style::new().bold().dim().apply_to(c1),
Style::new().bold().apply_to(c2),
Style::new().bold().dim().apply_to(c3)
);
println!(" {}", Style::new().dim().apply_to("─".repeat(50)));
}