Skip to main content

Module interface

Module interface 

Source
Expand description

User interface module

This module provides two main interfaces for interacting with the CLI framework:

  • CliInterface: One-shot command execution from command-line arguments
  • ReplInterface: Interactive REPL (Read-Eval-Print Loop) with history

§Overview

The interface module is the user-facing layer of the framework. It handles:

  • Parsing user input (CLI args or REPL lines)
  • Executing commands through the registry
  • Displaying results and errors
  • Managing command history (REPL only)

§Choosing an Interface

§CLI Interface

Use CliInterface when:

  • Running single commands from scripts
  • Building traditional CLI tools
  • No interaction is needed
  • Each invocation is independent
use dynamic_cli::interface::CliInterface;
use dynamic_cli::prelude::*;

let registry = CommandRegistry::new();
let context = Box::new(MyContext::default());

let cli = CliInterface::new(registry, context);
cli.run(std::env::args().skip(1).collect())?;

§REPL Interface

Use ReplInterface when:

  • Building interactive tools
  • Users need to run multiple commands
  • Context/state is preserved between commands
  • Command history and line editing are desired
use dynamic_cli::interface::ReplInterface;
use dynamic_cli::prelude::*;

let registry = CommandRegistry::new();
let context = Box::new(MyContext::default());

let repl = ReplInterface::new(registry, context, "myapp".to_string(), None, None)?;
repl.run()?; // Enters interactive loop

§Architecture

Both interfaces follow the same flow:

User Input → Parser → Validator → Executor → Handler
                                       ↓
                                 ExecutionContext

Key differences:

AspectCLIREPL
InputCommand-line argsInteractive lines
ParserCliParserReplParser
HistoryNoneWritten to disk by :quit and Ctrl-D
ErrorsExit processDisplay and continue
LifecycleOne command, exitLoop until :quit, :exit or Ctrl-D

§Error Handling

§CLI Interface

Errors cause the process to exit with specific codes:

  • 0: Success
  • 1: Execution error
  • 2: Parse/validation error
  • 3: Other errors

§REPL Interface

Errors are displayed but the REPL continues:

  • Parse errors → show suggestions, continue
  • Validation errors → explain issue, continue
  • Execution errors → display error, continue
  • Directive errors → list the available directives (unknown directive) or show the expected usage (wrong arguments), continue
  • Input failure (the terminal cannot be read) → reported, the REPL exits without writing the history

§REPL Directives

In the REPL, a line starting with : is a framework directive; any other line is an application command. The two never compete for a name: :quit is always the directive, quit is always looked up among the application’s commands.

DirectiveAliasesAction
:help [command]:h, :?Application help, or the help of command
:load <path>Run every line of a script file
:quit:qWrite the session history, then leave
:exitLeave without writing the session history

These are the default names. An application can rename a directive, replace its aliases or its description through the directives: section of its configuration, for instance to translate them; it can neither add nor disable one. See crate::config::directive.

Leaving the REPL:

  • :quit and end of input (Ctrl-D) write the session history, then leave.
  • :exit leaves without writing it. Any other termination (a handler calling std::process::exit, a panic, the process being killed) also leaves the history file as it was when the session started.
  • exit or quit typed without : is an ordinary command name. When the application has no command of that name, the error suggests the matching directive (:quit).

Inside a :load script, :quit and :exit are reported and skipped; the other lines run.

:help lists the directives after the application help, under their effective names (see HelpFormatter::format_directives). Tab completion after a leading : offers the directive names and aliases. --help and -h work in the REPL as in CLI mode.

§Examples

§Complete CLI Application

use dynamic_cli::prelude::*;
use dynamic_cli::config::loader::load_config;
use dynamic_cli::interface::CliInterface;

// Define context
#[derive(Default)]
struct AppContext {
    data: Vec<String>,
}

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

// Define handler
struct AddCommand;

impl CommandHandler for AddCommand {
    fn execute(
        &self,
        context: &mut dyn ExecutionContext,
        args: &ParsedArgs,
    ) -> dynamic_cli::Result<()> {
        let ctx = dynamic_cli::context::downcast_mut::<AppContext>(context).unwrap();
        let item = args.get_scalar("item").unwrap();
        ctx.data.push(item.to_string());
        println!("Added: {}", item);
        Ok(())
    }
}

fn main() -> dynamic_cli::Result<()> {
    // Load configuration
    let config = load_config("commands.yaml")?;
     
    // Build registry
    let mut registry = CommandRegistry::new();
    registry.register_sync(
        config.commands[0].clone(),
        Box::new(AddCommand),
    )?;
     
    // Create and run CLI
    let context = Box::new(AppContext::default());
    let cli = CliInterface::new(registry, context);
    cli.run(std::env::args().skip(1).collect())
}

§Complete REPL Application

use dynamic_cli::prelude::*;
use dynamic_cli::config::loader::load_config;
use dynamic_cli::interface::ReplInterface;

// Same context and handler as above

fn main() -> dynamic_cli::Result<()> {
    // Load configuration
    let config = load_config("commands.yaml")?;
     
    // Build registry
    let mut registry = CommandRegistry::new();
    registry.register_sync(
        config.commands[0].clone(),
        Box::new(AddCommand),
    )?;
     
    // Create and run REPL
    let context = Box::new(AppContext::default());
    let repl = ReplInterface::new(registry, context, "myapp".to_string(), None, None)?;
    repl.run() // Interactive loop
}

§Module Structure

  • cli: CLI interface implementation
  • repl: REPL interface implementation

Re-exports§

pub use cli::CliInterface;
pub use cli::ScriptErrorPolicy;
pub use cli::ScriptOutcome;
pub use repl::ReplInterface;

Modules§

cli
CLI (Command-Line Interface) implementation
repl
REPL (Read-Eval-Print Loop) implementation