use anyhow::{bail, Context, Result};
use clap::{Parser, Subcommand};
use colored::Colorize;
mod build;
mod doctor;
mod new;
mod run;
#[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 {
#[arg(hide = true)]
_cargo: Option<String>,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
New {
name: String,
#[arg(long)]
path: Option<String>,
},
Build {
#[arg(long, default_value = "x86_64-linux-android")]
target: String,
#[arg(long)]
release: bool,
#[arg(long, default_value = "dist")]
out: String,
},
Run {
#[arg(long, default_value = "x86_64-linux-android")]
target: String,
#[arg(long)]
device: Option<String>,
},
Doctor,
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);
}
if std::path::Path::new("dist").exists() {
std::fs::remove_dir_all("dist")?;
println!(" {} Removed dist/", "✓".green());
}
println!("{} Clean complete.", "✓".green().bold());
Ok(())
}