use std::collections::HashMap;
use std::time::Duration;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use crate::PromptStepType;
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ProgressItem {
pub message: String,
pub increment: Option<u64>,
pub delay_ms: Option<u64>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase", tag = "type")]
pub enum ProgressType {
#[serde(rename = "bar")]
Bar {
total: u64,
#[serde(default)]
use_download_template: bool,
},
#[serde(rename = "spinner")]
Spinner,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ProgressBarConfig {
pub progress_type: ProgressType,
pub start_message: String,
pub stop_message: String,
pub items: Option<Vec<ProgressItem>>,
}
pub fn handle_progress(
step_type: &PromptStepType,
context: &mut HashMap<String, String>,
) -> Result<()> {
if let PromptStepType::Progress {
progress_type,
start_message,
stop_message,
items,
output
} = step_type {
match progress_type {
ProgressType::Bar { total, use_download_template } => {
let mut progress = cliclack::progress_bar(*total);
if *use_download_template {
progress = progress.with_download_template();
}
progress.start(start_message);
if let Some(progress_items) = items {
for item in progress_items {
if let Some(delay) = item.delay_ms {
std::thread::sleep(Duration::from_millis(delay));
}
let increment = item.increment.unwrap_or(1);
progress.inc(increment);
if !item.message.is_empty() {
progress.set_message(&item.message);
}
}
} else {
for _ in 0..*total {
std::thread::sleep(Duration::from_millis(50));
progress.inc(1);
}
}
progress.stop(stop_message);
},
ProgressType::Spinner => {
let spinner = cliclack::spinner();
spinner.start(start_message);
if let Some(progress_items) = items {
for item in progress_items {
if let Some(delay) = item.delay_ms {
std::thread::sleep(Duration::from_millis(delay));
}
if !item.message.is_empty() {
spinner.set_message(&item.message);
}
}
} else {
std::thread::sleep(Duration::from_secs(2));
}
spinner.stop(stop_message);
}
}
if let Some(output_key) = output {
context.insert(output_key.clone(), "completed".to_string());
}
}
Ok(())
}
pub fn handle_multi_progress(
step_type: &PromptStepType,
context: &mut HashMap<String, String>,
) -> Result<()> {
if let PromptStepType::MultiProgress {
title,
progress_bars,
output
} = step_type {
let multi = cliclack::multi_progress(title);
let mut bars = Vec::new();
let mut spinners = Vec::new();
for (idx, bar_config) in progress_bars.iter().enumerate() {
match &bar_config.progress_type {
ProgressType::Bar { total, use_download_template } => {
let mut progress = multi.add(cliclack::progress_bar(*total));
if *use_download_template {
progress = progress.with_download_template();
}
bars.push((idx, progress, bar_config));
},
ProgressType::Spinner => {
let spinner = multi.add(cliclack::spinner());
spinners.push((idx, spinner, bar_config));
}
}
}
for (_, bar, config) in &bars {
bar.start(&config.start_message);
}
for (_, spinner, config) in &spinners {
spinner.start(&config.start_message);
}
for (_idx, bar, config) in &bars {
if let Some(progress_items) = &config.items {
for item in progress_items {
if let Some(delay) = item.delay_ms {
std::thread::sleep(Duration::from_millis(delay));
}
let increment = item.increment.unwrap_or(1);
bar.inc(increment);
if !item.message.is_empty() {
bar.set_message(&item.message);
}
}
} else {
if let ProgressType::Bar { total, .. } = config.progress_type {
for _ in 0..total {
std::thread::sleep(Duration::from_millis(20));
bar.inc(1);
}
}
}
}
for (_idx, spinner, config) in &spinners {
if let Some(progress_items) = &config.items {
for item in progress_items {
if let Some(delay) = item.delay_ms {
std::thread::sleep(Duration::from_millis(delay));
}
if !item.message.is_empty() {
spinner.set_message(&item.message);
}
}
} else {
std::thread::sleep(Duration::from_secs(1));
}
}
for (_, bar, config) in &bars {
bar.stop(&config.stop_message);
}
for (_, spinner, config) in &spinners {
spinner.stop(&config.stop_message);
}
multi.stop();
if let Some(output_key) = output {
context.insert(output_key.clone(), "completed".to_string());
}
}
Ok(())
}