pub struct Command { /* private fields */ }Expand description
Represents a command in the CLI application
Commands can have:
- Subcommands for nested command structures
- Flags that modify behavior
- A run function that executes the command logic
- Dynamic completion functions for arguments and flags
- Help text and aliases
§Examples
use flag_rs::{Command, CommandBuilder, Context};
// Using the builder pattern (recommended)
let cmd = CommandBuilder::new("serve")
.short("Start the web server")
.run(|ctx| {
println!("Server starting...");
Ok(())
})
.build();
// Direct construction
let mut cmd = Command::new("serve");Implementations§
Source§impl Command
impl Command
Sourcepub fn new(name: impl Into<String>) -> Self
pub fn new(name: impl Into<String>) -> Self
Creates a new command with the given name
§Examples
use flag_rs::Command;
let cmd = Command::new("myapp");Sourcepub fn subcommands(&self) -> &HashMap<String, Self>
pub fn subcommands(&self) -> &HashMap<String, Self>
Returns a reference to all subcommands
Examples found in repository?
116fn count_commands(cmd: &flag_rs::Command) -> usize {
117 1 + cmd
118 .subcommands()
119 .values()
120 .map(count_commands)
121 .sum::<usize>()
122}
123
124// Count flags recursively
125fn count_flags(cmd: &flag_rs::Command) -> usize {
126 cmd.flags().len() + cmd.subcommands().values().map(count_flags).sum::<usize>()
127}Sourcepub fn find_subcommand(&self, name: &str) -> Option<&Self>
pub fn find_subcommand(&self, name: &str) -> Option<&Self>
Finds a subcommand by name or alias
§Examples
let mut root = Command::new("app");
let sub = CommandBuilder::new("server")
.aliases(vec!["serve", "s"])
.build();
root.add_command(sub);
assert!(root.find_subcommand("server").is_some());
assert!(root.find_subcommand("serve").is_some());
assert!(root.find_subcommand("s").is_some());Examples found in repository?
168fn bench_subcommand_lookup() {
169 println!("\n=== Subcommand Lookup Benchmarks ===");
170
171 let cli = create_complex_cli();
172
173 let bench = Benchmark::new("Find immediate subcommand", 10_000);
174 let duration = bench.run(|| {
175 let _ = cli.find_subcommand("sub25");
176 });
177 bench.report(duration);
178
179 let bench = Benchmark::new("Find nested subcommand", 10_000);
180 let duration = bench.run(|| {
181 if let Some(sub) = cli.find_subcommand("sub25") {
182 let _ = sub.find_subcommand("nested5");
183 }
184 });
185 bench.report(duration);
186
187 let bench = Benchmark::new("Execute nested command", 1_000);
188 let duration = bench.run(|| {
189 let args = vec![
190 "complex".to_string(),
191 "sub25".to_string(),
192 "nested5".to_string(),
193 "--deep".to_string(),
194 ];
195 let _ = cli.execute(args);
196 });
197 bench.report(duration);
198}Sourcepub fn find_subcommand_mut(&mut self, name: &str) -> Option<&mut Self>
pub fn find_subcommand_mut(&mut self, name: &str) -> Option<&mut Self>
Finds a mutable reference to a subcommand by name or alias
Sourcepub fn add_command(&mut self, cmd: Self)
pub fn add_command(&mut self, cmd: Self)
Adds a subcommand to this command
§Examples
use flag_rs::{Command, CommandBuilder};
let mut root = Command::new("myapp");
let serve = CommandBuilder::new("serve")
.short("Start the server")
.build();
root.add_command(serve);Examples found in repository?
64fn create_complex_cli() -> flag_rs::Command {
65 let mut root = CommandBuilder::new("complex")
66 .short("Complex CLI with many subcommands")
67 .flag(
68 Flag::new("config")
69 .short('c')
70 .value_type(FlagType::File)
71 .default(FlagValue::String("config.yaml".to_string())),
72 )
73 .build();
74
75 // Add 50 subcommands
76 for i in 0..50 {
77 let mut sub = CommandBuilder::new(format!("sub{i:02}"))
78 .short(format!("Subcommand {i}"))
79 .flag(Flag::new("flag1").short('1').value_type(FlagType::String))
80 .flag(Flag::new("flag2").short('2').value_type(FlagType::Int))
81 .build();
82
83 // Add 10 nested subcommands
84 for j in 0..10 {
85 sub.add_command(
86 CommandBuilder::new(format!("nested{j}"))
87 .short(format!("Nested command {j}"))
88 .flag(Flag::new("deep").value_type(FlagType::Bool))
89 .build(),
90 );
91 }
92
93 root.add_command(sub);
94 }
95
96 root
97}More examples
12fn create_many_subcommands(parent: &mut flag_rs::Command) {
13 // Simulate a large CLI with 100 subcommands
14 for i in 0..100 {
15 let cmd_name = format!("service-{i:03}");
16
17 // Use string interning for flag names that repeat across commands
18 let namespace_flag_name = string_pool::intern("namespace");
19 let region_flag_name = string_pool::intern("region");
20 let env_flag_name = string_pool::intern("environment");
21
22 let cmd = CommandBuilder::new(cmd_name.clone())
23 .short(format!("Manage service {i}"))
24 .flag(
25 Flag::new(namespace_flag_name.to_string())
26 .short('n')
27 .usage("Kubernetes namespace")
28 .value_type(FlagType::String)
29 .default(flag_rs::FlagValue::String("default".to_string())),
30 )
31 .flag(
32 Flag::new(region_flag_name.to_string())
33 .short('r')
34 .usage("AWS region")
35 .value_type(FlagType::Choice(vec![
36 "us-east-1".to_string(),
37 "us-west-2".to_string(),
38 "eu-west-1".to_string(),
39 "ap-southeast-1".to_string(),
40 ]))
41 .default(flag_rs::FlagValue::String("us-east-1".to_string())),
42 )
43 .flag(
44 Flag::new(env_flag_name.to_string())
45 .short('e')
46 .usage("Deployment environment")
47 .value_type(FlagType::Choice(vec![
48 "dev".to_string(),
49 "staging".to_string(),
50 "prod".to_string(),
51 ]))
52 .default(flag_rs::FlagValue::String("dev".to_string())),
53 )
54 .subcommand(
55 CommandBuilder::new("deploy")
56 .short("Deploy the service")
57 .arg_completion(move |_ctx, prefix| {
58 // Use optimized completion result
59 let optimized = CompletionResultOptimized::new()
60 .add(Cow::Borrowed("rolling-update"))
61 .add(Cow::Borrowed("blue-green"))
62 .add(Cow::Borrowed("canary"))
63 .add_with_description(
64 Cow::Borrowed("recreate"),
65 Cow::Borrowed("Recreate all pods"),
66 );
67
68 // Filter based on prefix
69 let filtered = CompletionResultOptimized::new().extend_items(
70 optimized
71 .items
72 .into_iter()
73 .filter(|item| item.value.starts_with(prefix)),
74 );
75
76 // Convert to legacy format for compatibility
77 Ok(filtered.into_legacy())
78 })
79 .build(),
80 )
81 .subcommand(
82 CommandBuilder::new("scale")
83 .short("Scale the service")
84 .flag(
85 Flag::new("replicas")
86 .usage("Number of replicas")
87 .value_type(FlagType::Int)
88 .required(),
89 )
90 .build(),
91 )
92 .subcommand(
93 CommandBuilder::new("logs")
94 .short("View service logs")
95 .flag(
96 Flag::new("follow")
97 .short('f')
98 .usage("Follow log output")
99 .value_type(FlagType::Bool),
100 )
101 .flag(
102 Flag::new("tail")
103 .usage("Number of lines to show")
104 .value_type(FlagType::Int)
105 .default(flag_rs::FlagValue::Int(100)),
106 )
107 .build(),
108 )
109 .build();
110
111 parent.add_command(cmd);
112 }
113}
114
115// Count commands recursively
116fn count_commands(cmd: &flag_rs::Command) -> usize {
117 1 + cmd
118 .subcommands()
119 .values()
120 .map(count_commands)
121 .sum::<usize>()
122}
123
124// Count flags recursively
125fn count_flags(cmd: &flag_rs::Command) -> usize {
126 cmd.flags().len() + cmd.subcommands().values().map(count_flags).sum::<usize>()
127}
128
129fn main() {
130 // Create root command
131 let mut app = CommandBuilder::new("megacli")
132 .short("A large CLI demonstrating memory optimizations")
133 .long(
134 "This CLI simulates a large application with many subcommands and flags
135to demonstrate memory optimization techniques in flag-rs.
136
137Memory optimizations include:
138- String interning for repeated flag names
139- Cow (Copy-on-Write) strings for static completions
140- Optimized completion results that avoid parallel vectors
141- Lazy allocation strategies",
142 )
143 .flag(
144 Flag::new("verbose")
145 .short('v')
146 .usage("Enable verbose output")
147 .value_type(FlagType::Bool),
148 )
149 .flag(
150 Flag::new("config")
151 .short('c')
152 .usage("Path to config file")
153 .value_type(FlagType::File)
154 .default(flag_rs::FlagValue::String(
155 "~/.megacli/config.yaml".to_string(),
156 )),
157 )
158 .build();
159
160 // Add many subcommands
161 create_many_subcommands(&mut app);
162
163 let total_commands = count_commands(&app);
164 let total_flags = count_flags(&app);
165
166 // Add a special command to show memory usage stats
167 app.add_command(
168 CommandBuilder::new("stats")
169 .short("Show CLI statistics and memory usage")
170 .run(move |_ctx| {
171 println!("=== MegaCLI Statistics ===\n");
172
173 println!("Total commands: {total_commands}");
174 println!("Total flags: {total_flags}");
175 println!("String pool size: {} unique strings", 3); // We interned 3 flag names
176
177 println!("\nMemory optimization features in use:");
178 println!("✓ String interning for flag names");
179 println!("✓ Cow<str> for static completion values");
180 println!("✓ CompletionResultOptimized for reduced allocations");
181 println!("✓ Lazy allocation strategies");
182
183 println!("\nEstimated memory savings:");
184 println!("- 60-70% reduction in string allocations");
185 println!("- 40-50% reduction in completion memory usage");
186 println!("- Improved cache locality for better performance");
187
188 Ok(())
189 })
190 .build(),
191 );
192
193 // Execute the CLI
194 let args: Vec<String> = std::env::args().skip(1).collect();
195
196 if args.is_empty() {
197 println!("=== Memory Optimization Demo ===\n");
198 println!("This demo shows how flag-rs optimizes memory for large CLIs.\n");
199 println!("Try these commands:");
200 println!(
201 " {} stats # Show memory statistics",
202 std::env::args().next().unwrap_or_default()
203 );
204 println!(
205 " {} service-001 deploy <TAB> # Test optimized completions",
206 std::env::args().next().unwrap_or_default()
207 );
208 println!(
209 " {} --help # See all 100+ commands",
210 std::env::args().next().unwrap_or_default()
211 );
212 println!("\nThe optimizations are transparent to users but significantly");
213 println!("reduce memory usage for CLIs with many commands and flags.");
214 std::process::exit(0);
215 }
216
217 if let Err(e) = app.execute(args) {
218 eprintln!("{e}");
219 std::process::exit(1);
220 }
221}Sourcepub fn execute(&self, args: Vec<String>) -> Result<()>
pub fn execute(&self, args: Vec<String>) -> Result<()>
Executes the command with the given arguments
This is the main entry point for running your CLI application. It handles:
- Shell completion requests
- Flag parsing
- Subcommand routing
- Execution of the appropriate run function
§Examples
use flag_rs::CommandBuilder;
let app = CommandBuilder::new("myapp")
.run(|ctx| {
println!("Hello from myapp!");
Ok(())
})
.build();
// In main():
// let args: Vec<String> = std::env::args().skip(1).collect();
// if let Err(e) = app.execute(args) {
// eprintln!("Error: {}", e);
// std::process::exit(1);
// }Examples found in repository?
More examples
125fn bench_flag_parsing() {
126 println!("\n=== Flag Parsing Benchmarks ===");
127
128 let cli = create_simple_cli();
129
130 let bench = Benchmark::new("Parse no flags", 10_000);
131 let duration = bench.run(|| {
132 let args = vec!["bench".to_string()];
133 let _ = cli.execute(args);
134 });
135 bench.report(duration);
136
137 let bench = Benchmark::new("Parse single flag", 10_000);
138 let duration = bench.run(|| {
139 let args = vec!["bench".to_string(), "--verbose".to_string()];
140 let _ = cli.execute(args);
141 });
142 bench.report(duration);
143
144 let bench = Benchmark::new("Parse flag with value", 10_000);
145 let duration = bench.run(|| {
146 let args = vec![
147 "bench".to_string(),
148 "--output".to_string(),
149 "file.txt".to_string(),
150 ];
151 let _ = cli.execute(args);
152 });
153 bench.report(duration);
154
155 let bench = Benchmark::new("Parse multiple flags", 10_000);
156 let duration = bench.run(|| {
157 let args = vec![
158 "bench".to_string(),
159 "-v".to_string(),
160 "-o".to_string(),
161 "output.txt".to_string(),
162 ];
163 let _ = cli.execute(args);
164 });
165 bench.report(duration);
166}
167
168fn bench_subcommand_lookup() {
169 println!("\n=== Subcommand Lookup Benchmarks ===");
170
171 let cli = create_complex_cli();
172
173 let bench = Benchmark::new("Find immediate subcommand", 10_000);
174 let duration = bench.run(|| {
175 let _ = cli.find_subcommand("sub25");
176 });
177 bench.report(duration);
178
179 let bench = Benchmark::new("Find nested subcommand", 10_000);
180 let duration = bench.run(|| {
181 if let Some(sub) = cli.find_subcommand("sub25") {
182 let _ = sub.find_subcommand("nested5");
183 }
184 });
185 bench.report(duration);
186
187 let bench = Benchmark::new("Execute nested command", 1_000);
188 let duration = bench.run(|| {
189 let args = vec![
190 "complex".to_string(),
191 "sub25".to_string(),
192 "nested5".to_string(),
193 "--deep".to_string(),
194 ];
195 let _ = cli.execute(args);
196 });
197 bench.report(duration);
198}
199
200fn bench_completion() {
201 println!("\n=== Completion Benchmarks ===");
202
203 let cli = CommandBuilder::new("comp")
204 .arg_completion(|_ctx, prefix| {
205 let items: Vec<String> = (0..100)
206 .map(|i| format!("item{i:03}"))
207 .filter(|item| item.starts_with(prefix))
208 .collect();
209 Ok(CompletionResult::new().extend(items))
210 })
211 .build();
212
213 let ctx = flag_rs::Context::new(vec!["comp".to_string()]);
214
215 let bench = Benchmark::new("Complete with empty prefix (100 items)", 1_000);
216 let duration = bench.run(|| {
217 let _ = cli.get_completions(&ctx, "", None);
218 });
219 bench.report(duration);
220
221 let bench = Benchmark::new("Complete with prefix (filtered)", 1_000);
222 let duration = bench.run(|| {
223 let _ = cli.get_completions(&ctx, "item05", None);
224 });
225 bench.report(duration);
226
227 // Test completion with descriptions
228 let cli_desc = CommandBuilder::new("comp_desc")
229 .arg_completion(|_ctx, prefix| {
230 let mut result = CompletionResult::new();
231 for i in 0..50 {
232 let item = format!("option{i:02}");
233 if item.starts_with(prefix) {
234 result =
235 result.add_with_description(item, format!("Description for option {i}"));
236 }
237 }
238 Ok(result)
239 })
240 .build();
241
242 let bench = Benchmark::new("Complete with descriptions (50 items)", 1_000);
243 let duration = bench.run(|| {
244 let _ = cli_desc.get_completions(&ctx, "", None);
245 });
246 bench.report(duration);
247}
248
249fn bench_flag_validation() {
250 println!("\n=== Flag Validation Benchmarks ===");
251
252 let cli = CommandBuilder::new("validate")
253 .flag(Flag::new("choice").value_type(FlagType::Choice(vec![
254 "opt1".to_string(),
255 "opt2".to_string(),
256 "opt3".to_string(),
257 ])))
258 .flag(Flag::new("range").value_type(FlagType::Range(1, 100)))
259 .flag(
260 Flag::new("required")
261 .value_type(FlagType::String)
262 .required(),
263 )
264 .build();
265
266 let bench = Benchmark::new("Validate choice flag", 10_000);
267 let duration = bench.run(|| {
268 let args = vec![
269 "validate".to_string(),
270 "--choice".to_string(),
271 "opt2".to_string(),
272 "--required".to_string(),
273 "value".to_string(),
274 ];
275 let _ = cli.execute(args);
276 });
277 bench.report(duration);
278
279 let bench = Benchmark::new("Validate range flag", 10_000);
280 let duration = bench.run(|| {
281 let args = vec![
282 "validate".to_string(),
283 "--range".to_string(),
284 "50".to_string(),
285 "--required".to_string(),
286 "value".to_string(),
287 ];
288 let _ = cli.execute(args);
289 });
290 bench.report(duration);
291
292 let bench = Benchmark::new("Validate missing required flag", 10_000);
293 let duration = bench.run(|| {
294 let args = vec!["validate".to_string()];
295 let _ = cli.execute(args); // This will fail validation
296 });
297 bench.report(duration);
298}4fn main() {
5 let app = CommandBuilder::new("suggestion-demo")
6 .short("Demonstrates command suggestions")
7 .subcommand(
8 CommandBuilder::new("start")
9 .short("Start the service")
10 .run(|_| {
11 println!("Starting service...");
12 Ok(())
13 })
14 .build(),
15 )
16 .subcommand(
17 CommandBuilder::new("stop")
18 .short("Stop the service")
19 .run(|_| {
20 println!("Stopping service...");
21 Ok(())
22 })
23 .build(),
24 )
25 .subcommand(
26 CommandBuilder::new("restart")
27 .short("Restart the service")
28 .run(|_| {
29 println!("Restarting service...");
30 Ok(())
31 })
32 .build(),
33 )
34 .subcommand(
35 CommandBuilder::new("status")
36 .short("Show service status")
37 .run(|_| {
38 println!("Service is running");
39 Ok(())
40 })
41 .build(),
42 )
43 .subcommand(
44 CommandBuilder::new("config")
45 .short("Manage configuration")
46 .subcommand(
47 CommandBuilder::new("get")
48 .short("Get configuration value")
49 .run(|_| {
50 println!("Configuration value: enabled");
51 Ok(())
52 })
53 .build(),
54 )
55 .subcommand(
56 CommandBuilder::new("set")
57 .short("Set configuration value")
58 .run(|_| {
59 println!("Configuration updated");
60 Ok(())
61 })
62 .build(),
63 )
64 .build(),
65 )
66 .build();
67
68 let args: Vec<String> = std::env::args().skip(1).collect();
69
70 // Show what command was attempted
71 if !args.is_empty() {
72 println!("Command attempted: {}\n", args[0]);
73 }
74
75 if let Err(e) = app.execute(args) {
76 eprintln!("Error: {}", e);
77 std::process::exit(1);
78 }
79}3fn main() {
4 let cmd = CommandBuilder::new("test")
5 .short("Test completion with descriptions")
6 .flag(
7 Flag::new("environment")
8 .short('e')
9 .usage("Target environment")
10 .value_type(FlagType::String),
11 )
12 .flag_completion("environment", |_ctx, prefix| {
13 let mut result = CompletionResult::new();
14 let envs = vec![
15 ("dev", "Development environment - safe for testing"),
16 ("staging", "Staging environment - mirror of production"),
17 ("production", "Production environment - BE CAREFUL!"),
18 ];
19
20 for (env, desc) in envs {
21 if env.starts_with(prefix) {
22 result = result.add_with_description(env, desc);
23 }
24 }
25
26 Ok(result)
27 })
28 .subcommand(
29 CommandBuilder::new("deploy")
30 .short("Deploy the application")
31 .long("Deploy the application to the specified environment")
32 .build(),
33 )
34 .subcommand(
35 CommandBuilder::new("rollback")
36 .short("Rollback to previous version")
37 .long("Rollback the application to the previous deployed version")
38 .build(),
39 )
40 .build();
41
42 // Test completion output
43 if let Ok(shell_type) = std::env::var("TEST_COMPLETE") {
44 // For testing, override shell type to "display" to see formatted output
45 let display_mode = std::env::var("DISPLAY_MODE").is_ok();
46 let args: Vec<String> = std::env::args().skip(1).collect();
47
48 if display_mode {
49 // This is a hack for testing - normally shells would handle this
50 println!("Display mode - showing formatted completions:");
51 match cmd.handle_completion_request(&args) {
52 Ok(completions) => {
53 for completion in completions {
54 println!("{}", completion);
55 }
56 }
57 Err(e) => eprintln!("Completion error: {}", e),
58 }
59 } else {
60 match cmd.handle_completion_request(&args) {
61 Ok(completions) => {
62 println!("Shell type: {}", shell_type);
63 println!("Completions:");
64 for completion in completions {
65 println!("{}", completion);
66 }
67 }
68 Err(e) => eprintln!("Completion error: {}", e),
69 }
70 }
71 } else {
72 let args: Vec<String> = std::env::args().skip(1).collect();
73 if let Err(e) = cmd.execute(args) {
74 eprintln!("Error: {}", e);
75 std::process::exit(1);
76 }
77 }
78}129fn main() {
130 // Create root command
131 let mut app = CommandBuilder::new("megacli")
132 .short("A large CLI demonstrating memory optimizations")
133 .long(
134 "This CLI simulates a large application with many subcommands and flags
135to demonstrate memory optimization techniques in flag-rs.
136
137Memory optimizations include:
138- String interning for repeated flag names
139- Cow (Copy-on-Write) strings for static completions
140- Optimized completion results that avoid parallel vectors
141- Lazy allocation strategies",
142 )
143 .flag(
144 Flag::new("verbose")
145 .short('v')
146 .usage("Enable verbose output")
147 .value_type(FlagType::Bool),
148 )
149 .flag(
150 Flag::new("config")
151 .short('c')
152 .usage("Path to config file")
153 .value_type(FlagType::File)
154 .default(flag_rs::FlagValue::String(
155 "~/.megacli/config.yaml".to_string(),
156 )),
157 )
158 .build();
159
160 // Add many subcommands
161 create_many_subcommands(&mut app);
162
163 let total_commands = count_commands(&app);
164 let total_flags = count_flags(&app);
165
166 // Add a special command to show memory usage stats
167 app.add_command(
168 CommandBuilder::new("stats")
169 .short("Show CLI statistics and memory usage")
170 .run(move |_ctx| {
171 println!("=== MegaCLI Statistics ===\n");
172
173 println!("Total commands: {total_commands}");
174 println!("Total flags: {total_flags}");
175 println!("String pool size: {} unique strings", 3); // We interned 3 flag names
176
177 println!("\nMemory optimization features in use:");
178 println!("✓ String interning for flag names");
179 println!("✓ Cow<str> for static completion values");
180 println!("✓ CompletionResultOptimized for reduced allocations");
181 println!("✓ Lazy allocation strategies");
182
183 println!("\nEstimated memory savings:");
184 println!("- 60-70% reduction in string allocations");
185 println!("- 40-50% reduction in completion memory usage");
186 println!("- Improved cache locality for better performance");
187
188 Ok(())
189 })
190 .build(),
191 );
192
193 // Execute the CLI
194 let args: Vec<String> = std::env::args().skip(1).collect();
195
196 if args.is_empty() {
197 println!("=== Memory Optimization Demo ===\n");
198 println!("This demo shows how flag-rs optimizes memory for large CLIs.\n");
199 println!("Try these commands:");
200 println!(
201 " {} stats # Show memory statistics",
202 std::env::args().next().unwrap_or_default()
203 );
204 println!(
205 " {} service-001 deploy <TAB> # Test optimized completions",
206 std::env::args().next().unwrap_or_default()
207 );
208 println!(
209 " {} --help # See all 100+ commands",
210 std::env::args().next().unwrap_or_default()
211 );
212 println!("\nThe optimizations are transparent to users but significantly");
213 println!("reduce memory usage for CLIs with many commands and flags.");
214 std::process::exit(0);
215 }
216
217 if let Err(e) = app.execute(args) {
218 eprintln!("{e}");
219 std::process::exit(1);
220 }
221}4fn main() {
5 let app = CommandBuilder::new("validator-demo")
6 .short("Demonstrates argument validation")
7 .subcommand(
8 CommandBuilder::new("copy")
9 .short("Copy files (requires exactly 2 arguments)")
10 .args(ArgValidator::ExactArgs(2))
11 .run(|ctx| {
12 let args = ctx.args();
13 println!("Copying from '{}' to '{}'", args[0], args[1]);
14 Ok(())
15 })
16 .build(),
17 )
18 .subcommand(
19 CommandBuilder::new("delete")
20 .short("Delete files (requires at least 1 argument)")
21 .args(ArgValidator::MinimumArgs(1))
22 .run(|ctx| {
23 println!("Deleting {} file(s):", ctx.args().len());
24 for file in ctx.args() {
25 println!(" - {}", file);
26 }
27 Ok(())
28 })
29 .build(),
30 )
31 .subcommand(
32 CommandBuilder::new("list")
33 .short("List items (accepts 0-3 arguments)")
34 .args(ArgValidator::RangeArgs(0, 3))
35 .run(|ctx| {
36 if ctx.args().is_empty() {
37 println!("Listing all items");
38 } else {
39 println!("Listing specific items:");
40 for item in ctx.args() {
41 println!(" - {}", item);
42 }
43 }
44 Ok(())
45 })
46 .build(),
47 )
48 .subcommand(
49 CommandBuilder::new("action")
50 .short("Perform action (only start/stop/restart allowed)")
51 .args(ArgValidator::OnlyValidArgs(vec![
52 "start".to_string(),
53 "stop".to_string(),
54 "restart".to_string(),
55 ]))
56 .run(|ctx| {
57 let action = ctx.args().first().map(String::as_str).unwrap_or("start");
58 println!("Performing action: {}", action);
59 Ok(())
60 })
61 .build(),
62 )
63 .subcommand(
64 CommandBuilder::new("numbers")
65 .short("Process numbers (custom validator for integers)")
66 .args(ArgValidator::Custom(std::sync::Arc::new(|args| {
67 if args.is_empty() {
68 return Err(Error::ArgumentValidation {
69 message: "at least one number required".to_string(),
70 expected: "numbers".to_string(),
71 received: 0,
72 });
73 }
74
75 for (i, arg) in args.iter().enumerate() {
76 if arg.parse::<i32>().is_err() {
77 return Err(Error::ArgumentValidation {
78 message: format!(
79 "argument {} ('{}') must be an integer",
80 i + 1,
81 arg
82 ),
83 expected: "integer".to_string(),
84 received: args.len(),
85 });
86 }
87 }
88 Ok(())
89 })))
90 .run(|ctx| {
91 let numbers: Vec<i32> = ctx.args().iter().map(|s| s.parse().unwrap()).collect();
92 let sum: i32 = numbers.iter().sum();
93 println!("Sum of {} numbers: {}", numbers.len(), sum);
94 Ok(())
95 })
96 .build(),
97 )
98 .build();
99
100 let args: Vec<String> = std::env::args().skip(1).collect();
101 if let Err(e) = app.execute(args) {
102 eprintln!("Error: {}", e);
103 std::process::exit(1);
104 }
105}- examples/enhanced_errors_demo.rs
- examples/error_handling_demo.rs
- examples/timeout_completion_demo.rs
- examples/flag_completion_demo.rs
- examples/enhanced_help_demo.rs
- examples/cached_completion_demo.rs
- examples/lifecycle_hooks_demo.rs
- examples/builder_api_demo.rs
- examples/advanced_flags_demo.rs
- examples/help_formatting_demo.rs
- examples/active_help_demo.rs
Sourcepub fn execute_with_context(&self, ctx: &mut Context) -> Result<()>
pub fn execute_with_context(&self, ctx: &mut Context) -> Result<()>
Executes the command with an existing context
This method is useful when you need to provide pre-configured context or when implementing custom command routing.
Sourcepub fn set_arg_completion<F>(&mut self, f: F)
pub fn set_arg_completion<F>(&mut self, f: F)
Sets the argument completion function for this command
The completion function is called when the user presses TAB to complete command arguments. It receives the current context and the prefix to complete.
§Examples
use flag_rs::{Command, CompletionResult};
let mut cmd = Command::new("get");
cmd.set_arg_completion(|ctx, prefix| {
let items = vec!["users", "posts", "comments"];
Ok(CompletionResult::new().extend(
items.into_iter()
.filter(|i| i.starts_with(prefix))
.map(String::from)
))
});Sourcepub fn set_flag_completion<F>(&mut self, flag_name: impl Into<String>, f: F)
pub fn set_flag_completion<F>(&mut self, flag_name: impl Into<String>, f: F)
Sets the completion function for a specific flag
This allows dynamic completion of flag values based on runtime state.
§Examples
use flag_rs::{Command, CompletionResult};
let mut cmd = Command::new("deploy");
cmd.set_flag_completion("environment", |ctx, prefix| {
let envs = vec!["dev", "staging", "production"];
Ok(CompletionResult::new().extend(
envs.into_iter()
.filter(|e| e.starts_with(prefix))
.map(String::from)
))
});Sourcepub fn get_completions(
&self,
ctx: &Context,
to_complete: &str,
completing_flag: Option<&str>,
) -> Result<CompletionResult>
pub fn get_completions( &self, ctx: &Context, to_complete: &str, completing_flag: Option<&str>, ) -> Result<CompletionResult>
Gets completion suggestions for the current context
This method is primarily used internally by the shell completion system.
Examples found in repository?
200fn bench_completion() {
201 println!("\n=== Completion Benchmarks ===");
202
203 let cli = CommandBuilder::new("comp")
204 .arg_completion(|_ctx, prefix| {
205 let items: Vec<String> = (0..100)
206 .map(|i| format!("item{i:03}"))
207 .filter(|item| item.starts_with(prefix))
208 .collect();
209 Ok(CompletionResult::new().extend(items))
210 })
211 .build();
212
213 let ctx = flag_rs::Context::new(vec!["comp".to_string()]);
214
215 let bench = Benchmark::new("Complete with empty prefix (100 items)", 1_000);
216 let duration = bench.run(|| {
217 let _ = cli.get_completions(&ctx, "", None);
218 });
219 bench.report(duration);
220
221 let bench = Benchmark::new("Complete with prefix (filtered)", 1_000);
222 let duration = bench.run(|| {
223 let _ = cli.get_completions(&ctx, "item05", None);
224 });
225 bench.report(duration);
226
227 // Test completion with descriptions
228 let cli_desc = CommandBuilder::new("comp_desc")
229 .arg_completion(|_ctx, prefix| {
230 let mut result = CompletionResult::new();
231 for i in 0..50 {
232 let item = format!("option{i:02}");
233 if item.starts_with(prefix) {
234 result =
235 result.add_with_description(item, format!("Description for option {i}"));
236 }
237 }
238 Ok(result)
239 })
240 .build();
241
242 let bench = Benchmark::new("Complete with descriptions (50 items)", 1_000);
243 let duration = bench.run(|| {
244 let _ = cli_desc.get_completions(&ctx, "", None);
245 });
246 bench.report(duration);
247}Sourcepub fn print_help(&self)
pub fn print_help(&self)
Prints the help message for this command
The help message includes:
- Command description
- Usage information
- Available subcommands
- Local and global flags
Help text is automatically colored when outputting to a TTY.
Examples found in repository?
17fn main() {
18 let app = CommandBuilder::new("demo")
19 .short("Terminal feature demonstration")
20 .long("This application demonstrates the terminal width detection and text wrapping features. The help text should automatically adjust to your terminal width, wrapping long lines at word boundaries while maintaining readability. Try running this with different COLUMNS values to see how it adapts.")
21 .flag(
22 Flag::new("file")
23 .short('f')
24 .usage("Input file path. This flag expects a path to a file that will be processed. The file must exist and be readable.")
25 .value_type(FlagType::String)
26 )
27 .flag(
28 Flag::new("output-format")
29 .short('o')
30 .usage("Output format for results. Supported formats include: json (machine-readable JSON format), yaml (human-friendly YAML format), table (formatted ASCII table), csv (comma-separated values for spreadsheet import)")
31 .value_type(FlagType::String)
32 .default(FlagValue::String("table".to_string()))
33 )
34 .subcommand(
35 CommandBuilder::new("process")
36 .short("Process data with various transformations and filters that can be applied in sequence")
37 .build()
38 )
39 .build();
40
41 // Always show help for this demo
42 app.print_help();
43}Sourcepub fn handle_completion_request(&self, args: &[String]) -> Result<Vec<String>>
pub fn handle_completion_request(&self, args: &[String]) -> Result<Vec<String>>
Handles shell completion requests
This method is called when the shell requests completions via the
environment variable (e.g., MYAPP_COMPLETE=bash).
Examples found in repository?
3fn main() {
4 let cmd = CommandBuilder::new("test")
5 .short("Test completion with descriptions")
6 .flag(
7 Flag::new("environment")
8 .short('e')
9 .usage("Target environment")
10 .value_type(FlagType::String),
11 )
12 .flag_completion("environment", |_ctx, prefix| {
13 let mut result = CompletionResult::new();
14 let envs = vec![
15 ("dev", "Development environment - safe for testing"),
16 ("staging", "Staging environment - mirror of production"),
17 ("production", "Production environment - BE CAREFUL!"),
18 ];
19
20 for (env, desc) in envs {
21 if env.starts_with(prefix) {
22 result = result.add_with_description(env, desc);
23 }
24 }
25
26 Ok(result)
27 })
28 .subcommand(
29 CommandBuilder::new("deploy")
30 .short("Deploy the application")
31 .long("Deploy the application to the specified environment")
32 .build(),
33 )
34 .subcommand(
35 CommandBuilder::new("rollback")
36 .short("Rollback to previous version")
37 .long("Rollback the application to the previous deployed version")
38 .build(),
39 )
40 .build();
41
42 // Test completion output
43 if let Ok(shell_type) = std::env::var("TEST_COMPLETE") {
44 // For testing, override shell type to "display" to see formatted output
45 let display_mode = std::env::var("DISPLAY_MODE").is_ok();
46 let args: Vec<String> = std::env::args().skip(1).collect();
47
48 if display_mode {
49 // This is a hack for testing - normally shells would handle this
50 println!("Display mode - showing formatted completions:");
51 match cmd.handle_completion_request(&args) {
52 Ok(completions) => {
53 for completion in completions {
54 println!("{}", completion);
55 }
56 }
57 Err(e) => eprintln!("Completion error: {}", e),
58 }
59 } else {
60 match cmd.handle_completion_request(&args) {
61 Ok(completions) => {
62 println!("Shell type: {}", shell_type);
63 println!("Completions:");
64 for completion in completions {
65 println!("{}", completion);
66 }
67 }
68 Err(e) => eprintln!("Completion error: {}", e),
69 }
70 }
71 } else {
72 let args: Vec<String> = std::env::args().skip(1).collect();
73 if let Err(e) = cmd.execute(args) {
74 eprintln!("Error: {}", e);
75 std::process::exit(1);
76 }
77 }
78}Source§impl Command
impl Command
Sourcepub fn generate_completion(&self, shell: Shell) -> String
pub fn generate_completion(&self, shell: Shell) -> String
Generates a completion script for the specified shell
The generated script should be saved to the appropriate location for your shell to load it automatically.
§Arguments
shell- The shell to generate completions for
§Returns
A string containing the shell completion script
§Shell-specific installation
§Bash
Save to /etc/bash_completion.d/myapp or source from .bashrc:
myapp completion bash > ~/.myapp-completion.bash
echo "source ~/.myapp-completion.bash" >> ~/.bashrc§Zsh
Save to a directory in your $fpath:
myapp completion zsh > ~/.zsh/completions/_myapp§Fish
Save to Fish’s completion directory:
myapp completion fish > ~/.config/fish/completions/myapp.fishExamples found in repository?
319fn build_completion_command() -> Command {
320 CommandBuilder::new("completion")
321 .short("Generate shell completion scripts")
322 .long("Generate shell completion scripts for kubectl")
323 .arg_completion(|_ctx, prefix| {
324 let shells = vec![
325 ("bash", "Bash shell completion"),
326 ("zsh", "Zsh shell completion"),
327 ("fish", "Fish shell completion"),
328 ];
329
330 let mut result = CompletionResult::new();
331 for (shell, description) in shells {
332 if shell.starts_with(prefix) {
333 result =
334 result.add_with_description(shell.to_string(), description.to_string());
335 }
336 }
337
338 Ok(result)
339 })
340 .run(|ctx| {
341 let shell_name = ctx.args().first().ok_or_else(|| {
342 flag_rs::Error::ArgumentParsing(
343 "shell name required (bash, zsh, or fish)".to_string(),
344 )
345 })?;
346
347 let shell = match shell_name.as_str() {
348 "bash" => Shell::Bash,
349 "zsh" => Shell::Zsh,
350 "fish" => Shell::Fish,
351 _ => {
352 return Err(flag_rs::Error::ArgumentParsing(format!(
353 "unsupported shell: {}",
354 shell_name
355 )));
356 }
357 };
358
359 // In a real app, you'd get the root command from a shared reference
360 // For this example, we'll recreate it
361 let root = build_kubectl();
362 println!("{}", root.generate_completion(shell));
363
364 Ok(())
365 })
366 .build()
367}