hlx 1.2.5

Configuration language designed specifically for ml/ai/data systems
Documentation
use std::path::PathBuf;
use std::fs;
use anyhow::{Result, Context};
pub fn manage_cache(action: CacheAction, verbose: bool) -> Result<()> {
    match action {
        CacheAction::Show => show_cache_info(verbose),
        CacheAction::Clear => clear_cache(verbose),
        CacheAction::Clean => clean_cache(verbose),
        CacheAction::Size => show_cache_size(verbose),
    }
}
#[derive(Debug)]
enum CacheAction {
    Show,
    Clear,
    Clean,
    Size,
}
impl std::str::FromStr for CacheAction {
    type Err = anyhow::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "show" => Ok(CacheAction::Show),
            "clear" => Ok(CacheAction::Clear),
            "clean" => Ok(CacheAction::Clean),
            "size" => Ok(CacheAction::Size),
            _ => Err(anyhow::anyhow!("Invalid cache action: {}", s)),
        }
    }
}
fn show_cache_info(verbose: bool) -> Result<()> {
    let cache_dir = get_cache_directory()?;
    if !cache_dir.exists() {
        println!("📁 Cache directory: {}", cache_dir.display());
        println!("   Status: Not created yet");
        return Ok(());
    }
    let cache_size = calculate_directory_size(&cache_dir)?;
    let file_count = count_files_in_directory(&cache_dir)?;
    println!("📁 Cache Information");
    println!("===================");
    println!("Location: {}", cache_dir.display());
    println!("Size: {}", format_size(cache_size));
    println!("Files: {}", file_count);
    if verbose {
        println!("\n📋 Cache Contents:");
        list_cache_contents(&cache_dir, 0)?;
    }
    Ok(())
}
fn clear_cache(verbose: bool) -> Result<()> {
    let cache_dir = get_cache_directory()?;
    if !cache_dir.exists() {
        println!("✅ Cache is already empty");
        return Ok(());
    }
    if verbose {
        println!("🗑️  Clearing cache directory: {}", cache_dir.display());
    }
    let cache_size = calculate_directory_size(&cache_dir)?;
    let file_count = count_files_in_directory(&cache_dir)?;
    fs::remove_dir_all(&cache_dir).context("Failed to remove cache directory")?;
    println!("✅ Cache cleared successfully!");
    println!("  Removed: {} files", file_count);
    println!("  Freed: {}", format_size(cache_size));
    Ok(())
}
fn clean_cache(verbose: bool) -> Result<()> {
    let cache_dir = get_cache_directory()?;
    if !cache_dir.exists() {
        println!("✅ Cache is already clean");
        return Ok(());
    }
    if verbose {
        println!("🧹 Cleaning cache directory: {}", cache_dir.display());
    }
    let mut removed_files = 0;
    let mut freed_space = 0;
    clean_expired_entries(&cache_dir, &mut removed_files, &mut freed_space, verbose)?;
    clean_orphaned_files(&cache_dir, &mut removed_files, &mut freed_space, verbose)?;
    if removed_files > 0 {
        println!("✅ Cache cleaned successfully!");
        println!("  Removed: {} files", removed_files);
        println!("  Freed: {}", format_size(freed_space));
    } else {
        println!("✅ Cache is already clean");
    }
    Ok(())
}
fn show_cache_size(verbose: bool) -> Result<()> {
    let cache_dir = get_cache_directory()?;
    if !cache_dir.exists() {
        println!("0 bytes");
        return Ok(());
    }
    let cache_size = calculate_directory_size(&cache_dir)?;
    let file_count = count_files_in_directory(&cache_dir)?;
    if verbose {
        println!("Cache Size: {}", format_size(cache_size));
        println!("Files: {}", file_count);
    } else {
        println!("{}", format_size(cache_size));
    }
    Ok(())
}
fn clean_expired_entries(
    cache_dir: &PathBuf,
    removed_files: &mut usize,
    freed_space: &mut u64,
    verbose: bool,
) -> Result<()> {
    let entries = fs::read_dir(cache_dir).context("Failed to read cache directory")?;
    for entry in entries {
        let entry = entry.context("Failed to read directory entry")?;
        let path = entry.path();
        if path.is_file() {
            let metadata = fs::metadata(&path).context("Failed to read file metadata")?;
            let modified = metadata
                .modified()
                .context("Failed to get file modification time")?;
            let age = std::time::SystemTime::now()
                .duration_since(modified)
                .unwrap_or_default();
            if age.as_secs() > 24 * 3600 {
                let file_size = metadata.len();
                fs::remove_file(&path).context("Failed to remove expired file")?;
                *removed_files += 1;
                *freed_space += file_size;
                if verbose {
                    println!(
                        "  Removed expired: {}", path.file_name().unwrap()
                        .to_string_lossy()
                    );
                }
            }
        } else if path.is_dir() {
            clean_expired_entries(&path, removed_files, freed_space, verbose)?;
        }
    }
    Ok(())
}
fn clean_orphaned_files(
    cache_dir: &PathBuf,
    removed_files: &mut usize,
    freed_space: &mut u64,
    verbose: bool,
) -> Result<()> {
    let entries = fs::read_dir(cache_dir).context("Failed to read cache directory")?;
    for entry in entries {
        let entry = entry.context("Failed to read directory entry")?;
        let path = entry.path();
        if path.is_file() {
            if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) {
                if file_name.ends_with(".hlxb") {
                    let source_name = file_name.replace(".hlxb", ".hlx");
                    let source_path = cache_dir.join(&source_name);
                    if !source_path.exists() {
                        let file_size = fs::metadata(&path)?.len();
                        fs::remove_file(&path)
                            .context("Failed to remove orphaned file")?;
                        *removed_files += 1;
                        *freed_space += file_size;
                        if verbose {
                            println!("  Removed orphaned: {}", file_name);
                        }
                    }
                }
            }
        } else if path.is_dir() {
            clean_orphaned_files(&path, removed_files, freed_space, verbose)?;
        }
    }
    Ok(())
}
fn list_cache_contents(dir: &PathBuf, depth: usize) -> Result<()> {
    let entries = fs::read_dir(dir).context("Failed to read directory")?;
    let mut entries: Vec<_> = entries
        .collect::<Result<Vec<_>, _>>()?
        .into_iter()
        .collect();
    entries.sort_by_key(|e| e.file_name());
    for entry in entries {
        let path = entry.path();
        let indent = "  ".repeat(depth);
        if path.is_file() {
            let metadata = fs::metadata(&path)?;
            let size = format_size(metadata.len());
            let modified = metadata.modified()?;
            let age = std::time::SystemTime::now()
                .duration_since(modified)
                .unwrap_or_default();
            println!(
                "{}{} ({} - {} ago)", indent, path.file_name().unwrap()
                .to_string_lossy(), size, format_duration(age)
            );
        } else if path.is_dir() {
            println!("{}{}/", indent, path.file_name().unwrap().to_string_lossy());
            list_cache_contents(&path, depth + 1)?;
        }
    }
    Ok(())
}
fn calculate_directory_size(dir: &PathBuf) -> Result<u64> {
    let mut total_size = 0;
    let entries = fs::read_dir(dir).context("Failed to read directory")?;
    for entry in entries {
        let entry = entry.context("Failed to read directory entry")?;
        let path = entry.path();
        if path.is_file() {
            let metadata = fs::metadata(&path).context("Failed to read file metadata")?;
            total_size += metadata.len();
        } else if path.is_dir() {
            total_size += calculate_directory_size(&path)?;
        }
    }
    Ok(total_size)
}
fn count_files_in_directory(dir: &PathBuf) -> Result<usize> {
    let mut count = 0;
    let entries = fs::read_dir(dir).context("Failed to read directory")?;
    for entry in entries {
        let entry = entry.context("Failed to read directory entry")?;
        let path = entry.path();
        if path.is_file() {
            count += 1;
        } else if path.is_dir() {
            count += count_files_in_directory(&path)?;
        }
    }
    Ok(count)
}
fn format_size(bytes: u64) -> String {
    const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
    const THRESHOLD: u64 = 1024;
    if bytes == 0 {
        return "0 B".to_string();
    }
    let mut size = bytes as f64;
    let mut unit_index = 0;
    while size >= THRESHOLD as f64 && unit_index < UNITS.len() - 1 {
        size /= THRESHOLD as f64;
        unit_index += 1;
    }
    if unit_index == 0 {
        format!("{} {}", bytes, UNITS[unit_index])
    } else {
        format!("{:.1} {}", size, UNITS[unit_index])
    }
}
fn format_duration(duration: std::time::Duration) -> String {
    let seconds = duration.as_secs();
    if seconds < 60 {
        format!("{}s", seconds)
    } else if seconds < 3600 {
        format!("{}m", seconds / 60)
    } else if seconds < 86400 {
        format!("{}h", seconds / 3600)
    } else {
        format!("{}d", seconds / 86400)
    }
}
fn get_cache_directory() -> Result<PathBuf> {
    let home_dir = dirs::home_dir()
        .ok_or_else(|| anyhow::anyhow!("Failed to get home directory"))?;
    Ok(home_dir.join(".baton").join("cache"))
}