Skip to main content

Crate standout_dispatch

Crate standout_dispatch 

Source
Expand description

Command dispatch and orchestration for clap-based CLIs.

standout-dispatch provides command routing, handler execution, and a hook system for CLI applications. It orchestrates the execution flow while remaining agnostic to rendering implementation.

§Architecture

Dispatch is an orchestration layer that manages this execution flow:

parsed CLI args
  → pre-dispatch hook (validation, setup)
  → logic handler (business logic → serializable data)
  → post-dispatch hook (data transformation)
  → render handler (view + data → string output)
  → post-output hook (output transformation)

§Design Rationale

Dispatch deliberately does not own rendering or output format logic:

  • Logic handlers have a strict input signature (&ArgMatches, &CommandContext) and return serializable data. They focus purely on business logic.

  • Render handlers are pluggable callbacks provided by the consuming framework. They receive (view name, data) and return a formatted string. All rendering decisions (format, theme, template engine) live in the render handler.

This separation allows:

  • Using dispatch without any rendering (just return data)
  • Using dispatch with custom renderers (not just standout-render)
  • Keeping format/theme/template logic out of the dispatch layer

§Render Handler Pattern

The render handler is a closure that captures rendering context:

// Framework (e.g., standout) creates the render handler at runtime
// after parsing CLI args to determine format
let format = extract_output_mode(&matches);  // --output=json
let theme = &config.theme;

let render_handler = move |view: &str, data: &Value| {
    // All format/theme knowledge lives here, not in dispatch
    my_renderer::render(view, data, theme, format)
};

dispatcher.run_with_renderer(matches, render_handler);

This pattern means dispatch calls render_handler(view, data) without knowing what format, theme, or template engine is being used.

§State Management

CommandContext provides two mechanisms for dependency injection:

  • app_state: Immutable, app-lifetime state (database, config, API clients). Configured at app build time, shared across all dispatches via Arc<Extensions>.

  • extensions: Mutable, per-request state. Injected by pre-dispatch hooks for request-scoped data like user sessions or request IDs.

// App-level state (build time)
App::builder()
    .app_state(Database::connect()?)
    .app_state(Config::load()?)

// In handler
fn handler(matches: &ArgMatches, ctx: &CommandContext) -> HandlerResult<T> {
    let db = ctx.app_state.get_required::<Database>()?;   // shared
    let scope = ctx.extensions.get_required::<UserScope>()?; // per-request
    // ...
}

§Features

  • Command routing: Extract command paths from clap ArgMatches
  • Handler traits: Thread-safe (Handler) and local (LocalHandler) variants
  • Hook system: Pre/post dispatch and post-output hooks for cross-cutting concerns
  • State injection: App-level state via app_state, per-request state via extensions
  • Render abstraction: Pluggable render handlers via RenderFn / LocalRenderFn

§Usage

§Standalone (no rendering framework)

use standout_dispatch::{Handler, Output, from_fn};

// Simple render handler that just serializes to JSON
let render = from_fn(|data, _| Ok(serde_json::to_string_pretty(data)?));

Dispatcher::builder()
    .command("list", list_handler, render)
    .build()?
    .run(cmd, args);

§With standout framework

The standout crate provides full integration with templates and themes:

use standout::{App, embed_templates};

App::builder()
    .templates(embed_templates!("src/templates"))
    .command("list", list_handler, "list")  // template name
    .build()?
    .run(cmd, args);

In this case, standout creates the render handler internally, injecting the template registry, theme, and output format from CLI args.

Structs§

CommandContext
Context passed to command handlers.
Extensions
Type-safe container for injecting custom state into handlers.
FnHandler
A wrapper that implements Handler for closures.
HookError
Error returned by a hook.
Hooks
Per-command hook configuration.
LocalFnHandler
A wrapper that implements LocalHandler for FnMut closures.

Enums§

HookPhase
The phase at which a hook error occurred.
Output
What a handler produces.
RenderError
Errors that can occur during rendering.
RenderedOutput
Output from a command, used in post-output hooks.
RunResult
Result of running the CLI dispatcher.

Traits§

Handler
Trait for thread-safe command handlers.
LocalHandler
Trait for local (single-threaded) command handlers.

Functions§

extract_command_path
Extracts the command path from ArgMatches by following the subcommand chain.
from_fn
Creates a render function from a closure.
from_fn_mut
Creates a local render function from a FnMut closure.
get_deepest_matches
Gets the deepest subcommand matches.
has_subcommand
Returns true if the matches contain a subcommand (excluding “help”).
insert_default_command
Inserts a command name at position 1 (after program name) in the argument list.
path_to_string
Converts a command path vector to a dot-separated string.
string_to_path
Parses a dot-separated command path string into a vector.

Type Aliases§

HandlerResult
The result type for command handlers.
LocalRenderFn
A local (non-Send) render function for single-threaded use.
PostDispatchFn
Type alias for post-dispatch hook functions.
PostOutputFn
Type alias for post-output hook functions.
PreDispatchFn
Type alias for pre-dispatch hook functions.
RenderFn
The render function signature.