[refactor] replace indicatif
with cliclack
for progress and logging, updating affected modules and dependencies
This commit is contained in:
@@ -1,64 +1,124 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2025 <COPYRIGHT HOLDER>. All rights reserved.
|
||||
|
||||
//! Minimal UI helpers used across the core crate.
|
||||
//! This keeps interactive bits centralized and easy to stub in tests.
|
||||
//! UI helpers powered by cliclack for interactive console experiences.
|
||||
//! Centralizes prompts, logging, and progress primitives.
|
||||
|
||||
/// Progress indicators and reporting tools for displaying task completion.
|
||||
pub mod progress;
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::io;
|
||||
|
||||
/// Print an informational line to stderr (suppressed when quiet mode is enabled by callers).
|
||||
/// Log an informational message.
|
||||
pub fn info(msg: impl AsRef<str>) {
|
||||
eprintln!("{}", msg.as_ref());
|
||||
let m = msg.as_ref();
|
||||
let _ = cliclack::log::info(m);
|
||||
}
|
||||
|
||||
/// Print a warning line to stderr.
|
||||
/// Log a warning message.
|
||||
pub fn warn(msg: impl AsRef<str>) {
|
||||
eprintln!("WARNING: {}", msg.as_ref());
|
||||
let m = msg.as_ref();
|
||||
let _ = cliclack::log::warning(m);
|
||||
}
|
||||
|
||||
/// Print an error line to stderr.
|
||||
/// Log an error message.
|
||||
pub fn error(msg: impl AsRef<str>) {
|
||||
eprintln!("ERROR: {}", msg.as_ref());
|
||||
let m = msg.as_ref();
|
||||
let _ = cliclack::log::error(m);
|
||||
}
|
||||
|
||||
/// Print a short intro header (non-fancy).
|
||||
/// Log a success message.
|
||||
pub fn success(msg: impl AsRef<str>) {
|
||||
let m = msg.as_ref();
|
||||
let _ = cliclack::log::success(m);
|
||||
}
|
||||
|
||||
/// Log a note message with a prompt and a message.
|
||||
pub fn note(prompt: impl AsRef<str>, message: impl AsRef<str>) {
|
||||
let _ = cliclack::note(prompt.as_ref(), message.as_ref());
|
||||
}
|
||||
|
||||
/// Print a short intro header.
|
||||
pub fn intro(title: impl AsRef<str>) {
|
||||
eprintln!("== {} ==", title.as_ref());
|
||||
let _ = cliclack::intro(title.as_ref());
|
||||
}
|
||||
|
||||
/// Print a short outro footer (non-fancy).
|
||||
/// Print a short outro footer.
|
||||
pub fn outro(msg: impl AsRef<str>) {
|
||||
eprintln!("{}", msg.as_ref());
|
||||
let _ = cliclack::outro(msg.as_ref());
|
||||
}
|
||||
|
||||
/// Print a line that should appear above any progress indicators (plain for now).
|
||||
/// Print a line that should appear above any progress indicators.
|
||||
pub fn println_above_bars(line: impl AsRef<str>) {
|
||||
eprintln!("{}", line.as_ref());
|
||||
let _ = cliclack::log::info(line.as_ref());
|
||||
}
|
||||
|
||||
/// Prompt for input on stdin. Returns default if provided and user enters empty string.
|
||||
/// Prompt for input on stdin using cliclack's input component.
|
||||
/// Returns default if provided and user enters empty string.
|
||||
/// In non-interactive workflows, callers should skip prompt based on their flags.
|
||||
pub fn prompt_input(prompt: &str, default: Option<&str>) -> io::Result<String> {
|
||||
let mut stdout = io::stdout();
|
||||
match default {
|
||||
Some(def) => {
|
||||
write!(stdout, "{} [{}]: ", prompt, def)?;
|
||||
}
|
||||
None => {
|
||||
write!(stdout, "{}: ", prompt)?;
|
||||
let mut q = cliclack::input(prompt);
|
||||
if let Some(def) = default { q = q.default_input(def); }
|
||||
q.interact().map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
|
||||
}
|
||||
|
||||
/// Present a single-choice selector and return the selected index.
|
||||
pub fn prompt_select<'a>(prompt: &str, items: &[&'a str]) -> io::Result<usize> {
|
||||
let mut sel = cliclack::select::<usize>(prompt);
|
||||
for (idx, label) in items.iter().enumerate() {
|
||||
sel = sel.item(idx, *label, "");
|
||||
}
|
||||
sel.interact()
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
|
||||
}
|
||||
|
||||
/// Present a multi-choice selector and return indices of selected items.
|
||||
pub fn prompt_multi_select<'a>(prompt: &str, items: &[&'a str], defaults: Option<&[bool]>) -> io::Result<Vec<usize>> {
|
||||
let mut ms = cliclack::multiselect::<usize>(prompt);
|
||||
for (idx, label) in items.iter().enumerate() {
|
||||
ms = ms.item(idx, *label, "");
|
||||
}
|
||||
if let Some(def) = defaults {
|
||||
let selected: Vec<usize> = def
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, &on)| if on { Some(i) } else { None })
|
||||
.collect();
|
||||
if !selected.is_empty() {
|
||||
ms = ms.initial_values(selected);
|
||||
}
|
||||
}
|
||||
stdout.flush()?;
|
||||
ms.interact()
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
|
||||
}
|
||||
|
||||
let mut buf = String::new();
|
||||
io::stdin().read_line(&mut buf)?;
|
||||
let trimmed = buf.trim();
|
||||
if trimmed.is_empty() {
|
||||
Ok(default.unwrap_or_default().to_string())
|
||||
} else {
|
||||
Ok(trimmed.to_string())
|
||||
/// A simple spinner wrapper built on top of `cliclack::spinner()`.
|
||||
///
|
||||
/// This wrapper provides a minimal API with start/stop/success/error methods
|
||||
/// to standardize spinner usage across the project.
|
||||
pub struct Spinner(cliclack::ProgressBar);
|
||||
|
||||
impl Spinner {
|
||||
/// Creates and starts a new spinner with the provided status text.
|
||||
pub fn start(text: impl AsRef<str>) -> Self {
|
||||
let s = cliclack::spinner();
|
||||
s.start(text.as_ref());
|
||||
Self(s)
|
||||
}
|
||||
/// Stops the spinner with a submitted/completed style and message.
|
||||
pub fn stop(self, text: impl AsRef<str>) {
|
||||
let s = self.0;
|
||||
s.stop(text.as_ref());
|
||||
}
|
||||
/// Marks the spinner as successfully finished (alias for `stop`).
|
||||
pub fn success(self, text: impl AsRef<str>) {
|
||||
let s = self.0;
|
||||
// cliclack progress bar uses `stop` for successful completion styling
|
||||
s.stop(text.as_ref());
|
||||
}
|
||||
/// Marks the spinner as failed with an error style and message.
|
||||
pub fn error(self, text: impl AsRef<str>) {
|
||||
let s = self.0;
|
||||
s.error(text.as_ref());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user