Skip to main content

CliBuilder

Struct CliBuilder 

Source
pub struct CliBuilder { /* private fields */ }
Expand description

Fluent builder for creating CLI/REPL applications

Provides a chainable API for configuring and building applications. Automatically loads configuration, registers handlers, and creates the appropriate interface (CLI or REPL).

§Builder Pattern

The builder follows the standard Rust builder pattern:

  • Methods consume self and return Self
  • Final build() method consumes the builder and returns the app

§Example

use dynamic_cli::prelude::*;

let app = CliBuilder::new()
    .config_file("commands.yaml")
    .context(Box::new(MyContext::default()))
    .register_sync_handler("my_handler", Box::new(MyHandler))
    .prompt("myapp")
    .build()?;

Implementations§

Source§

impl CliBuilder

Source

pub fn new() -> Self

Create a new builder

§Example
use dynamic_cli::CliBuilder;

let builder = CliBuilder::new();
Source

pub fn config_file<P: Into<PathBuf>>(self, path: P) -> Self

Specify the configuration file

The file will be loaded during build(). Supports YAML and JSON formats.

§Arguments
  • path - Path to the configuration file (.yaml, .yml, or .json)
§Example
use dynamic_cli::CliBuilder;

let builder = CliBuilder::new()
    .config_file("commands.yaml");
Source

pub fn config(self, config: CommandsConfig) -> Self

Provide a pre-loaded configuration

Use this instead of config_file() if you want to load and potentially modify the configuration before building.

§Arguments
  • config - Loaded and validated configuration
§Example
use dynamic_cli::{CliBuilder, config::loader::load_config};

let mut config = load_config("commands.yaml")?;
// Modify config if needed...

let builder = CliBuilder::new()
    .config(config);
Source

pub fn context(self, context: Box<dyn ExecutionContext>) -> Self

Set the execution context

The context will be passed to all command handlers and can store application state.

§Arguments
  • context - Boxed execution context implementing ExecutionContext
§Example
use dynamic_cli::prelude::*;

#[derive(Default)]
struct MyContext {
    count: u32,
}

impl ExecutionContext for MyContext {
    fn as_any(&self) -> &dyn std::any::Any { self }
    fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
}

let builder = CliBuilder::new()
    .context(Box::new(MyContext::default()));
Source

pub fn register_sync_handler( self, name: impl Into<String>, handler: Box<dyn CommandHandler>, ) -> Self

Register a (sync) command handler

Associates a handler with the command’s implementation name from the config. The name must match the implementation field in the command definition.

Renamed from register_handler() in v0.5.0 for symmetry with register_async_handler. register_handler() remains available as a deprecated alias until v1.0.0 (DD-022).

§Arguments
  • name - Implementation name from the configuration
  • handler - Boxed command handler implementing CommandHandler
§Example
use dynamic_cli::prelude::*;
use std::collections::HashMap;

struct MyCommand;

impl CommandHandler for MyCommand {
    fn execute(
        &self,
        _ctx: &mut dyn ExecutionContext,
        _args: &HashMap<String, String>,
    ) -> dynamic_cli::Result<()> {
        println!("Executed!");
        Ok(())
    }
}

let builder = CliBuilder::new()
    .register_sync_handler("my_command", Box::new(MyCommand));
Source

pub fn register_handler( self, name: impl Into<String>, handler: Box<dyn CommandHandler>, ) -> Self

👎Deprecated since 0.5.0:

renamed to register_sync_handler for symmetry with register_async_handler; will be removed in 1.0.0

Deprecated alias for register_sync_handler. Scheduled for removal in v1.0.0 alongside CommandRegistry::register.

Source

pub fn register_async_handler( self, name: impl Into<String>, handler: Box<dyn AsyncCommandHandler>, ) -> Self

Register an async command handler (DD-022)

Additive counterpart of register_sync_handler. Associates an async handler with the command’s implementation name from the config, exactly like its sync counterpart. Registering both a sync and an async handler for the same implementation name is detected as an error at build() time, not here — this method, like register_sync_handler, always succeeds (it just stores the handler for build() to consume).

§Arguments
  • name - Implementation name from the configuration
  • handler - Boxed async command handler implementing AsyncCommandHandler
§Example
use dynamic_cli::prelude::*;
use dynamic_cli::executor::AsyncCommandHandler;
use std::collections::HashMap;
use async_trait::async_trait;

struct FetchCommand;

