#![allow(dead_code)]
use colored::Colorize;
use indicatif::{ProgressBar, ProgressStyle};
use std::time::Duration;
pub mod symbols {
pub const ARROW: &str = "→";
pub const SUCCESS: &str = "✓";
pub const FAILURE: &str = "✗";
pub const WARNING: &str = "!";
pub const BULLET: &str = "•";
pub const ACTIVE: &str = "●";
pub const INACTIVE: &str = "○";
pub const IN_PROGRESS: &str = "◐";
pub const UPLOAD: &str = "↑";
pub const PLUS: &str = "+";
pub const EQUALS: &str = "=";
}
pub fn print_step(message: &str) {
println!("{} {}", symbols::ARROW.blue().bold(), message);
}
pub fn print_numbered_step(num: u32, message: &str) {
println!("\n{} {}", num.to_string().blue().bold(), message);
}
pub fn print_success(message: &str) {
println!("{} {}", symbols::SUCCESS.green().bold(), message);
}
pub fn print_error(message: &str) {
println!("{} {}", symbols::FAILURE.red().bold(), message);
}
pub fn print_warning(message: &str) {
println!("{} {}", symbols::WARNING.yellow().bold(), message);
}
pub fn print_info(message: &str) {
println!(" {}", message.dimmed());
}
pub fn print_section(title: &str) {
println!();
println!("{}", title.bold());
println!("{}", "─".repeat(50).dimmed());
}
pub fn print_divider() {
println!("{}", "━".repeat(50).dimmed());
}
pub fn create_spinner(message: &str) -> ProgressBar {
let spinner = ProgressBar::new_spinner();
spinner.set_style(
ProgressStyle::default_spinner()
.tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏")
.template("{spinner:.blue} {msg}")
.expect("Invalid spinner template"),
);
spinner.set_message(message.to_string());
spinner.enable_steady_tick(Duration::from_millis(80));
spinner
}
pub fn create_progress_bar(total: u64, message: &str) -> ProgressBar {
let bar = ProgressBar::new(total);
bar.set_style(
ProgressStyle::default_bar()
.template("{spinner:.blue} [{bar:30.green/dim}] {pos}% {msg}")
.expect("Invalid progress bar template")
.progress_chars("█░░"),
);
bar.set_message(message.to_string());
bar.enable_steady_tick(Duration::from_millis(100));
bar
}
pub fn create_build_progress_bar() -> ProgressBar {
let bar = ProgressBar::new(100);
bar.set_style(
ProgressStyle::default_bar()
.template("{spinner:.blue} [{bar:30.green/dim}] {pos}% {msg}")
.expect("Invalid progress bar template")
.progress_chars("█░░"),
);
bar.enable_steady_tick(Duration::from_millis(100));
bar
}
pub fn format_build_status(status: &str) -> String {
match status.to_lowercase().as_str() {
"pending" => status.yellow().to_string(),
"uploading" => status.yellow().to_string(),
"queued" => status.yellow().to_string(),
"building" => status.blue().to_string(),
"pushing" => status.blue().to_string(),
"deploying" => status.blue().to_string(),
"completed" => status.green().bold().to_string(),
"failed" => status.red().bold().to_string(),
"cancelled" => status.dimmed().to_string(),
_ => status.to_string(),
}
}
pub fn format_deployment_status(status: &str) -> String {
match status.to_lowercase().as_str() {
"active" => "active".green().to_string(),
"updating" => "updating".yellow().to_string(),
"stopped" => "stopped".dimmed().to_string(),
"failed" => "failed".red().to_string(),
_ => status.to_string(),
}
}
pub fn humanize_phase(phase: &str) -> &str {
match phase.to_uppercase().as_str() {
"SUBMITTED" => "Queued",
"PROVISIONING" => "Starting build environment",
"DOWNLOAD_SOURCE" => "Preparing",
"INSTALL" => "Installing dependencies",
"PRE_BUILD" => "Preparing build",
"BUILD" => "Building",
"POST_BUILD" => "Finalizing build",
"UPLOAD_ARTIFACTS" => "Publishing image",
"FINALIZING" => "Deploying",
"COMPLETED" => "Completed",
_ => phase,
}
}
pub fn format_relative_time(timestamp: &str) -> Option<String> {
let parsed = chrono::DateTime::parse_from_rfc3339(timestamp).ok()?;
let now = chrono::Utc::now();
let duration = now.signed_duration_since(parsed);
let seconds = duration.num_seconds();
if seconds < 0 {
return Some("just now".to_string());
}
if seconds < 60 {
return Some("just now".to_string());
}
let minutes = duration.num_minutes();
if minutes < 60 {
return Some(format!(
"{} minute{} ago",
minutes,
if minutes == 1 { "" } else { "s" }
));
}
let hours = duration.num_hours();
if hours < 24 {
return Some(format!(
"{} hour{} ago",
hours,
if hours == 1 { "" } else { "s" }
));
}
let days = duration.num_days();
if days < 30 {
return Some(format!(
"{} day{} ago",
days,
if days == 1 { "" } else { "s" }
));
}
Some(parsed.format("%Y-%m-%d").to_string())
}
pub fn timestamp_now() -> String {
chrono::Local::now().format("%H:%M:%S").to_string()
}
pub const DEFAULT_POLL_TIMEOUT_SECS: u64 = 1800;
pub const DEFAULT_POLL_INTERVAL_MS: u64 = 500;
pub const STATUS_POLL_INTERVAL_SECS: u64 = 3;