use anyhow::Result;
use colored::*;
use indicatif::{ProgressBar, ProgressStyle};
use sha2::{Digest, Sha256};
use std::fs;
use std::path::Path;
use std::time::Duration;
pub fn use_color() -> bool {
atty::is(atty::Stream::Stdout)
}
pub fn create_spinner(message: &str) -> ProgressBar {
let pb = ProgressBar::new_spinner();
pb.set_style(
ProgressStyle::default_spinner()
.template("{spinner:.green} {msg}")
.unwrap()
.tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ "),
);
pb.set_message(message.to_string());
pb.enable_steady_tick(Duration::from_millis(100));
pb
}
pub fn hash_file_content(content: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(content.as_bytes());
format!("{:x}", hasher.finalize())
}
pub fn file_has_changed(path: &Path, expected_content: &str) -> Result<bool> {
if !path.exists() {
return Ok(true);
}
let current_content = fs::read_to_string(path)?;
Ok(hash_file_content(¤t_content) != hash_file_content(expected_content))
}
pub fn format_size(bytes: u64) -> String {
const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
let mut size = bytes as f64;
let mut unit_idx = 0;
while size >= 1024.0 && unit_idx < UNITS.len() - 1 {
size /= 1024.0;
unit_idx += 1;
}
if unit_idx == 0 {
format!("{} {}", bytes, UNITS[0])
} else {
format!("{:.2} {}", size, UNITS[unit_idx])
}
}
pub fn success(message: &str) {
if use_color() {
println!("{} {}", "✓".green().bold(), message);
} else {
println!("✓ {}", message);
}
}
pub fn error(message: &str) {
if use_color() {
eprintln!("{} {}", "✗".red().bold(), message);
} else {
eprintln!("✗ {}", message);
}
}
pub fn warning(message: &str) {
if use_color() {
println!("{} {}", "⚠".yellow().bold(), message);
} else {
println!("⚠ {}", message);
}
}
pub fn info(message: &str) {
if use_color() {
println!("{} {}", "ℹ".blue().bold(), message);
} else {
println!("ℹ {}", message);
}
}
pub fn suggest_command(message: &str, command: &str) {
if use_color() {
println!("{}", message);
println!(" Try: {}", command.cyan().bold());
} else {
println!("{}", message);
println!(" Try: {}", command);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hash_file_content() {
let content = "test content";
let hash1 = hash_file_content(content);
let hash2 = hash_file_content(content);
assert_eq!(hash1, hash2);
let different = hash_file_content("different content");
assert_ne!(hash1, different);
}
#[test]
fn test_format_size() {
assert_eq!(format_size(100), "100 B");
assert_eq!(format_size(1024), "1.00 KB");
assert_eq!(format_size(1024 * 1024), "1.00 MB");
assert_eq!(format_size(1024 * 1024 * 1024), "1.00 GB");
}
}