use crate::errors::SecParError;
use comfy_table::{Table, presets::UTF8_FULL};
use dialoguer::{Confirm, Select};
use indicatif::{ProgressBar, ProgressStyle};
use std::time::Duration;
pub fn new_spinner(message: &str) -> ProgressBar {
let pb = ProgressBar::new_spinner();
pb.enable_steady_tick(Duration::from_millis(80));
pb.set_style(
ProgressStyle::with_template("{spinner:.blue} {msg}")
.unwrap()
.tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]),
);
pb.set_message(message.to_owned());
pb
}
pub fn confirm_delete(resource_name: &str) -> Result<bool, SecParError> {
Confirm::new()
.with_prompt(format!("Delete '{resource_name}'?"))
.default(false)
.interact()
.map_err(|e| SecParError::Interactive(e.to_string()))
}
pub fn confirm_action(prompt: &str) -> Result<bool, SecParError> {
Confirm::new()
.with_prompt(prompt)
.default(false)
.interact()
.map_err(|e| SecParError::Interactive(e.to_string()))
}
pub fn select_from_list(prompt: &str, items: &[String]) -> Result<String, SecParError> {
if items.is_empty() {
return Err(SecParError::Interactive(
"No items available to select from".to_string(),
));
}
let idx = Select::new()
.with_prompt(prompt)
.items(items)
.default(0)
.interact()
.map_err(|e| SecParError::Interactive(e.to_string()))?;
Ok(items[idx].clone())
}
pub fn build_secrets_table(rows: &[(&str, &str, &str)]) -> Table {
let mut table = Table::new();
table.load_preset(UTF8_FULL);
table.set_header(["NAME", "ARN", "LAST CHANGED"]);
for (name, arn, last_changed) in rows {
table.add_row([*name, *arn, *last_changed]);
}
table
}
pub fn build_parameters_table(rows: &[(&str, &str, &str)]) -> Table {
let mut table = Table::new();
table.load_preset(UTF8_FULL);
table.set_header(["NAME", "TYPE", "LAST MODIFIED"]);
for (name, typ, last_modified) in rows {
table.add_row([*name, *typ, *last_modified]);
}
table
}
pub fn print_success(msg: &str) {
println!("✅ {msg}");
}
pub fn print_info(msg: &str) {
println!("ℹ️ {msg}");
}
pub fn print_value(label: &str, value: &str) {
println!("🔑 {label}\n{value}");
}
pub fn print_aborted() {
println!("🚫 Aborted.");
}
pub fn print_env_context(context: &str) {
let label = format!(" 🔗 Target: {context} ");
let border = "─".repeat(label.chars().count());
println!("┌{border}┐");
println!("│{label}│");
println!("└{border}┘");
println!();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn select_from_list_errors_on_empty_input() {
let result = select_from_list("Pick one", &[]);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("No items available"));
}
#[test]
fn build_secrets_table_contains_header_and_row() {
let rows = vec![(
"my-secret",
"arn:aws:secretsmanager:us-east-1:123:secret:my-secret",
"2024-06-01",
)];
let table = build_secrets_table(&rows);
let rendered = table.to_string();
assert!(rendered.contains("NAME"));
assert!(rendered.contains("ARN"));
assert!(rendered.contains("LAST CHANGED"));
assert!(rendered.contains("my-secret"));
assert!(rendered.contains("2024-06-01"));
}
#[test]
fn build_secrets_table_handles_empty_rows() {
let table = build_secrets_table(&[]);
let rendered = table.to_string();
assert!(rendered.contains("NAME"));
assert!(rendered.contains("ARN"));
assert!(rendered.contains("LAST CHANGED"));
}
#[test]
fn build_parameters_table_contains_header_and_row() {
let rows = vec![("/my/param", "SecureString", "2024-06-01")];
let table = build_parameters_table(&rows);
let rendered = table.to_string();
assert!(rendered.contains("NAME"));
assert!(rendered.contains("TYPE"));
assert!(rendered.contains("LAST MODIFIED"));
assert!(rendered.contains("/my/param"));
assert!(rendered.contains("SecureString"));
}
#[test]
fn build_parameters_table_handles_empty_rows() {
let table = build_parameters_table(&[]);
let rendered = table.to_string();
assert!(rendered.contains("NAME"));
assert!(rendered.contains("TYPE"));
assert!(rendered.contains("LAST MODIFIED"));
}
}