standout_dispatch/lib.rs
1//! Command dispatch and orchestration for clap-based CLIs.
2//!
3//! `standout-dispatch` provides command routing, handler execution, and a hook
4//! system for CLI applications. It orchestrates the execution flow while remaining
5//! agnostic to rendering implementation.
6//!
7//! # Architecture
8//!
9//! Dispatch is an orchestration layer that manages this execution flow:
10//!
11//! ```text
12//! parsed CLI args
13//! → pre-dispatch hook (validation, setup)
14//! → logic handler (business logic → serializable data)
15//! → post-dispatch hook (data transformation)
16//! → render handler (view + data → string output)
17//! → post-output hook (output transformation)
18//! ```
19//!
20//! ## Design Rationale
21//!
22//! Dispatch deliberately does not own rendering or output format logic:
23//!
24//! - Logic handlers have a strict input signature (`&ArgMatches`, `&CommandContext`)
25//! and return serializable data. They focus purely on business logic.
26//!
27//! - Render handlers are pluggable callbacks provided by the consuming framework.
28//! They receive (view name, data) and return a formatted string. All rendering
29//! decisions (format, theme, template engine) live in the render handler.
30//!
31//! This separation allows:
32//! - Using dispatch without any rendering (just return data)
33//! - Using dispatch with custom renderers (not just standout-render)
34//! - Keeping format/theme/template logic out of the dispatch layer
35//!
36//! ## Render Handler Pattern
37//!
38//! The render handler is a closure that captures rendering context:
39//!
40//! ```rust,ignore
41//! // Framework (e.g., standout) creates the render handler at runtime
42//! // after parsing CLI args to determine format
43//! let format = extract_output_mode(&matches); // --output=json
44//! let theme = &config.theme;
45//!
46//! let render_handler = move |view: &str, data: &Value| {
47//! // All format/theme knowledge lives here, not in dispatch
48//! my_renderer::render(view, data, theme, format)
49//! };
50//!
51//! dispatcher.run_with_renderer(matches, render_handler);
52//! ```
53//!
54//! This pattern means dispatch calls `render_handler(view, data)` without knowing
55//! what format, theme, or template engine is being used.
56//!
57//! # Features
58//!
59//! - Command routing: Extract command paths from clap `ArgMatches`
60//! - Handler traits: Thread-safe ([`Handler`]) and local ([`LocalHandler`]) variants
61//! - Hook system: Pre/post dispatch and post-output hooks for cross-cutting concerns
62//! - Render abstraction: Pluggable render handlers via [`RenderFn`] / [`LocalRenderFn`]
63//!
64//! # Usage
65//!
66//! ## Standalone (no rendering framework)
67//!
68//! ```rust,ignore
69//! use standout_dispatch::{Handler, Output, from_fn};
70//!
71//! // Simple render handler that just serializes to JSON
72//! let render = from_fn(|data, _| Ok(serde_json::to_string_pretty(data)?));
73//!
74//! Dispatcher::builder()
75//! .command("list", list_handler, render)
76//! .build()?
77//! .run(cmd, args);
78//! ```
79//!
80//! ## With standout framework
81//!
82//! The `standout` crate provides full integration with templates and themes:
83//!
84//! ```rust,ignore
85//! use standout::{App, embed_templates};
86//!
87//! App::builder()
88//! .templates(embed_templates!("src/templates"))
89//! .command("list", list_handler, "list") // template name
90//! .build()?
91//! .run(cmd, args);
92//! ```
93//!
94//! In this case, `standout` creates the render handler internally, injecting
95//! the template registry, theme, and output format from CLI args.
96
97// Core modules
98mod dispatch;
99mod handler;
100mod hooks;
101mod render;
102
103// Re-export command routing utilities
104pub use dispatch::{
105 extract_command_path, get_deepest_matches, has_subcommand, insert_default_command,
106 path_to_string, string_to_path,
107};
108
109// Re-export handler types
110pub use handler::{
111 CommandContext, FnHandler, Handler, HandlerResult, LocalFnHandler, LocalHandler, Output,
112 RunResult,
113};
114
115// Re-export hook types
116pub use hooks::{
117 HookError, HookPhase, Hooks, PostDispatchFn, PostOutputFn, PreDispatchFn, RenderedOutput,
118};
119
120// Re-export render abstraction
121pub use render::{from_fn, from_fn_mut, LocalRenderFn, RenderError, RenderFn};