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//! use std::collections::HashMap;
21//!
22//! // 1. Define the execution context
23//! #[derive(Default)]
24//! struct MyContext;
25//!
26//! impl ExecutionContext for MyContext {
27//!     fn as_any(&self) -> &dyn std::any::Any { self }
28//!     fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
29//! }
30//!
31//! // 2. Implement a command handler
32//! struct HelloCommand;
33//!
34//! impl CommandHandler for HelloCommand {
35//!     fn execute(
36//!         &self,
37//!         _context: &mut dyn ExecutionContext,
38//!         args: &HashMap<String, String>,
39//!     ) -> dynamic_cli::Result<()> {
40//!         let default_name = "World".to_string();
41//!         let name = args.get("name").unwrap_or(&default_name);
42//!         println!("Hello, {}!", name);
43//!         Ok(())
44//!     }
45//! }
46//!
47//! // 3. Load configuration and register commands
48//! # fn main() -> dynamic_cli::Result<()> {
49//! use dynamic_cli::config::loader::load_config;
50//!
51//! let config = load_config("commands.yaml")?;
52//! let mut registry = CommandRegistry::new();
53//! registry.register(config.commands[0].clone(), Box::new(HelloCommand))?;
54//!
55//! // 4. Parse and execute
56//! let parser = ReplParser::new(&registry);
57//! let parsed = parser.parse_line("hello World")?;
58//!
59//! let mut context = MyContext::default();
60//! let handler = registry.get_handler(&parsed.command_name).unwrap();
61//! handler.execute(&mut context, &parsed.arguments)?;
62//! # Ok(())
63//! # }
64//! ```
65//!
66//! ## Architecture
67//!
68//! The framework is organized into modules:
69//!
70//! - [`error`]: Error types and handling
71//! - [`config`]: Configuration file loading and validation
72//! - [`context`]: Execution context trait
73//! - [`executor`]: Command execution
74//! - [`registry`]: Command and handler registry
75//! - [`parser`]: CLI and REPL argument parsing
76//! - [`validator`]: Argument validation
77//!
78//! ## Module Status
79//!
80//! - ✅ Complete: error, config, context, executor, registry, parser, validator, interface, builder
81//! - 📋 Planned: utils, examples
82//!
83//! ## Examples
84//!
85//! See the documentation for each module for detailed examples.
86
87// ============================================================================
88// PUBLIC MODULES (Complete and ready to use)
89// ============================================================================
90
91pub mod builder;
92pub mod config;
93pub mod context;
94pub mod error;
95pub mod executor;
96pub mod help;
97pub mod interface;
98pub mod parser;
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, 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};
130
131// Builder types
132pub use builder::{CliApp, CliBuilder};
133
134// Helper system
135pub use help::{DefaultHelpFormatter, HelpFormatter};
136
137// Utility functions
138pub use utils::{
139    detect_type, format_bytes, format_duration, get_extension, has_extension, is_blank, normalize,
140    normalize_path, parse_bool, parse_float, parse_int, truncate,
141};
142
143// ============================================================================
144// PRELUDE MODULE (Quick imports)
145// ============================================================================
146
147/// Prelude module for quickly importing essential types
148///
149/// This module re-exports the most commonly used types and traits,
150/// allowing you to import everything with a single `use` statement.
151///
152/// # Example
153///
154/// ```
155/// use dynamic_cli::prelude::*;
156///
157/// // Now you have access to:
158/// // - ExecutionContext, downcast_ref, downcast_mut
159/// // - CommandHandler
160/// // - DynamicCliError, Result
161/// // - CommandRegistry
162/// // - ParsedCommand, CliParser, ReplParser
163/// // - validate_file_exists, validate_file_extension, validate_range
164/// // - Common config types (ArgumentType, CommandsConfig)
165/// // - CliBuilder, CliApp
166/// // - Utility functions (parse_int, parse_bool, is_blank, etc.)
167/// ```
168pub mod prelude {
169    // Context management
170    pub use crate::context::{downcast_mut, downcast_ref, ExecutionContext};
171
172    // Command handling
173    pub use crate::executor::CommandHandler;
174
175    // Error handling
176    pub use crate::error::{DynamicCliError, Result};
177
178    // Configuration
179    pub use crate::config::schema::{ArgumentType, CommandsConfig};
180
181    // Registry
182    pub use crate::registry::CommandRegistry;
183
184    // Parsing
185    pub use crate::parser::{CliParser, ParsedCommand, ReplParser};
186
187    // Validation
188    pub use crate::validator::{validate_file_exists, validate_file_extension, validate_range};
189
190    // Interface
191    pub use crate::interface::{CliInterface, ReplInterface};
192
193    // Builder
194    pub use crate::builder::{CliApp, CliBuilder};
195
196    // Help system — re-exported so framework users need only `use dynamic_cli::prelude::*`
197    pub use crate::help::{DefaultHelpFormatter, HelpFormatter};
198
199    // Utilities (most commonly used)
200    pub use crate::utils::{detect_type, is_blank, normalize, parse_bool, parse_float, parse_int};
201}
202
203// ============================================================================
204// INTEGRATION TESTS
205// ============================================================================
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    /// Verify that prelude imports work correctly
212    #[test]
213    fn test_prelude_imports() {
214        use crate::prelude::*;
215
216        // If this compiles, prelude imports are working
217        let _: Option<&dyn ExecutionContext> = None;
218        let _: Option<&dyn CommandHandler> = None;
219    }
220
221    /// Verify that individual module imports work
222    #[test]
223    fn test_module_imports() {
224        use crate::config::schema::CommandsConfig;
225        use crate::parser::ParsedCommand;
226        use crate::registry::CommandRegistry;
227
228        // If this compiles, module structure is correct
229        let _config = CommandsConfig::minimal();
230        let _registry = CommandRegistry::new();
231        let _parsed = ParsedCommand {
232            command_name: "test".to_string(),
233            arguments: std::collections::HashMap::new(),
234        };
235    }
236
237    /// Verify that re-exports work
238    #[test]
239    fn test_reexports() {
240        // These should be accessible from the crate root
241        let _: Option<&dyn ExecutionContext> = None;
242        let _: Option<&dyn CommandHandler> = None;
243        let _registry = CommandRegistry::new();
244
245        // If this compiles, re-exports are working
246    }
247
248    /// Verify that help types are accessible from the prelude
249    #[test]
250    fn test_help_prelude_imports() {
251        use crate::config::schema::{CommandsConfig, Metadata};
252        use crate::prelude::*;
253
254        let config = CommandsConfig {
255            metadata: Metadata {
256                version: "1.0.0".to_string(),
257                prompt: "test".to_string(),
258                prompt_suffix: " > ".to_string(),
259            },
260            commands: vec![],
261            global_options: vec![],
262        };
263
264        // DefaultHelpFormatter accessible from prelude
265        let f = DefaultHelpFormatter::new();
266        let _ = f.format_app(&config);
267
268        // Trait object usable (object-safe by design)
269        let _: Box<dyn HelpFormatter> = Box::new(DefaultHelpFormatter::new());
270    }
271}