Skip to main content

dynamic_cli/
lib.rs

1//! # dynamic-cli
2//!
3//! A framework for creating configurable CLI and REPL applications via YAML/JSON.
4//!
5//! ## Overview
6//!
7//! **dynamic-cli** allows you to define your application's command-line interface
8//! in a configuration file rather than coding it manually. The framework
9//! automatically generates:
10//! - Argument parsing
11//! - Input validation
12//! - Contextual help
13//! - Interactive mode (REPL)
14//! - Error handling with suggestions
15//!
16//! ## Quick Start
17//!
18//! ```no_run
19//! use dynamic_cli::prelude::*;
20//!
21//! // 1. Define the execution context
22//! #[derive(Default)]
23//! struct MyContext;
24//!
25//! impl ExecutionContext for MyContext {
26//!     fn as_any(&self) -> &dyn std::any::Any { self }
27//!     fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
28//! }
29//!
30//! // 2. Implement a command handler
31//! struct HelloCommand;
32//!
33//! impl CommandHandler for HelloCommand {
34//!     fn execute(
35//!         &self,
36//!         _context: &mut dyn ExecutionContext,
37//!         args: &ParsedArgs,
38//!     ) -> dynamic_cli::Result<()> {
39//!         let name = args.get_scalar("name").unwrap_or("World");
40//!         println!("Hello, {}!", name);
41//!         Ok(())
42//!     }
43//! }
44//!
45//! // 3. Load configuration and register commands
46//! # fn main() -> dynamic_cli::Result<()> {
47//! use dynamic_cli::config::loader::load_config;
48//!
49//! let config = load_config("commands.yaml")?;
50//! let mut registry = CommandRegistry::new();
51//! registry.register_sync(config.commands[0].clone(), Box::new(HelloCommand))?;
52//!
53//! // 4. Parse and execute
54//! let parser = ReplParser::new(&registry);
55//! let parsed = parser.parse_line("hello World")?;
56//!
57//! let mut context = MyContext::default();
58//! let handler = registry.get_handler_sync(&parsed.command_name).unwrap();
59//! let args = ParsedArgs::from_scalars(parsed.arguments);
60//! handler.execute(&mut context, &args)?;
61//! # Ok(())
62//! # }
63//! ```
64//!
65//! ## Architecture
66//!
67//! The framework is organized into modules:
68//!
69//! - [`error`]: Error types and handling
70//! - [`config`]: Configuration file loading and validation
71//! - [`context`]: Execution context trait
72//! - [`executor`]: Command execution
73//! - [`registry`]: Command and handler registry
74//! - [`parser`]: CLI and REPL argument parsing
75//! - [`validator`]: Argument validation
76//!
77//! ## Module Status
78//!
79//! - ✅ Complete: error, config, context, executor, registry, parser, validator, interface, builder
80//! - 📋 Planned: utils, examples
81//!
82//! ## Examples
83//!
84//! See the documentation for each module for detailed examples.
85
86// ============================================================================
87// PUBLIC MODULES (Complete and ready to use)
88// ============================================================================
89
90pub mod builder;
91pub mod config;
92pub mod context;
93pub mod error;
94pub mod executor;
95pub mod help;
96pub mod interface;
97pub mod parser;
98pub mod plugin;
99pub mod registry;
100pub mod utils;
101pub mod validator;
102// ============================================================================
103// PUBLIC RE-EXPORTS (For convenience)
104// ============================================================================
105
106// Core traits
107pub use context::{downcast_mut, downcast_ref, ExecutionContext};
108pub use executor::CommandHandler;
109
110// Error handling
111pub use error::{DynamicCliError, Result};
112
113// Configuration types
114pub use config::schema::{
115    ArgumentDefinition, ArgumentType, CommandDefinition, CommandsConfig, Metadata,
116    OptionDefinition, ValidationRule,
117};
118
119// Registry
120pub use registry::CommandRegistry;
121
122// Parser types
123pub use parser::{CliParser, ParsedArgs, ParsedCommand, ReplParser};
124
125// Validator functions
126pub use validator::{validate_file_exists, validate_file_extension, validate_range};
127
128// Interface types
129pub use interface::{CliInterface, ReplInterface, ScriptErrorPolicy, ScriptOutcome};
130
131// Builder types
132pub use builder::{CliApp, CliBuilder};
133
134// Helper system
135pub use help::{DefaultHelpFormatter, HelpFormatter};
136
137// Plugin system
138#[cfg(feature = "wasm-plugins")]
139pub use plugin::wasm::{WasmPlugin, WasmSerializationFormat};
140#[cfg(feature = "config-plugin")]
141pub use plugin::ConfigPlugin;
142#[cfg(feature = "env-plugin")]
143pub use plugin::EnvPlugin;
144#[cfg(feature = "sysinfo-plugin")]
145pub use plugin::SysInfoPlugin;
146pub use plugin::{ExitPlugin, HelpPlugin, Plugin, SystemPlugin, VersionPlugin};
147
148// Utility functions
149pub use utils::{
150    detect_type, format_bytes, format_duration, get_extension, has_extension, is_blank, normalize,
151    normalize_path, parse_bool, parse_float, parse_int, truncate,
152};
153
154// ============================================================================
155// PRELUDE MODULE (Quick imports)
156// ============================================================================
157
158/// Prelude module for quickly importing essential types
159///
160/// This module re-exports the most commonly used types and traits,
161/// allowing you to import everything with a single `use` statement.
162///
163/// # Example
164///
165/// ```
166/// use dynamic_cli::prelude::*;
167///
168/// // Now you have access to:
169/// // - ExecutionContext, downcast_ref, downcast_mut
170/// // - CommandHandler
171/// // - DynamicCliError, Result
172/// // - CommandRegistry
173/// // - ParsedCommand, CliParser, ReplParser
174/// // - validate_file_exists, validate_file_extension, validate_range
175/// // - Common config types (ArgumentType, CommandsConfig)
176/// // - CliBuilder, CliApp
177/// // - Utility functions (parse_int, parse_bool, is_blank, etc.)
178/// ```
179pub mod prelude {
180    // Context management
181    pub use crate::context::{downcast_mut, downcast_ref, ExecutionContext};
182
183    // Command handling
184    pub use crate::executor::CommandHandler;
185
186    // Error handling
187    pub use crate::error::{DynamicCliError, Result};
188
189    // Configuration
190    pub use crate::config::schema::{ArgumentType, CommandsConfig};
191
192    // Registry
193    pub use crate::registry::CommandRegistry;
194
195    // Parsing
196    pub use crate::parser::{CliParser, ParsedArgs, ParsedCommand, ReplParser};
197
198    // Validation
199    pub use crate::validator::{validate_file_exists, validate_file_extension, validate_range};
200
201    // Interface
202    pub use crate::interface::{CliInterface, ReplInterface, ScriptErrorPolicy, ScriptOutcome};
203
204    // Builder
205    pub use crate::builder::{CliApp, CliBuilder};
206
207    // Help system — re-exported so framework users need only `use dynamic_cli::prelude::*`
208    pub use crate::help::{DefaultHelpFormatter, HelpFormatter};
209
210    // Plugin system
211    #[cfg(feature = "wasm-plugins")]
212    pub use crate::plugin::wasm::{WasmPlugin, WasmSerializationFormat};
213    #[cfg(feature = "config-plugin")]
214    pub use crate::plugin::ConfigPlugin;
215    #[cfg(feature = "env-plugin")]
216    pub use crate::plugin::EnvPlugin;
217    #[cfg(feature = "sysinfo-plugin")]
218    pub use crate::plugin::SysInfoPlugin;
219    pub use crate::plugin::{ExitPlugin, HelpPlugin, Plugin, SystemPlugin, VersionPlugin};
220
221    // Utilities (most commonly used)
222    pub use crate::utils::{detect_type, is_blank, normalize, parse_bool, parse_float, parse_int};
223}
224
225// ============================================================================
226// INTEGRATION TESTS
227// ============================================================================
228
229#[cfg(test)]
230mod tests {
231    use super::*;
232
233    /// Verify that prelude imports work correctly
234    #[test]
235    fn test_prelude_imports() {
236        use crate::prelude::*;
237
238        // If this compiles, prelude imports are working
239        let _: Option<&dyn ExecutionContext> = None;
240        let _: Option<&dyn CommandHandler> = None;
241    }
242
243    /// Verify that individual module imports work
244    #[test]
245    fn test_module_imports() {
246        use crate::config::schema::CommandsConfig;
247        use crate::parser::ParsedCommand;
248        use crate::registry::CommandRegistry;
249
250        // If this compiles, module structure is correct
251        let _config = CommandsConfig::minimal();
252        let _registry = CommandRegistry::new();
253        let _parsed = ParsedCommand {
254            command_name: "test".to_string(),
255            arguments: std::collections::HashMap::new(),
256        };
257    }
258
259    /// Verify that re-exports work
260    #[test]
261    fn test_reexports() {
262        // These should be accessible from the crate root
263        let _: Option<&dyn ExecutionContext> = None;
264        let _: Option<&dyn CommandHandler> = None;
265        let _registry = CommandRegistry::new();
266
267        // If this compiles, re-exports are working
268    }
269
270    /// Verify that help types are accessible from the prelude
271    #[test]
272    fn test_help_prelude_imports() {
273        use crate::config::schema::{CommandsConfig, Metadata};
274        use crate::prelude::*;
275
276        let config = CommandsConfig {
277            metadata: Metadata {
278                version: "1.0.0".to_string(),
279                prompt: "test".to_string(),
280                prompt_suffix: " > ".to_string(),
281            },
282            commands: vec![],
283            global_options: vec![],
284        };
285
286        // DefaultHelpFormatter accessible from prelude
287        let f = DefaultHelpFormatter::new();
288        let _ = f.format_app(&config);
289
290        // Trait object usable (object-safe by design)
291        let _: Box<dyn HelpFormatter> = Box::new(DefaultHelpFormatter::new());
292    }
293}