rskiller 0.2.1

Find and clean Rust project build artifacts and caches with parallel processing
Documentation
use anyhow::Result;
use clap::{CommandFactory, Parser};
use clap_complete::generate;
use std::fs;
use std::io;
use std::path::PathBuf;
use rskiller::{Cli, ConfigLoader, ProjectScanner, InteractiveUI};

#[tokio::main]
async fn main() -> Result<()> {
    // Parse CLI args first to check for config-related commands
    let cli = Cli::parse();
    
    // Handle completion generation
    if let Some(shell) = cli.completion {
        let mut cmd = Cli::command();
        generate(shell, &mut cmd, "rskiller", &mut io::stdout());
        return Ok(());
    }
    
    // Handle config-specific commands
    if cli.print_config {
        let loader = ConfigLoader::new();
        let config = loader.load_merged_config()?;
        let config_str = serde_json::to_string_pretty(&config)?;
        println!("{}", config_str);
        return Ok(());
    }
    
    if cli.init_config {
        let format = cli.format.clone().into();
        let sample_config = ConfigLoader::generate_sample_config(format)?;
        
        let filename = match format {
            rskiller::config::ConfigFormat::Toml => {
                if cli.global {
                    if let Some(config_dir) = dirs::config_dir() {
                        let dir = config_dir.join("rskiller");
                        fs::create_dir_all(&dir)?;
                        dir.join("rskill.toml")
                    } else {
                        PathBuf::from("rskill.toml")
                    }
                } else {
                    PathBuf::from("rskill.toml")
                }
            }
            rskiller::config::ConfigFormat::Json => {
                if cli.global {
                    if let Some(home_dir) = home::home_dir() {
                        home_dir.join(".rskillrc")
                    } else {
                        PathBuf::from(".rskillrc")
                    }
                } else {
                    PathBuf::from(".rskillrc")
                }
            }
        };
        
        if filename.exists() {
            eprintln!("Configuration file already exists: {}", filename.display());
            eprintln!("Use --force to overwrite (not implemented yet)");
            return Ok(());
        }
        
        fs::write(&filename, sample_config)?;
        println!("Generated configuration file: {}", filename.display());
        return Ok(());
    }
    
    if let Some(config_path) = &cli.validate_config {
        let loader = ConfigLoader::new();
        match loader.load_from_file(config_path) {
            Ok(_) => println!("Configuration file is valid: {}", config_path.display()),
            Err(e) => {
                eprintln!("Configuration file is invalid: {}", e);
                std::process::exit(1);
            }
        }
        return Ok(());
    }
    
    if cli.config_info {
        let loader = ConfigLoader::new();
        println!("Configuration file search paths (in priority order):");
        for (i, path) in loader.search_paths.iter().enumerate() {
            let exists = if path.exists() { " (exists)" } else { "" };
            println!("  {}. {}{}", i + 1, path.display(), exists);
        }
        return Ok(());
    }
    
    // Load configuration
    let loader = if let Some(config_path) = &cli.config {
        ConfigLoader::with_paths(vec![config_path.clone()])
    } else {
        ConfigLoader::new()
    };
    
    let config = loader.load_merged_config()?;
    
    // Create final CLI with config defaults and command line overrides
    let final_cli = Cli::from_config_and_args(config.clone());
    
    let scanner = ProjectScanner::new(final_cli.clone(), config.clone());
    
    if final_cli.list_only {
        // Non-interactive mode - just list projects
        let projects = scanner.scan().await?;
        scanner.print_projects(&projects).await?;
    } else {
        // Interactive mode
        let mut ui = InteractiveUI::new(final_cli.clone(), config.clone());
        ui.run().await?;
    }
    
    Ok(())
}