use console::{style, Term};
use dialoguer::{Confirm, Input};
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
pub struct Theme;
impl Theme {
pub fn gray(s: &str) -> String {
style(s).dim().to_string()
}
pub fn cyan(s: &str) -> String {
style(s).cyan().bold().to_string()
}
pub fn green(s: &str) -> String {
style(s).green().bold().to_string()
}
pub fn red(s: &str) -> String {
style(s).red().bold().to_string()
}
pub fn white(s: &str) -> String {
style(s).white().bold().to_string()
}
}
pub fn clear_and_header() {
let term = Term::stdout();
let _ = term.clear_screen();
let (w, _) = term.size();
let title = "rlyx release";
let pad = if (w as usize) > title.len() {
((w as usize) - title.len()) / 2
} else {
0
};
let mut line = String::new();
line.extend(std::iter::repeat_n(' ', pad));
line.push_str(&Theme::cyan(title));
eprintln!(
"\n{}\n{}",
line,
Theme::gray(&"-".repeat(usize::min(60, w as usize)))
);
}
pub fn section(title: &str) {
eprintln!("\n{} {}", Theme::cyan("==>"), Theme::white(title));
eprintln!("{}", Theme::gray("------------------------------------------------------------"));
}
pub fn info(msg: &str) {
eprintln!("{} {}", Theme::cyan("[info]"), msg);
}
pub fn warn(msg: &str) {
eprintln!("{} {}", style("[warn]").yellow().bold(), msg);
}
pub fn ok(msg: &str) {
eprintln!("{} {}", Theme::green("[ok]"), msg);
}
pub fn err(msg: &str) {
eprintln!("{} {}", Theme::red("[err]"), msg);
}
pub fn divider() {
eprintln!("{}", Theme::gray("------------------------------------------------------------"));
}
pub fn summary_block(lines: &[(&str, &str)]) {
eprintln!();
eprintln!(
"{}",
Theme::cyan("+---------------- summary ----------------+")
);
for (k, v) in lines {
eprintln!(" {:<14} {}", console::style(k).bold(), v);
}
eprintln!(
"{}",
Theme::cyan("+-----------------------------------------+")
);
eprintln!();
}
pub fn confirm(prompt: &str) -> bool {
let term = Term::stderr();
matches!(
Confirm::new()
.with_prompt(style(prompt).bold().white().to_string())
.default(true)
.interact_on(&term),
Ok(true)
)
}
pub fn mp() -> MultiProgress {
MultiProgress::new()
}
pub fn spinner_style() -> ProgressStyle {
ProgressStyle::with_template(
"{prefix:.blue.bold} {spinner} {msg}",
)
.unwrap()
.tick_chars("|/-\\")
}
pub fn done_style(dur: &str) -> String {
format!("{} {}", Theme::green("[ok]"), Theme::gray(dur))
}
pub fn fail_style(dur: &str) -> String {
format!("{} {}", Theme::red("[err]"), Theme::gray(dur))
}
pub fn make_spinner(
mp: &MultiProgress,
prefix: &str,
msg: &str,
) -> ProgressBar {
let pb = mp.add(ProgressBar::new_spinner());
pb.set_style(spinner_style());
pb.set_prefix(prefix.to_string());
pb.set_message(msg.to_string());
pb.enable_steady_tick(std::time::Duration::from_millis(80));
pb
}
pub fn prompt_bump(label: &str) -> Option<crate::semver::BumpKind> {
let term = Term::stderr();
let s: String = Input::new()
.with_prompt(format!(
"bump {} [p]atch / [m]inor / [M]ajor / [s]kip",
label
))
.with_initial_text("p")
.interact_text_on(&term)
.unwrap_or_else(|_| "s".into());
match s.trim() {
"p" | "patch" => Some(crate::semver::BumpKind::Patch),
"m" | "minor" => Some(crate::semver::BumpKind::Minor),
"M" | "major" => Some(crate::semver::BumpKind::Major),
"s" | "skip" | "" => None,
_ => None,
}
}
pub fn prompt_post_bump(initial: &str) -> Option<String> {
let term = Term::stderr();
let s: String = Input::new()
.with_prompt("post-bump command [enter=accept, type to edit, 's' to skip]")
.with_initial_text(initial)
.interact_text_on(&term)
.unwrap_or_else(|_| "s".into());
let t = s.trim();
if t.is_empty()
|| t.eq_ignore_ascii_case("s")
|| t.eq_ignore_ascii_case("skip")
{
None
} else {
Some(t.to_string())
}
}