clap_noun_verb/
builder.rs

1//! Builder pattern API for composable CLI applications
2//!
3//! The `CliBuilder` provides a fluent interface for constructing CLI applications
4//! using the noun-verb pattern.
5
6use crate::error::Result;
7use crate::noun::NounCommand;
8use crate::registry::CommandRegistry;
9use clap::Command;
10
11/// Main builder for creating composable CLI applications
12///
13/// # Examples
14///
15/// ```rust,no_run
16/// use clap_noun_verb::{CliBuilder, noun, verb, VerbArgs, Result};
17///
18/// fn main() -> Result<()> {
19///     let cli = CliBuilder::new()
20///         .name("myapp")
21///         .about("My application")
22///         .noun(noun!("services", "Manage services", [
23///             verb!("status", "Show status", |_args: &VerbArgs| {
24///                 println!("Services are running");
25///                 Ok(())
26///             }),
27///         ]));
28///
29///     cli.run()
30/// }
31/// ```
32pub struct CliBuilder {
33    registry: CommandRegistry,
34}
35
36impl CliBuilder {
37    /// Create a new CLI builder
38    pub fn new() -> Self {
39        Self { registry: CommandRegistry::new() }
40    }
41
42    /// Set the application name
43    pub fn name(mut self, name: impl Into<String>) -> Self {
44        self.registry = self.registry.name(name);
45        self
46    }
47
48    /// Set the application description
49    pub fn about(mut self, about: impl Into<String>) -> Self {
50        self.registry = self.registry.about(about);
51        self
52    }
53
54    /// Set the application version
55    pub fn version(mut self, version: impl Into<String>) -> Self {
56        self.registry = self.registry.version(version);
57        self
58    }
59
60    /// Add global arguments available to all commands
61    pub fn global_args(mut self, args: Vec<clap::Arg>) -> Self {
62        self.registry = self.registry.global_args(args);
63        self
64    }
65
66    /// Enable automatic validation of command structure
67    pub fn auto_validate(mut self, enable: bool) -> Self {
68        self.registry = self.registry.auto_validate(enable);
69        self
70    }
71
72    /// Add a noun command to the CLI
73    pub fn noun(mut self, noun: impl NounCommand + 'static) -> Self {
74        self.registry = self.registry.register_noun(noun);
75        self
76    }
77
78    /// Add multiple noun commands
79    pub fn nouns<I>(mut self, nouns: I) -> Self
80    where
81        I: IntoIterator<Item = Box<dyn NounCommand>>,
82    {
83        self.registry = self.registry.register_nouns(nouns);
84        self
85    }
86
87    /// Run the CLI application
88    pub fn run(self) -> Result<()> {
89        self.registry.run()
90    }
91
92    /// Run the CLI application with custom args
93    pub fn run_with_args(self, args: Vec<String>) -> Result<()> {
94        self.registry.run_with_args(args)
95    }
96
97    /// Get the built command for testing or manual execution
98    pub fn build_command(self) -> Command {
99        self.registry.build_command()
100    }
101
102    /// Get the underlying registry for advanced usage
103    pub fn registry(self) -> CommandRegistry {
104        self.registry
105    }
106
107    /// Get a reference to the registry for introspection
108    pub fn registry_ref(&self) -> &CommandRegistry {
109        &self.registry
110    }
111
112    /// Get the command structure for introspection
113    pub fn command_structure(&self) -> std::collections::HashMap<String, Vec<String>> {
114        self.registry.command_structure()
115    }
116
117    /// Check if a command exists
118    pub fn has_command(&self, name: &str) -> bool {
119        self.registry.has_noun(name)
120    }
121}
122
123impl Default for CliBuilder {
124    fn default() -> Self {
125        Self::new()
126    }
127}
128
129/// Convenience function to run a CLI with the current process args
130pub fn run_cli<F>(builder: F) -> Result<()>
131where
132    F: FnOnce(CliBuilder) -> CliBuilder,
133{
134    let cli = CliBuilder::new();
135    let cli = builder(cli);
136    cli.run()
137}
138
139/// Convenience function to run a CLI with custom args
140pub fn run_cli_with_args<F>(args: Vec<String>, builder: F) -> Result<()>
141where
142    F: FnOnce(CliBuilder) -> CliBuilder,
143{
144    let cli = CliBuilder::new();
145    let cli = builder(cli);
146    cli.run_with_args(args)
147}
148
149/// Convenience function to build a CLI and get the command structure
150pub fn build_cli<F>(builder: F) -> (Command, std::collections::HashMap<String, Vec<String>>)
151where
152    F: FnOnce(CliBuilder) -> CliBuilder,
153{
154    let cli = CliBuilder::new();
155    let cli = builder(cli);
156    let registry = cli.registry;
157    let command = registry.build_command();
158    let structure = registry.command_structure();
159    (command, structure)
160}
161
162/// Macro for quickly building CLI applications
163///
164/// # Example
165///
166/// ```rust
167/// use clap_noun_verb::{cli_builder, noun, verb, VerbArgs, Result};
168///
169/// let cli = cli_builder! {
170///     name: "myapp",
171///     about: "My awesome CLI application",
172///     nouns: [
173///         noun!("services", "Manage services", [
174///             verb!("status", "Show status", |_args: &VerbArgs| {
175///                 println!("Services are running");
176///                 Ok(())
177///             }),
178///         ]),
179///     ]
180/// };
181/// ```
182#[macro_export]
183macro_rules! cli_builder {
184    (name: $name:expr, about: $about:expr, nouns: [$($noun:expr),* $(,)?]) => {
185        {
186            let mut builder = $crate::CliBuilder::new()
187                .name($name)
188                .about($about);
189
190            $(
191                builder = builder.noun($noun);
192            )*
193
194            builder
195        }
196    };
197}