cmdkit 0.1.1

Core library for CLI tools, providing common functionality and utilities for building command-line applications.
Documentation
use std::sync::{Arc, Mutex};

use cmdkit::{CliCore, Command, command};

#[test]
fn strategy_chain_handles_subtask_tokens() {
    let captured = Arc::new(Mutex::new(Vec::new()));
    let captured_for_strategy = Arc::clone(&captured);

    let core = CliCore::new();
    core.register(Command::from_fn(
        "parent",
        "Parent command",
        move |options, arguments, subcommands| {
            let mut guard = captured_for_strategy
                .lock()
                .expect("capture lock should not be poisoned");
            guard.push(vec![
                format!("options={options:?}"),
                format!("arguments={arguments:?}"),
                format!("subcommands={subcommands:?}"),
            ]);
            Ok(())
        },
    ));

    let args = vec![
        "app".to_string(),
        "parent".to_string(),
        "--opt".to_string(),
        "--flag".to_string(),
        "value".to_string(),
    ];
    assert!(core.try_run_from_args(&args).is_ok());

    let guard = captured
        .lock()
        .expect("capture lock should not be poisoned");
    assert_eq!(guard.len(), 1);
    assert!(guard[0][0].contains("options=[\"opt\"]"));
    assert!(guard[0][1].contains("flag"));
    assert!(guard[0][1].contains("value"));
    assert!(guard[0][2].contains("[]"));
}

#[test]
fn command_builder_registers_leaf_command_without_exposing_strategy_types() {
    let captured = Arc::new(Mutex::new(Vec::new()));
    let captured_for_handler = Arc::clone(&captured);

    let core = CliCore::new();
    core.register(
        command("echo", "Echo command")
            .handler_fn(move |options, arguments, subcommands| {
                captured_for_handler
                    .lock()
                    .expect("capture lock should not be poisoned")
                    .push(vec![
                        format!("options={options:?}"),
                        format!("arguments={arguments:?}"),
                        format!("subcommands={subcommands:?}"),
                    ]);
                Ok(())
            })
            .build(),
    );

    let args = vec![
        "app".to_string(),
        "echo".to_string(),
        "--message".to_string(),
        "hello".to_string(),
    ];

    assert!(core.try_run_from_args(&args).is_ok());
    let guard = captured
        .lock()
        .expect("capture lock should not be poisoned");
    assert!(guard[0][0].contains("options=[]"));
    assert!(guard[0][1].contains("message"));
    assert!(guard[0][1].contains("hello"));
    assert!(guard[0][2].contains("[]"));
}

#[test]
fn command_builder_registers_recursive_subcommands_without_router_exposure() {
    let captured = Arc::new(Mutex::new(Vec::new()));
    let captured_for_handler = Arc::clone(&captured);

    let core = CliCore::new();
    core.register(
        command("tool", "tool root")
            .subcommand(command("run", "run tasks").subcommand(
                command("one", "run one task").handler_fn(
                    move |options, arguments, subcommands| {
                        captured_for_handler
                            .lock()
                            .expect("capture lock should not be poisoned")
                            .push(vec![
                                format!("options={options:?}"),
                                format!("arguments={arguments:?}"),
                                format!("subcommands={subcommands:?}"),
                            ]);
                        Ok(())
                    },
                ),
            ))
            .build(),
    );

    let args = vec![
        "app".to_string(),
        "tool".to_string(),
        "run".to_string(),
        "one".to_string(),
        "--value".to_string(),
        "alpha".to_string(),
    ];

    assert!(core.try_run_from_args(&args).is_ok());
    let guard = captured
        .lock()
        .expect("capture lock should not be poisoned");
    assert!(guard[0][0].contains("options=[]"));
    assert!(guard[0][1].contains("value"));
    assert!(guard[0][1].contains("alpha"));
    assert!(guard[0][2].contains("[]"));
}