cargo-bubba 0.1.7

cargo subcommand for the Bubba mobile framework
//! # cargo-bubba
//!
//! The Bubba build tool, installed as a cargo subcommand.
//!
//! ```
//! cargo bubba new my_app      — scaffold a new project
//! cargo bubba build           — compile to .apk
//! cargo bubba run             — build + launch in emulator
//! cargo bubba doctor          — check environment
//! cargo bubba clean           — remove build artifacts
//! ```

use anyhow::{bail, Context, Result};
use clap::{Parser, Subcommand};
use colored::Colorize;

mod build;
mod doctor;
mod new;
mod run;

/// cargo subcommand for the Bubba mobile framework.
/// Fast, modular, beginner-friendly Rust apps for Android.
#[derive(Parser)]
#[command(
    name = "cargo-bubba",
    bin_name = "cargo bubba",
    version,
    about = "⚡ Bubba — The polished Rust framework for mobile apps",
    long_about = None,
)]
struct Cli {
    /// Cargo passes its own name as first arg when used as a subcommand.
    #[arg(hide = true)]
    _cargo: Option<String>,

    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Scaffold a new Bubba project
    New {
        /// Name of the new project
        name: String,
        /// Target directory (defaults to ./<name>)
        #[arg(long)]
        path: Option<String>,
    },
    /// Build the project into a release .apk
    Build {
        /// Android ABI target (default: x86_64-linux-android)
        #[arg(long, default_value = "x86_64-linux-android")]
        target: String,
        /// Build in release mode (optimised)
        #[arg(long)]
        release: bool,
        /// Output directory for the .apk (default: ./dist)
        #[arg(long, default_value = "dist")]
        out: String,
    },
    /// Build and launch in an Android emulator or connected device
    Run {
        /// Android ABI target
        #[arg(long, default_value = "x86_64-linux-android")]
        target: String,
        /// Device serial (from `adb devices`)
        #[arg(long)]
        device: Option<String>,
    },
    /// Diagnose your environment: NDK, SDK, Rust targets, etc.
    Doctor,
    /// Remove build artifacts
    Clean,
}

fn main() {
    env_logger::init();

    print_banner();

    let cli = Cli::parse();

    let result = match cli.command {
        Commands::New { name, path } => new::run(&name, path.as_deref()),
        Commands::Build { target, release, out } => build::run(&target, release, &out),
        Commands::Run { target, device } => run::run(&target, device.as_deref()),
        Commands::Doctor => doctor::run(),
        Commands::Clean => clean(),
    };

    if let Err(e) = result {
        eprintln!("\n{} {}", "✖ Error:".red().bold(), e);
        std::process::exit(1);
    }
}

fn print_banner() {
    println!(
        "\n{} {} {}\n",
        "".yellow(),
        "Bubba".bold().cyan(),
        "— fast, modular, Rust-native mobile".dimmed(),
    );
}

fn clean() -> Result<()> {
    println!("{} Cleaning build artifacts…", "".cyan());
    let status = std::process::Command::new("cargo")
        .args(["clean"])
        .status()
        .context("Failed to run `cargo clean`")?;

    if !status.success() {
        bail!("`cargo clean` exited with status {}", status);
    }

    // Also remove dist/
    if std::path::Path::new("dist").exists() {
        std::fs::remove_dir_all("dist")?;
        println!("  {} Removed dist/", "".green());
    }

    println!("{} Clean complete.", "".green().bold());
    Ok(())
}