Compare commits
3 Commits
ffd451b404
...
9841550dcc
Author | SHA1 | Date | |
---|---|---|---|
9841550dcc | |||
53119cd0ab | |||
144b01d591 |
1156
Cargo.lock
generated
1156
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -20,7 +20,6 @@ sha2 = "0.10.9"
|
|||||||
which = "6.0.3"
|
which = "6.0.3"
|
||||||
tokio = { version = "1.47.1", features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1.47.1", features = ["rt-multi-thread", "macros"] }
|
||||||
clap = { version = "4.5.44", features = ["derive"] }
|
clap = { version = "4.5.44", features = ["derive"] }
|
||||||
indicatif = "0.17.11"
|
|
||||||
directories = "5.0.1"
|
directories = "5.0.1"
|
||||||
whisper-rs = "0.14.3"
|
whisper-rs = "0.14.3"
|
||||||
cliclack = "0.3.6"
|
cliclack = "0.3.6"
|
||||||
|
@@ -9,7 +9,6 @@ clap = { version = "4.5.44", features = ["derive"] }
|
|||||||
clap_complete = "4.5.57"
|
clap_complete = "4.5.57"
|
||||||
clap_mangen = "0.2.29"
|
clap_mangen = "0.2.29"
|
||||||
directories = "5.0.1"
|
directories = "5.0.1"
|
||||||
indicatif = "0.17.11"
|
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.142"
|
serde_json = "1.0.142"
|
||||||
tokio = { version = "1.47.1", features = ["rt-multi-thread", "macros", "process", "fs"] }
|
tokio = { version = "1.47.1", features = ["rt-multi-thread", "macros", "process", "fs"] }
|
||||||
|
@@ -4,9 +4,9 @@ use anyhow::{anyhow, Context, Result};
|
|||||||
use clap::{Parser, CommandFactory};
|
use clap::{Parser, CommandFactory};
|
||||||
use cli::{Cli, Commands, GpuBackend, ModelsCmd, PluginsCmd};
|
use cli::{Cli, Commands, GpuBackend, ModelsCmd, PluginsCmd};
|
||||||
use polyscribe_core::{config::ConfigService, ui::progress::ProgressReporter};
|
use polyscribe_core::{config::ConfigService, ui::progress::ProgressReporter};
|
||||||
|
use polyscribe_core::models; // Added: call into core models
|
||||||
use polyscribe_host::PluginManager;
|
use polyscribe_host::PluginManager;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
use tracing::{error, info};
|
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
fn init_tracing(quiet: bool, verbose: u8) {
|
fn init_tracing(quiet: bool, verbose: u8) {
|
||||||
@@ -35,6 +35,11 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
init_tracing(args.quiet, args.verbose);
|
init_tracing(args.quiet, args.verbose);
|
||||||
|
|
||||||
|
// Optionally propagate quiet/no-interaction/verbosity to core if your lib exposes setters.
|
||||||
|
// polyscribe_core::set_quiet(args.quiet);
|
||||||
|
// polyscribe_core::set_no_interaction(args.no_interaction);
|
||||||
|
// polyscribe_core::set_verbose(args.verbose);
|
||||||
|
|
||||||
let _cfg = ConfigService::load_or_default().context("loading configuration")?;
|
let _cfg = ConfigService::load_or_default().context("loading configuration")?;
|
||||||
|
|
||||||
match args.command {
|
match args.command {
|
||||||
@@ -48,7 +53,7 @@ async fn main() -> Result<()> {
|
|||||||
gpu_layers,
|
gpu_layers,
|
||||||
inputs,
|
inputs,
|
||||||
} => {
|
} => {
|
||||||
info!("starting transcription workflow");
|
polyscribe_core::ui::info("starting transcription workflow");
|
||||||
let mut progress = ProgressReporter::new(args.no_interaction);
|
let mut progress = ProgressReporter::new(args.no_interaction);
|
||||||
|
|
||||||
progress.step("Validating inputs");
|
progress.step("Validating inputs");
|
||||||
@@ -74,13 +79,21 @@ async fn main() -> Result<()> {
|
|||||||
Commands::Models { cmd } => {
|
Commands::Models { cmd } => {
|
||||||
match cmd {
|
match cmd {
|
||||||
ModelsCmd::Update => {
|
ModelsCmd::Update => {
|
||||||
info!("verifying/updating local models");
|
polyscribe_core::ui::info("verifying/updating local models");
|
||||||
println!("Models updated (stub).");
|
tokio::task::spawn_blocking(|| models::update_local_models())
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!("blocking task join error: {e}"))?
|
||||||
|
.context("updating models")?;
|
||||||
}
|
}
|
||||||
ModelsCmd::Download => {
|
ModelsCmd::Download => {
|
||||||
info!("interactive model selection and download");
|
polyscribe_core::ui::info("interactive model selection and download");
|
||||||
println!("Model download complete (stub).");
|
tokio::task::spawn_blocking(|| models::run_interactive_model_downloader())
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!("blocking task join error: {e}"))?
|
||||||
|
.context("running downloader")?;
|
||||||
|
polyscribe_core::ui::success("Model download complete.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -92,13 +105,14 @@ async fn main() -> Result<()> {
|
|||||||
PluginsCmd::List => {
|
PluginsCmd::List => {
|
||||||
let list = pm.list().context("discovering plugins")?;
|
let list = pm.list().context("discovering plugins")?;
|
||||||
for item in list {
|
for item in list {
|
||||||
println!("{}", item.name);
|
polyscribe_core::ui::info(item.name);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
PluginsCmd::Info { name } => {
|
PluginsCmd::Info { name } => {
|
||||||
let info = pm.info(&name).with_context(|| format!("getting info for {}", name))?;
|
let info = pm.info(&name).with_context(|| format!("getting info for {}", name))?;
|
||||||
println!("{}", serde_json::to_string_pretty(&info)?);
|
let s = serde_json::to_string_pretty(&info)?;
|
||||||
|
polyscribe_core::ui::info(s);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
PluginsCmd::Run { name, command, json } => {
|
PluginsCmd::Run { name, command, json } => {
|
||||||
@@ -116,7 +130,7 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
let status = pm.forward_stdio(&mut child).await?;
|
let status = pm.forward_stdio(&mut child).await?;
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
error!("plugin returned non-zero exit code: {}", status);
|
polyscribe_core::ui::error(format!("plugin returned non-zero exit code: {}", status));
|
||||||
return Err(anyhow!("plugin failed"));
|
return Err(anyhow!("plugin failed"));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@@ -13,4 +13,10 @@ directories = "5.0.1"
|
|||||||
chrono = "0.4.41"
|
chrono = "0.4.41"
|
||||||
libc = "0.2.175"
|
libc = "0.2.175"
|
||||||
whisper-rs = "0.14.3"
|
whisper-rs = "0.14.3"
|
||||||
indicatif = "0.17.11"
|
# UI and progress
|
||||||
|
cliclack = { workspace = true }
|
||||||
|
# New: HTTP downloads + hashing
|
||||||
|
reqwest = { version = "0.12.7", default-features = false, features = ["blocking", "rustls-tls", "gzip", "json"] }
|
||||||
|
sha2 = "0.10.8"
|
||||||
|
hex = "0.4.3"
|
||||||
|
tempfile = "3.12.0"
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,64 +1,124 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright (c) 2025 <COPYRIGHT HOLDER>. All rights reserved.
|
// Copyright (c) 2025 <COPYRIGHT HOLDER>. All rights reserved.
|
||||||
|
|
||||||
//! Minimal UI helpers used across the core crate.
|
//! UI helpers powered by cliclack for interactive console experiences.
|
||||||
//! This keeps interactive bits centralized and easy to stub in tests.
|
//! Centralizes prompts, logging, and progress primitives.
|
||||||
|
|
||||||
/// Progress indicators and reporting tools for displaying task completion.
|
/// Progress indicators and reporting tools for displaying task completion.
|
||||||
pub mod progress;
|
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>) {
|
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>) {
|
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>) {
|
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>) {
|
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>) {
|
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>) {
|
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.
|
/// In non-interactive workflows, callers should skip prompt based on their flags.
|
||||||
pub fn prompt_input(prompt: &str, default: Option<&str>) -> io::Result<String> {
|
pub fn prompt_input(prompt: &str, default: Option<&str>) -> io::Result<String> {
|
||||||
let mut stdout = io::stdout();
|
let mut q = cliclack::input(prompt);
|
||||||
match default {
|
if let Some(def) = default { q = q.default_input(def); }
|
||||||
Some(def) => {
|
q.interact().map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
|
||||||
write!(stdout, "{} [{}]: ", prompt, def)?;
|
}
|
||||||
}
|
|
||||||
None => {
|
/// Present a single-choice selector and return the selected index.
|
||||||
write!(stdout, "{}: ", prompt)?;
|
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();
|
/// A simple spinner wrapper built on top of `cliclack::spinner()`.
|
||||||
io::stdin().read_line(&mut buf)?;
|
///
|
||||||
let trimmed = buf.trim();
|
/// This wrapper provides a minimal API with start/stop/success/error methods
|
||||||
if trimmed.is_empty() {
|
/// to standardize spinner usage across the project.
|
||||||
Ok(default.unwrap_or_default().to_string())
|
pub struct Spinner(cliclack::ProgressBar);
|
||||||
} else {
|
|
||||||
Ok(trimmed.to_string())
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,22 +1,21 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright (c) 2025 <COPYRIGHT HOLDER>. All rights reserved.
|
// Copyright (c) 2025 <COPYRIGHT HOLDER>. All rights reserved.
|
||||||
|
|
||||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
|
||||||
use std::io::IsTerminal as _;
|
use std::io::IsTerminal as _;
|
||||||
|
|
||||||
/// Manages a set of per-file progress bars plus a top aggregate bar.
|
/// Manages a set of per-file progress bars plus a top aggregate bar using cliclack.
|
||||||
pub struct ProgressManager {
|
pub struct ProgressManager {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
mp: Option<MultiProgress>,
|
per: Vec<cliclack::ProgressBar>,
|
||||||
per: Vec<ProgressBar>,
|
total: Option<cliclack::ProgressBar>,
|
||||||
total: Option<ProgressBar>,
|
|
||||||
completed: usize,
|
completed: usize,
|
||||||
|
total_len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProgressManager {
|
impl ProgressManager {
|
||||||
/// Create a new manager with the given enabled flag.
|
/// Create a new manager with the given enabled flag.
|
||||||
pub fn new(enabled: bool) -> Self {
|
pub fn new(enabled: bool) -> Self {
|
||||||
Self { enabled, mp: None, per: Vec::new(), total: None, completed: 0 }
|
Self { enabled, per: Vec::new(), total: None, completed: 0, total_len: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a manager that enables bars when `n > 1`, stderr is a TTY, and not quiet.
|
/// Create a manager that enables bars when `n > 1`, stderr is a TTY, and not quiet.
|
||||||
@@ -27,61 +26,69 @@ impl ProgressManager {
|
|||||||
|
|
||||||
/// Initialize bars for the given file labels. If disabled or single file, no-op.
|
/// Initialize bars for the given file labels. If disabled or single file, no-op.
|
||||||
pub fn init_files(&mut self, labels: &[String]) {
|
pub fn init_files(&mut self, labels: &[String]) {
|
||||||
|
self.total_len = labels.len();
|
||||||
if !self.enabled || labels.len() <= 1 {
|
if !self.enabled || labels.len() <= 1 {
|
||||||
// No bars in single-file mode or when disabled
|
// No bars in single-file mode or when disabled
|
||||||
self.enabled = false;
|
self.enabled = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mp = MultiProgress::new();
|
|
||||||
// Aggregate bar at the top
|
// Aggregate bar at the top
|
||||||
let total = mp.add(ProgressBar::new(labels.len() as u64));
|
let mut total = cliclack::progress_bar(labels.len() as u64);
|
||||||
total.set_style(ProgressStyle::with_template("{prefix} [{bar:40.cyan/blue}] {pos}/{len}")
|
total.start("Total");
|
||||||
.unwrap()
|
|
||||||
.progress_chars("=>-"));
|
|
||||||
total.set_prefix("Total");
|
|
||||||
self.total = Some(total);
|
self.total = Some(total);
|
||||||
// Per-file bars
|
// Per-file bars (100% scale for each)
|
||||||
for label in labels {
|
for label in labels {
|
||||||
let pb = mp.add(ProgressBar::new(100));
|
let mut pb = cliclack::progress_bar(100);
|
||||||
pb.set_style(ProgressStyle::with_template("{prefix} [{bar:40.green/black}] {pos}% {msg}")
|
pb.start(label);
|
||||||
.unwrap()
|
|
||||||
.progress_chars("=>-"));
|
|
||||||
pb.set_position(0);
|
|
||||||
pb.set_prefix(label.clone());
|
|
||||||
self.per.push(pb);
|
self.per.push(pb);
|
||||||
}
|
}
|
||||||
self.mp = Some(mp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true when bars are enabled (multi-file TTY mode).
|
/// Returns true when bars are enabled (multi-file TTY mode).
|
||||||
pub fn is_enabled(&self) -> bool { self.enabled }
|
pub fn is_enabled(&self) -> bool { self.enabled }
|
||||||
|
|
||||||
/// Get a clone of the per-file progress bar at index, if enabled.
|
/// Update a per-file bar message.
|
||||||
pub fn per_bar(&self, idx: usize) -> Option<ProgressBar> {
|
pub fn set_per_message(&mut self, idx: usize, message: &str) {
|
||||||
if !self.enabled { return None; }
|
if !self.enabled { return; }
|
||||||
self.per.get(idx).cloned()
|
if let Some(pb) = self.per.get_mut(idx) {
|
||||||
|
pb.set_message(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a clone of the aggregate (total) progress bar, if enabled.
|
/// Update a per-file bar percent (0..=100).
|
||||||
pub fn total_bar(&self) -> Option<ProgressBar> {
|
pub fn set_per_percent(&mut self, idx: usize, percent: u64) {
|
||||||
if !self.enabled { return None; }
|
if !self.enabled { return; }
|
||||||
self.total.as_ref().cloned()
|
if let Some(pb) = self.per.get_mut(idx) {
|
||||||
|
let p = percent.min(100);
|
||||||
|
pb.set_message(&format!("{p}%"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark a file as finished (set to 100% and update total counter).
|
/// Mark a file as finished (set to 100% and update total counter).
|
||||||
pub fn mark_file_done(&mut self, idx: usize) {
|
pub fn mark_file_done(&mut self, idx: usize) {
|
||||||
if !self.enabled { return; }
|
if !self.enabled { return; }
|
||||||
if let Some(pb) = self.per.get(idx) {
|
if let Some(pb) = self.per.get_mut(idx) {
|
||||||
pb.set_position(100);
|
pb.stop("done");
|
||||||
pb.finish_with_message("done");
|
|
||||||
}
|
}
|
||||||
self.completed += 1;
|
self.completed += 1;
|
||||||
if let Some(total) = &self.total { total.set_position(self.completed as u64); }
|
if let Some(total) = &mut self.total {
|
||||||
|
total.inc(1);
|
||||||
|
if self.completed >= self.total_len {
|
||||||
|
total.stop("all done");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finish the aggregate bar with a custom message.
|
||||||
|
pub fn finish_total(&mut self, message: &str) {
|
||||||
|
if !self.enabled { return; }
|
||||||
|
if let Some(total) = &mut self.total {
|
||||||
|
total.stop(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A simple reporter for displaying progress messages in the terminal.
|
/// A simple reporter for displaying progress messages using cliclack logging.
|
||||||
/// Provides different output formatting based on whether the environment is interactive or not.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProgressReporter {
|
pub struct ProgressReporter {
|
||||||
non_interactive: bool,
|
non_interactive: bool,
|
||||||
@@ -89,37 +96,23 @@ pub struct ProgressReporter {
|
|||||||
|
|
||||||
impl ProgressReporter {
|
impl ProgressReporter {
|
||||||
/// Creates a new progress reporter.
|
/// Creates a new progress reporter.
|
||||||
///
|
pub fn new(non_interactive: bool) -> Self { Self { non_interactive } }
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `non_interactive` - Whether the output should be formatted for non-interactive environments.
|
|
||||||
pub fn new(non_interactive: bool) -> Self {
|
|
||||||
Self { non_interactive }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Displays a progress step message.
|
/// Displays a progress step message.
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `message` - The message to display for this progress step.
|
|
||||||
pub fn step(&mut self, message: &str) {
|
pub fn step(&mut self, message: &str) {
|
||||||
if self.non_interactive {
|
if self.non_interactive {
|
||||||
eprintln!("[..] {message}");
|
let _ = cliclack::log::info(format!("[..] {message}"));
|
||||||
} else {
|
} else {
|
||||||
eprintln!("• {message}");
|
let _ = cliclack::log::info(format!("• {message}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Displays a completion message.
|
/// Displays a completion message.
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `message` - The message to display when a task is completed.
|
|
||||||
pub fn finish_with_message(&mut self, message: &str) {
|
pub fn finish_with_message(&mut self, message: &str) {
|
||||||
if self.non_interactive {
|
if self.non_interactive {
|
||||||
eprintln!("[ok] {message}");
|
let _ = cliclack::log::info(format!("[ok] {message}"));
|
||||||
} else {
|
} else {
|
||||||
eprintln!("✓ {message}");
|
let _ = cliclack::log::info(format!("✓ {message}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user