Building Your First CLI App

Complete tutorial for building a CLI application with XaCLI

⏱️ 10 minutes

Building Your First CLI App

Setup

cargo new mycli
cd mycli
# Cargo.toml
[dependencies]
xacli = { version = "0.1", features = ["derive"] }

Define CLI with Derive Macros

use xacli::derive::{App, Command};
use xacli::Context;

// Application definition
#[derive(App)]
#[app(
    name = "mycli",
    version = "1.0.0",
    title = "My CLI",
    description = "A sample CLI application"
)]
struct MyCli {
    #[command(subcommands)]
    commands: Commands,
}

// Top-level commands
#[derive(Command)]
enum Commands {
    /// Greet someone
    Greet(GreetCmd),
    /// Database operations
    Db(DbCmd),
}

// Leaf command with arguments
#[derive(Command)]
#[command(description = "Say hello")]
struct GreetCmd {
    #[arg(short = 'n', long = "name")]
    name: bool,

    #[arg(short = 'c', long = "count")]
    count: bool,
}

impl GreetCmd {
    fn run(&self, ctx: &mut dyn Context) -> xacli::Result<()> {
        use std::io::Write;
        writeln!(ctx.stdout(), "Hello!")?;
        Ok(())
    }
}

// Nested command group
#[derive(Command)]
#[command(description = "Database commands")]
struct DbCmd {
    #[command(subcommands)]
    action: DbActions,
}

#[derive(Command)]
enum DbActions {
    /// Run migrations
    Migrate(DbMigrateCmd),
    /// Seed database
    Seed(DbSeedCmd),
}

#[derive(Command)]
#[command(name = "migrate")]
struct DbMigrateCmd {
    #[arg(long = "dry-run")]
    dry_run: bool,
}

impl DbMigrateCmd {
    fn run(&self, ctx: &mut dyn Context) -> xacli::Result<()> {
        use std::io::Write;
        writeln!(ctx.stdout(), "Running migrations...")?;
        Ok(())
    }
}

#[derive(Command)]
#[command(name = "seed")]
struct DbSeedCmd {}

impl DbSeedCmd {
    fn run(&self, ctx: &mut dyn Context) -> xacli::Result<()> {
        use std::io::Write;
        writeln!(ctx.stdout(), "Seeding database...")?;
        Ok(())
    }
}

fn main() {
    if let Err(e) = MyCli::execute() {
        eprintln!("Error: {}", e);
        std::process::exit(1);
    }
}

Run

cargo run -- --help
cargo run -- greet --help
cargo run -- db migrate --dry-run
cargo run -- db seed

Key Patterns

PatternUsage
#[derive(App)]Application entry point
#[derive(Command)]Commands and subcommands
#[command(subcommands)]Mark field as subcommand container
#[arg(short, long)]Define arguments
impl XxxCmd { fn run() }Command execution logic

Next Steps

  • Run full example: cargo run -p xacli --example derive --features derive,testing
  • Add testing with xacli::testing
  • Add interactive components with features = ["components"]