#[async_trait]
impl AsyncCommandHandler for FetchCommand {
    async fn execute(
        &self,
        _ctx: &mut dyn ExecutionContext,
        _args: &HashMap<String, String>,
    ) -> dynamic_cli::Result<()> {
        println!("Fetched!");
        Ok(())
    }
}

let builder = CliBuilder::new()
    .register_async_handler("fetch_command", Box::new(FetchCommand));
Source

pub fn register_plugin(self, plugin: Box<dyn Plugin>) -> Self

Register a plugin

A plugin groups related handlers under a single unit. During build(), each handler declared by the plugin is merged into the handler map. Conflicts with already-registered handler names (whether from another plugin or from register_sync_handler) produce a build-time error.

The YAML config remains the sole source of truth for command definitions. Plugin handlers are matched by their implementation name, exactly as with register_sync_handler. Plugins remain sync-only for now — out of scope for DD-022.

§Example
use dynamic_cli::CliBuilder;
use dynamic_cli::plugin::SystemPlugin;

let builder = CliBuilder::new()
    .register_plugin(Box::new(SystemPlugin::new()));
Source

pub fn register_wasm_plugin( self, path: &Path, function_map: &[(&str, &str)], ) -> Result<Self>

Load a WASM plugin from disk, map its business functions, and register it

Convenience wrapper around WasmPlugin::load, WasmPlugin::with_function_map, and register_plugin. Only available with the wasm-plugins feature.

function_map is mandatory here, unlike the other WasmPlugin builder methods (with_format, with_metadata), which have reasonable defaults (YAML, file-name-derived metadata). A WasmPlugin with an empty function map registers zero handlers — silently inert — so this wrapper does not offer a path that skips mapping. Applications that also need a non-default format or explicit metadata should build the WasmPlugin directly and pass it to register_plugin instead.

§Errors

Returns an error if the module cannot be loaded or fails mandatory export validation. See WASM_PLUGIN_INTERFACE.md for the ABI contract.

§Example
use dynamic_cli::CliBuilder;
use std::path::Path;

let builder = CliBuilder::new()
    .register_wasm_plugin(
        Path::new("plugins/greet.wasm"),
        &[("greet_hello", "say_hello")],
    )?;
Source

pub fn prompt(self, prompt: impl Into<String>) -> Self

Set the REPL prompt

Only used in REPL mode. If not specified, uses the prompt from the configuration or defaults to “cli”.

§Arguments
  • prompt - Prompt prefix (e.g., “myapp” displays as “myapp > “)
§Example
use dynamic_cli::CliBuilder;

let builder = CliBuilder::new()
    .prompt("myapp");
Source

pub fn help_formatter(self, formatter: Box<dyn HelpFormatter>) -> Self

Set a custom help formatter.

By default, DefaultHelpFormatter is used lazily when --help is detected. Call this method to supply your own implementation.

The formatter is stored and transferred to CliApp during build(). It is instantiated only when --help is detected in run_cli().

§Arguments
§Example
use dynamic_cli::CliBuilder;
use dynamic_cli::help::{HelpFormatter, DefaultHelpFormatter};
use dynamic_cli::config::schema::CommandsConfig;

struct MyFormatter;

impl HelpFormatter for MyFormatter {
    fn format_app(&self, config: &CommandsConfig) -> String {
        format!("Help for {}", config.metadata.prompt)
    }
    fn format_command(&self, config: &CommandsConfig, command: &str) -> String {
        format!("Help for command '{command}'")
    }
}

let builder = CliBuilder::new()
    .help_formatter(Box::new(MyFormatter));
Source

pub fn build(self) -> Result<CliApp>

Build the application

Performs the following steps:

  1. Load configuration (if config_file() was used)
  2. Validate that a context was provided
  3. Create the command registry
  4. Register all command handlers
  5. Verify that all required commands have handlers
  6. Create the CliApp
§Returns

A configured CliApp ready to run

§Errors
  • Configuration errors (file not found, invalid format, etc.)
  • Missing context
  • Missing required handlers
  • Registry errors
§Example
use dynamic_cli::prelude::*;

let app = CliBuilder::new()
    .config_file("commands.yaml")
    .context(Box::new(MyContext::default()))
    .register_sync_handler("handler", Box::new(MyHandler))
    .build()?;

// Now app is ready to run

Trait Implementations§

Source§

impl Default for CliBuilder

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.