xacli 0.2.1

A modern, developer-friendly CLI framework for Rust
Documentation
use xacli::derive::{App, Command};
use xacli::Context;

#[derive(App)]
#[app(
    name = "myapp",
    version = "1.0.0",
    title = "My CLI Application",
    description = "A demo CLI built with xacli-derive macros"
)]
struct MyApp {
    #[command(subcommands)]
    commands: AppCommands,
}

#[derive(Command)]
enum AppCommands {
    Serve(ServeCmd),
    Build(BuildCmd),
    Db(DbCmd),
}

#[derive(Command)]
#[command(description = "Start the development server with hot reload")]
struct ServeCmd {
    #[arg(short = 'p', long = "port", default = "8080")]
    port: i64,

    #[arg(short = 'H', long = "host", default = "127.0.0.1")]
    host: String,

    #[arg(short = 'v', long = "verbose")]
    verbose: bool,
}

impl ServeCmd {
    fn run(&self, ctx: &mut dyn Context) -> xacli_core::Result<()> {
        use std::io::Write;
        let mut out = ctx.stdout();
        writeln!(out, "Starting development server...")?;
        writeln!(out, "   Listening on http://{}:{}", self.host, self.port)?;
        if self.verbose {
            writeln!(out, "   Verbose logging enabled")?;
        }
        writeln!(out, "   Press Ctrl+C to stop")?;
        Ok(())
    }
}

#[derive(Command)]
#[command(description = "Build the project for production deployment")]
struct BuildCmd {
    #[arg(long = "release")]
    release: bool,

    #[arg(short = 'o', long = "output", default = "./dist")]
    output: String,
}

impl BuildCmd {
    fn run(&self, ctx: &mut dyn Context) -> xacli_core::Result<()> {
        use std::io::Write;
        let mut out = ctx.stdout();
        let mode = if self.release {
            "release"
        } else {
            "development"
        };
        writeln!(out, "Building project...")?;
        writeln!(out, "   Mode: {}", mode)?;
        writeln!(out, "   Output: {}", self.output)?;
        writeln!(out, "Build complete!")?;
        Ok(())
    }
}

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

#[derive(Command)]
enum DbActions {
    Migrate(DbMigrateCmd),
    Seed(DbSeedCmd),
    Reset(DbResetCmd),
}

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

    #[arg(long = "fresh")]
    fresh: bool,
}

impl DbMigrateCmd {
    fn run(&self, ctx: &mut dyn Context) -> xacli_core::Result<()> {
        use std::io::Write;
        let mut out = ctx.stdout();
        if self.dry_run {
            writeln!(out, "Dry run - no changes will be applied")?;
        }
        if self.fresh {
            writeln!(out, "Running fresh migration (up and down)...")?;
        }
        writeln!(out, "Running database migrations...")?;
        writeln!(out, "   Applied: 001_create_users")?;
        writeln!(out, "   Applied: 002_create_posts")?;
        writeln!(out, "Migrations complete!")?;
        Ok(())
    }
}

#[derive(Command)]
#[command(name = "seed", description = "Populate database with sample data")]
struct DbSeedCmd {
    #[arg(short = 'n', long = "count", default = "10")]
    count: i64,
}

impl DbSeedCmd {
    fn run(&self, ctx: &mut dyn Context) -> xacli_core::Result<()> {
        use std::io::Write;
        let mut out = ctx.stdout();
        writeln!(out, "Seeding database...")?;
        writeln!(out, "   Created {} users", self.count)?;
        writeln!(out, "   Created {} posts", self.count * 5)?;
        writeln!(out, "Seeding complete!")?;
        Ok(())
    }
}

#[derive(Command)]
#[command(name = "reset", description = "Drop all tables and re-run migrations")]
struct DbResetCmd {
    #[arg(short = 'f', long = "force")]
    force: bool,
}

impl DbResetCmd {
    fn run(&self, ctx: &mut dyn Context) -> xacli_core::Result<()> {
        use std::io::Write;
        let mut out = ctx.stdout();
        if !self.force {
            writeln!(
                out,
                "Warning: This will delete all data. Use --force to confirm."
            )?;
            return Ok(());
        }
        writeln!(out, "Resetting database...")?;
        writeln!(out, "   Dropped all tables")?;
        writeln!(out, "   Re-running migrations...")?;
        writeln!(out, "Database reset complete!")?;
        Ok(())
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;
    use xacli::testing::{assert, TestCase, TestCaseStatus, TestingApp};

    #[test]
    fn test_with_testing_framework() {
        let app = TestingApp::new(MyApp::build());

        let test_case = TestCase::new("help output")
            .args(vec!["--help".to_string()])
            .assertions(vec![
                assert::success(),
                assert::stdout()
                    .contains("My CLI Application")
                    .contains("serve")
                    .contains("build")
                    .contains("db"),
            ]);

        let result = app.execute(test_case);
        assert!(
            matches!(result.status, TestCaseStatus::Passed),
            "Test failed: {:?}",
            result.status
        );
    }
}