scoop-uv 0.13.0

Scoop up your Python envs — pyenv-style workflow powered by uv
Documentation
//! scoop - Python virtual environment manager powered by uv

use clap::Parser;
use color_eyre::eyre::Result;

use scoop_uv::cli::{Cli, Commands, SelfCommand};
use scoop_uv::output::Output;

fn main() -> Result<()> {
    // Initialize i18n (must be early, before any translated output)
    scoop_uv::i18n::init();

    // Initialize error handling
    color_eyre::install()?;

    // Initialize logging
    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::from_default_env()
                .add_directive(tracing::Level::WARN.into()),
        )
        .init();

    // Parse CLI arguments
    let cli = Cli::parse();

    // Execute command
    let result = match cli.command {
        Commands::List {
            pythons,
            bare,
            python_version,
            sort,
            json,
        } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::list(&output, pythons, bare, python_version.as_deref(), sort)
        }
        Commands::Create {
            name,
            python,
            python_path,
            force,
            install_python,
            json,
        } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::create(
                &output,
                &name,
                &python,
                python_path.as_deref(),
                force,
                install_python,
            )
        }
        Commands::Doctor { verbose, json, fix } => {
            let output = Output::new(verbose, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::doctor(&output, fix)
        }
        Commands::Info {
            name,
            json,
            all_packages,
            no_size,
        } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::info(&output, &name, all_packages, no_size)
        }
        Commands::Use {
            name,
            unset,
            global,
            link,
            no_link: _, // explicit option, same as default (no symlink)
            json,
        } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::use_env(&output, name.as_deref(), unset, global, link)
        }
        Commands::Remove { name, force, json } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::remove(&output, &name, force)
        }
        Commands::Install {
            python_version,
            latest,
            stable,
            json,
        } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::install(&output, python_version.as_deref(), latest, stable)
        }
        Commands::Uninstall {
            python_version,
            cascade,
            force,
            json,
        } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::uninstall(&output, &python_version, cascade, force)
        }
        Commands::Init { shell } => scoop_uv::cli::commands::init(shell),
        Commands::Completions { shell } => scoop_uv::cli::commands::completions(shell),
        Commands::Resolve => scoop_uv::cli::commands::resolve(),
        Commands::Activate { name, shell } => scoop_uv::cli::commands::activate(&name, shell),
        Commands::Deactivate { shell } => scoop_uv::cli::commands::deactivate(shell),
        Commands::Shell { name, unset, shell } => {
            let output = Output::new(0, cli.quiet, cli.no_color, false);
            scoop_uv::cli::commands::shell(&output, name.as_deref(), unset, shell)
        }
        Commands::Migrate { command } => {
            let output = Output::new(0, cli.quiet, cli.no_color, false);
            scoop_uv::cli::commands::migrate(&output, command)
        }
        Commands::Lang {
            lang,
            list,
            reset,
            json,
        } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::lang(&output, lang.as_deref(), list, reset)
        }
        Commands::Self_ { command } => match command {
            SelfCommand::Update {
                force,
                version,
                no_verify,
                json,
            } => {
                let output = Output::new(0, cli.quiet, cli.no_color, json);
                scoop_uv::cli::commands::self_update(&output, force, version.as_deref(), no_verify)
            }
        },
        Commands::Status { json } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::status(&output)
        }
        Commands::Run { env, command } => {
            let output = Output::new(0, cli.quiet, cli.no_color, false);
            scoop_uv::cli::commands::run(&output, &env, &command)
        }
        Commands::Sync {
            with,
            dry_run,
            json,
        } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::sync(&output, &with, dry_run)
        }
        Commands::Clone {
            src,
            dst,
            no_packages,
            force,
            json,
        } => {
            let out = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::clone(&out, &src, &dst, no_packages, force)
        }
        Commands::Export { name, output } => {
            // Stdout is the schema itself; status messages go to stderr only.
            let out = Output::new(0, cli.quiet, cli.no_color, false);
            scoop_uv::cli::commands::export(&out, &name, output.as_deref())
        }
        Commands::Import {
            path,
            name,
            force,
            json,
        } => {
            let out = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::import(&out, &path, name.as_deref(), force)
        }
        Commands::Which { exe, env, json } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::which(&output, &exe, env.as_deref())
        }
        Commands::Prune { json } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::prune(&output)
        }
        Commands::Gc {
            yes,
            aggressive,
            older_than,
            json,
        } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::gc(&output, yes, aggressive, older_than.as_deref())
        }
        Commands::Man { output_dir, json } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::man(&output, output_dir.as_deref())
        }
        Commands::Verify { name, strict, json } => {
            let output = Output::new(0, cli.quiet, cli.no_color, json);
            scoop_uv::cli::commands::verify(&output, name.as_deref(), strict)
        }
    };

    // Handle errors
    if let Err(e) = result {
        let output = Output::new(0, cli.quiet, cli.no_color, false);
        output.error(&e.to_string());

        // Print suggestion hint if available
        if let Some(suggestion) = e.suggestion() {
            eprintln!("{suggestion}");
        }

        std::process::exit(1);
    }

    Ok(())
}