pub struct CommandBuilder { /* private fields */ }Expand description
Builder for creating commands with a fluent API
CommandBuilder provides a convenient way to construct commands
with method chaining. This is the recommended way to create commands.
§Examples
use flag_rs::{CommandBuilder, Flag, FlagType, FlagValue};
let cmd = CommandBuilder::new("serve")
.short("Start the web server")
.long("Start the web server on the specified port with the given configuration")
.aliases(vec!["server", "s"])
.flag(
Flag::new("port")
.short('p')
.usage("Port to listen on")
.value_type(FlagType::Int)
.default(FlagValue::Int(8080))
)
.flag(
Flag::new("config")
.short('c')
.usage("Configuration file path")
.value_type(FlagType::String)
.required()
)
.run(|ctx| {
let port = ctx.flag("port")
.and_then(|s| s.parse::<i64>().ok())
.unwrap_or(8080);
let config = ctx.flag("config")
.map(|s| s.as_str())
.unwrap_or("config.toml");
println!("Starting server on port {} with config {}", port, config);
Ok(())
})
.build();Implementations§
Source§impl CommandBuilder
impl CommandBuilder
Sourcepub fn new(name: impl Into<String>) -> Self
pub fn new(name: impl Into<String>) -> Self
Creates a new command builder with the given name
Examples found in repository?
50fn create_simple_cli() -> flag_rs::Command {
51 CommandBuilder::new("bench")
52 .short("Benchmark CLI")
53 .flag(
54 Flag::new("verbose")
55 .short('v')
56 .value_type(FlagType::Bool)
57 .default(FlagValue::Bool(false)),
58 )
59 .flag(Flag::new("output").short('o').value_type(FlagType::String))
60 .build()
61}
62
63/// Creates a complex nested command structure
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}
98
99fn bench_command_creation() {
100 println!("\n=== Command Creation Benchmarks ===");
101
102 let bench = Benchmark::new("Simple command creation", 10_000);
103 let duration = bench.run(|| {
104 let _ = create_simple_cli();
105 });
106 bench.report(duration);
107
108 let bench = Benchmark::new("Complex command creation (50 subs)", 100);
109 let duration = bench.run(|| {
110 let _ = create_complex_cli();
111 });
112 bench.report(duration);
113
114 let bench = Benchmark::new("CommandBuilder with 10 flags", 1_000);
115 let duration = bench.run(|| {
116 let mut cmd = CommandBuilder::new("test");
117 for i in 0..10 {
118 cmd = cmd.flag(Flag::new(format!("flag{i}")).value_type(FlagType::String));
119 }
120 let _ = cmd.build();
121 });
122 bench.report(duration);
123}
124
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}More examples
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}
78
79fn build_get_pods() -> Command {
80 CommandBuilder::new("pods")
81 .aliases(vec!["po", "pod"])
82 .short("List pods")
83 .arg_completion(|ctx, prefix| {
84 // This is the key feature - dynamic completion based on runtime state!
85 let namespace = ctx
86 .flag("namespace")
87 .map(String::as_str)
88 .unwrap_or("default");
89
90 // In real kubectl, this would query the K8s API
91 let pods = get_pods_with_status(namespace);
92 let mut result = CompletionResult::new();
93
94 for (pod_name, status) in pods {
95 if pod_name.starts_with(prefix) {
96 result = result.add_with_description(pod_name, status);
97 }
98 }
99
100 Ok(result)
101 })
102 .run(|ctx| {
103 let namespace = ctx
104 .flag("namespace")
105 .map(String::as_str)
106 .unwrap_or("default");
107
108 println!("Listing pods in namespace: {namespace}");
109
110 if let Some(pod_name) = ctx.args().first() {
111 println!("Getting specific pod: {pod_name}");
112 } else {
113 for pod in get_pods_in_namespace(namespace) {
114 println!("pod/{}", pod);
115 }
116 }
117
118 Ok(())
119 })
120 .build()
121}
122
123fn build_get_services() -> Command {
124 CommandBuilder::new("services")
125 .aliases(vec!["svc", "service"])
126 .short("List services")
127 .arg_completion(|ctx, prefix| {
128 let namespace = ctx
129 .flag("namespace")
130 .map(String::as_str)
131 .unwrap_or("default");
132
133 let services = get_services_in_namespace(namespace);
134 Ok(CompletionResult::new()
135 .extend(services.into_iter().filter(|svc| svc.starts_with(prefix))))
136 })
137 .run(|ctx| {
138 let namespace = ctx
139 .flag("namespace")
140 .map(String::as_str)
141 .unwrap_or("default");
142
143 println!("Listing services in namespace: {namespace}");
144 Ok(())
145 })
146 .build()
147}
148
149fn build_get_deployments() -> Command {
150 CommandBuilder::new("deployments")
151 .aliases(vec!["deploy", "deployment"])
152 .short("List deployments")
153 .run(|ctx| {
154 let namespace = ctx
155 .flag("namespace")
156 .map(String::as_str)
157 .unwrap_or("default");
158
159 println!("Listing deployments in namespace: {namespace}");
160 Ok(())
161 })
162 .build()
163}
164
165fn build_describe_command() -> Command {
166 CommandBuilder::new("describe")
167 .short("Show details of a specific resource")
168 .run(|_ctx| {
169 println!("Describe command - add resource type subcommands");
170 Ok(())
171 })
172 .build()
173}
174
175fn build_delete_command() -> Command {
176 CommandBuilder::new("delete")
177 .short("Delete resources")
178 .run(|_ctx| {
179 println!("Delete command - add resource type subcommands");
180 Ok(())
181 })
182 .build()
183}
184
185// Mock functions that would normally query the Kubernetes API
186fn get_namespaces_with_descriptions() -> Vec<(String, String)> {
187 vec![
188 (
189 "default".to_string(),
190 "Default namespace for user workloads".to_string(),
191 ),
192 (
193 "kube-system".to_string(),
194 "Kubernetes system components".to_string(),
195 ),
196 (
197 "kube-public".to_string(),
198 "Public resources accessible to all users".to_string(),
199 ),
200 (
201 "development".to_string(),
202 "Development environment".to_string(),
203 ),
204 (
205 "staging".to_string(),
206 "Staging environment for pre-production testing".to_string(),
207 ),
208 (
209 "production".to_string(),
210 "Production environment (CAUTION)".to_string(),
211 ),
212 ]
213}
214
215fn get_pods_with_status(namespace: &str) -> Vec<(String, String)> {
216 use std::time::{SystemTime, UNIX_EPOCH};
217
218 // Generate random suffix based on current time
219 let timestamp = SystemTime::now()
220 .duration_since(UNIX_EPOCH)
221 .unwrap()
222 .as_secs();
223
224 // Simple pseudo-random generation
225 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
226 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
227 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
228
229 match namespace {
230 "default" => vec![
231 (
232 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
233 "Running (2/2 containers)".to_string(),
234 ),
235 (
236 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
237 "Running (2/2 containers)".to_string(),
238 ),
239 (
240 "redis-master-0".to_string(),
241 "Running (1/1 containers)".to_string(),
242 ),
243 (
244 "redis-slave-0".to_string(),
245 "Running (1/1 containers)".to_string(),
246 ),
247 ("redis-slave-1".to_string(), "Pending".to_string()),
248 ],
249 "kube-system" => vec![
250 (
251 format!("coredns-5d78c9869d-{:05x}", rand1),
252 "Running (1/1 containers)".to_string(),
253 ),
254 (
255 format!("coredns-5d78c9869d-{:05x}", rand2),
256 "Running (1/1 containers)".to_string(),
257 ),
258 ("etcd-minikube".to_string(), "Running".to_string()),
259 ("kube-apiserver-minikube".to_string(), "Running".to_string()),
260 (
261 "kube-controller-manager-minikube".to_string(),
262 "Running".to_string(),
263 ),
264 (format!("kube-proxy-{:05x}", rand3), "Running".to_string()),
265 ("kube-scheduler-minikube".to_string(), "Running".to_string()),
266 ],
267 _ => vec![],
268 }
269}
270
271fn get_pods_in_namespace(namespace: &str) -> Vec<String> {
272 use std::time::{SystemTime, UNIX_EPOCH};
273
274 // Generate random suffix based on current time
275 let timestamp = SystemTime::now()
276 .duration_since(UNIX_EPOCH)
277 .unwrap()
278 .as_secs();
279
280 // Simple pseudo-random generation
281 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
282 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
283 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
284
285 match namespace {
286 "default" => vec![
287 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
288 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
289 format!("redis-master-0"),
290 format!("redis-slave-0"),
291 format!("redis-slave-1"),
292 ],
293 "kube-system" => vec![
294 format!("coredns-5d78c9869d-{:05x}", rand1),
295 format!("coredns-5d78c9869d-{:05x}", rand2),
296 format!("etcd-minikube"),
297 format!("kube-apiserver-minikube"),
298 format!("kube-controller-manager-minikube"),
299 format!("kube-proxy-{:05x}", rand3),
300 format!("kube-scheduler-minikube"),
301 ],
302 _ => vec![],
303 }
304}
305
306fn get_services_in_namespace(namespace: &str) -> Vec<String> {
307 match namespace {
308 "default" => vec![
309 "kubernetes".to_string(),
310 "nginx-service".to_string(),
311 "redis-master".to_string(),
312 "redis-slave".to_string(),
313 ],
314 "kube-system" => vec!["kube-dns".to_string()],
315 _ => vec![],
316 }
317}
318
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}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}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}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/memory_optimization_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 alias(self, alias: impl Into<String>) -> Self
pub fn alias(self, alias: impl Into<String>) -> Self
Adds a single alias for this command
§Examples
use flag_rs::CommandBuilder;
let cmd = CommandBuilder::new("remove")
.alias("rm")
.alias("delete")
.build();Examples found in repository?
7fn main() {
8 let app = CommandBuilder::new("helpdemo")
9 .short("A demo of enhanced help formatting")
10 .long("This is a comprehensive demonstration of the enhanced help formatting system in flag-rs. \
11 It shows how help text is beautifully formatted with proper grouping, constraint \
12 information, and visual hierarchy.")
13 .group_id("utilities")
14 .example("helpdemo config --format=yaml --output=config.yaml")
15 .example("helpdemo process --workers=4 --verbose data.json")
16 .example("helpdemo serve --port=8080 --ssl --ssl-cert=cert.pem")
17 .flag(
18 Flag::new("verbose")
19 .short('v')
20 .value_type(FlagType::Bool)
21 .usage("Enable verbose output (can be repeated for more verbosity)"),
22 )
23 .flag(
24 Flag::new("config")
25 .short('c')
26 .value_type(FlagType::File)
27 .default(FlagValue::String("~/.config/app.toml".to_string()))
28 .usage("Configuration file path"),
29 )
30 .flag(
31 Flag::new("workers")
32 .short('w')
33 .value_type(FlagType::Range(1, 16))
34 .default(FlagValue::Int(4))
35 .usage("Number of worker threads"),
36 )
37 .flag(
38 Flag::new("timeout")
39 .short('t')
40 .value_type(FlagType::Int)
41 .default(FlagValue::Int(30))
42 .usage("Request timeout in seconds"),
43 )
44 .subcommand(
45 CommandBuilder::new("config")
46 .short("Manage configuration")
47 .long("The config command allows you to view, edit, and validate configuration files. \
48 It supports multiple output formats and can merge configurations from various sources.")
49 .alias("cfg")
50 .alias("conf")
51 .example("helpdemo config show")
52 .example("helpdemo config validate --strict")
53 .flag(
54 Flag::new("format")
55 .short('f')
56 .value_type(FlagType::Choice(vec![
57 "json".to_string(),
58 "yaml".to_string(),
59 "toml".to_string(),
60 "ini".to_string(),
61 ]))
62 .default(FlagValue::String("yaml".to_string()))
63 .usage("Configuration file format"),
64 )
65 .flag(
66 Flag::new("output")
67 .short('o')
68 .value_type(FlagType::String)
69 .required()
70 .usage("Output file path"),
71 )
72 .flag(
73 Flag::new("merge")
74 .value_type(FlagType::StringArray)
75 .usage("Additional config files to merge"),
76 )
77 .flag(
78 Flag::new("strict")
79 .value_type(FlagType::Bool)
80 .usage("Enable strict validation mode"),
81 )
82 .subcommand(
83 CommandBuilder::new("show")
84 .short("Display current configuration")
85 .run(|_| Ok(()))
86 .build(),
87 )
88 .subcommand(
89 CommandBuilder::new("validate")
90 .short("Validate configuration syntax")
91 .run(|_| Ok(()))
92 .build(),
93 )
94 .build(),
95 )
96 .subcommand(
97 CommandBuilder::new("process")
98 .short("Process data files")
99 .group_id("operations")
100 .flag(
101 Flag::new("input")
102 .short('i')
103 .value_type(FlagType::File)
104 .required()
105 .usage("Input data file"),
106 )
107 .flag(
108 Flag::new("output")
109 .short('o')
110 .value_type(FlagType::String)
111 .required()
112 .usage("Output file path"),
113 )
114 .flag(
115 Flag::new("format")
116 .short('f')
117 .value_type(FlagType::Choice(vec![
118 "csv".to_string(),
119 "json".to_string(),
120 "parquet".to_string(),
121 ]))
122 .usage("Output format"),
123 )
124 .flag(
125 Flag::new("compress")
126 .value_type(FlagType::Bool)
127 .usage("Compress output"),
128 )
129 .flag(
130 Flag::new("compression-level")
131 .value_type(FlagType::Range(1, 9))
132 .default(FlagValue::Int(6))
133 .constraint(FlagConstraint::RequiredIf("compress".to_string()))
134 .usage("Compression level"),
135 )
136 .run(|_| Ok(()))
137 .build(),
138 )
139 .subcommand(
140 CommandBuilder::new("serve")
141 .short("Start the server")
142 .group_id("operations")
143 .flag(
144 Flag::new("port")
145 .short('p')
146 .value_type(FlagType::Range(1, 65535))
147 .default(FlagValue::Int(8080))
148 .usage("Port to listen on"),
149 )
150 .flag(
151 Flag::new("host")
152 .short('h')
153 .value_type(FlagType::String)
154 .default(FlagValue::String("127.0.0.1".to_string()))
155 .usage("Host to bind to"),
156 )
157 .flag(
158 Flag::new("ssl")
159 .value_type(FlagType::Bool)
160 .usage("Enable SSL/TLS"),
161 )
162 .flag(
163 Flag::new("ssl-cert")
164 .value_type(FlagType::File)
165 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
166 .usage("SSL certificate file"),
167 )
168 .flag(
169 Flag::new("ssl-key")
170 .value_type(FlagType::File)
171 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
172 .usage("SSL private key file"),
173 )
174 .flag(
175 Flag::new("http2")
176 .value_type(FlagType::Bool)
177 .constraint(FlagConstraint::Requires(vec!["ssl".to_string()]))
178 .usage("Enable HTTP/2 support"),
179 )
180 .flag(
181 Flag::new("debug")
182 .short('d')
183 .value_type(FlagType::Bool)
184 .constraint(FlagConstraint::ConflictsWith(vec!["production".to_string()]))
185 .usage("Enable debug mode"),
186 )
187 .flag(
188 Flag::new("production")
189 .value_type(FlagType::Bool)
190 .usage("Enable production mode"),
191 )
192 .run(|_| Ok(()))
193 .build(),
194 )
195 .build();
196
197 println!("This example demonstrates enhanced help formatting.");
198 println!("Try these commands to see different help layouts:\n");
199 println!(" # Main help with command groups");
200 println!(" cargo run --example help_formatting_demo -- --help\n");
201
202 println!(" # Subcommand help with required flags");
203 println!(" cargo run --example help_formatting_demo -- config --help\n");
204
205 println!(" # Subcommand with flag constraints");
206 println!(" cargo run --example help_formatting_demo -- serve --help\n");
207
208 let args: Vec<String> = std::env::args().skip(1).collect();
209 if !args.is_empty() {
210 if let Err(e) = app.execute(args) {
211 eprintln!("{}", e);
212 std::process::exit(1);
213 }
214 }
215}Sourcepub fn aliases<I, S>(self, aliases: I) -> Self
pub fn aliases<I, S>(self, aliases: I) -> Self
Adds multiple aliases for this command
§Examples
use flag_rs::CommandBuilder;
let cmd = CommandBuilder::new("remove")
.aliases(vec!["rm", "delete", "del"])
.build();Examples found in repository?
79fn build_get_pods() -> Command {
80 CommandBuilder::new("pods")
81 .aliases(vec!["po", "pod"])
82 .short("List pods")
83 .arg_completion(|ctx, prefix| {
84 // This is the key feature - dynamic completion based on runtime state!
85 let namespace = ctx
86 .flag("namespace")
87 .map(String::as_str)
88 .unwrap_or("default");
89
90 // In real kubectl, this would query the K8s API
91 let pods = get_pods_with_status(namespace);
92 let mut result = CompletionResult::new();
93
94 for (pod_name, status) in pods {
95 if pod_name.starts_with(prefix) {
96 result = result.add_with_description(pod_name, status);
97 }
98 }
99
100 Ok(result)
101 })
102 .run(|ctx| {
103 let namespace = ctx
104 .flag("namespace")
105 .map(String::as_str)
106 .unwrap_or("default");
107
108 println!("Listing pods in namespace: {namespace}");
109
110 if let Some(pod_name) = ctx.args().first() {
111 println!("Getting specific pod: {pod_name}");
112 } else {
113 for pod in get_pods_in_namespace(namespace) {
114 println!("pod/{}", pod);
115 }
116 }
117
118 Ok(())
119 })
120 .build()
121}
122
123fn build_get_services() -> Command {
124 CommandBuilder::new("services")
125 .aliases(vec!["svc", "service"])
126 .short("List services")
127 .arg_completion(|ctx, prefix| {
128 let namespace = ctx
129 .flag("namespace")
130 .map(String::as_str)
131 .unwrap_or("default");
132
133 let services = get_services_in_namespace(namespace);
134 Ok(CompletionResult::new()
135 .extend(services.into_iter().filter(|svc| svc.starts_with(prefix))))
136 })
137 .run(|ctx| {
138 let namespace = ctx
139 .flag("namespace")
140 .map(String::as_str)
141 .unwrap_or("default");
142
143 println!("Listing services in namespace: {namespace}");
144 Ok(())
145 })
146 .build()
147}
148
149fn build_get_deployments() -> Command {
150 CommandBuilder::new("deployments")
151 .aliases(vec!["deploy", "deployment"])
152 .short("List deployments")
153 .run(|ctx| {
154 let namespace = ctx
155 .flag("namespace")
156 .map(String::as_str)
157 .unwrap_or("default");
158
159 println!("Listing deployments in namespace: {namespace}");
160 Ok(())
161 })
162 .build()
163}More examples
12fn main() {
13 let app = CommandBuilder::new("error-demo")
14 .short("Demonstrates error handling")
15 .long("This example shows various error scenarios and how flag-rs handles them")
16 .subcommand(
17 CommandBuilder::new("deploy")
18 .short("Deploy the application")
19 .args(ArgValidator::ExactArgs(1))
20 .run(|ctx| {
21 let env = ctx.args().first().unwrap();
22 if !["dev", "staging", "production"].contains(&env.as_str()) {
23 return Err(Error::Validation(format!(
24 "invalid environment '{}', must be one of: dev, staging, production",
25 env
26 )));
27 }
28 println!("Deploying to {}", env);
29 Ok(())
30 })
31 .build(),
32 )
33 .subcommand(
34 CommandBuilder::new("serve")
35 .short("Start the server")
36 .flag(
37 Flag::new("port")
38 .short('p')
39 .usage("Port to listen on")
40 .value_type(FlagType::Int)
41 .required(),
42 )
43 .run(|ctx| {
44 let port = ctx.flag("port").unwrap();
45 println!("Server starting on port {}", port);
46 Ok(())
47 })
48 .build(),
49 )
50 .subcommand(
51 CommandBuilder::new("migrate")
52 .short("Run database migrations")
53 .subcommand(
54 CommandBuilder::new("up")
55 .short("Run pending migrations")
56 .run(|_| {
57 println!("Running migrations...");
58 Ok(())
59 })
60 .build(),
61 )
62 .subcommand(
63 CommandBuilder::new("down")
64 .short("Rollback migrations")
65 .args(ArgValidator::MaximumArgs(1))
66 .run(|ctx| {
67 let steps = ctx
68 .args()
69 .first()
70 .and_then(|s| s.parse::<u32>().ok())
71 .unwrap_or(1);
72 println!(
73 "Rolling back {} migration{}",
74 steps,
75 if steps == 1 { "" } else { "s" }
76 );
77 Ok(())
78 })
79 .build(),
80 )
81 .build(),
82 )
83 .subcommand(
84 CommandBuilder::new("config")
85 .short("Manage configuration")
86 .aliases(vec!["cfg", "conf"])
87 .run(|_| {
88 println!("Configuration management");
89 Ok(())
90 })
91 .build(),
92 )
93 .build();
94
95 println!("=== Error Handling Demo ===\n");
96 println!("This demo shows how flag-rs handles various error scenarios.\n");
97 println!("Try these commands to see different error types:\n");
98 println!(
99 "1. {} deploi (typo - will show suggestions)",
100 std::env::args().next().unwrap_or_default()
101 );
102 println!(
103 "2. {} deploy (missing required argument)",
104 std::env::args().next().unwrap_or_default()
105 );
106 println!(
107 "3. {} deploy dev staging (too many arguments)",
108 std::env::args().next().unwrap_or_default()
109 );
110 println!(
111 "4. {} deploy test (invalid environment)",
112 std::env::args().next().unwrap_or_default()
113 );
114 println!(
115 "5. {} serve (missing required flag)",
116 std::env::args().next().unwrap_or_default()
117 );
118 println!(
119 "6. {} serve -p abc (invalid flag value)",
120 std::env::args().next().unwrap_or_default()
121 );
122 println!(
123 "7. {} migrate (subcommand required)",
124 std::env::args().next().unwrap_or_default()
125 );
126 println!(
127 "8. {} confi (typo - will suggest 'config')",
128 std::env::args().next().unwrap_or_default()
129 );
130 println!("\n---\n");
131
132 let args: Vec<String> = std::env::args().skip(1).collect();
133 if let Err(e) = app.execute(args) {
134 eprintln!("{}", e);
135 std::process::exit(1);
136 }
137}10fn main() {
11 let app = CommandBuilder::new("kubectl")
12 .short("Kubernetes command-line tool")
13 .long("kubectl controls the Kubernetes cluster manager.\n\n\
14 Find more information at: https://kubernetes.io/docs/reference/kubectl/")
15 .example("kubectl get pods")
16 .example("kubectl apply -f deployment.yaml")
17 .example("kubectl logs -f my-pod")
18 .flag(
19 Flag::new("namespace")
20 .short('n')
21 .usage("The namespace scope for this CLI request")
22 .value_type(FlagType::String)
23 .default(flag_rs::FlagValue::String("default".to_string())),
24 )
25 .flag(
26 Flag::new("kubeconfig")
27 .usage("Path to the kubeconfig file to use for CLI requests")
28 .value_type(FlagType::String),
29 )
30 .subcommand(
31 CommandBuilder::new("get")
32 .short("Display one or many resources")
33 .group_id("Basic Commands")
34 .long("Prints a table of the most important information about the specified resources.\n\n\
35 You can filter the list using a label selector and the --selector flag. If the\n\
36 desired resource type is namespaced you will only see results in your current\n\
37 namespace unless you pass --all-namespaces.")
38 .example("kubectl get pods")
39 .example("kubectl get pods -n kube-system")
40 .example("kubectl get pods --selector=app=nginx")
41 .example("kubectl get pods,services")
42 .build(),
43 )
44 .subcommand(
45 CommandBuilder::new("apply")
46 .short("Apply a configuration to a resource by file name or stdin")
47 .group_id("Basic Commands")
48 .long("Apply a configuration to a resource by file name or stdin. The resource name must\n\
49 be specified. This resource will be created if it doesn't exist yet. To use 'apply',\n\
50 always create the resource initially with either 'apply' or 'create --save-config'.")
51 .example("kubectl apply -f ./pod.yaml")
52 .example("kubectl apply -f https://example.com/manifest.yaml")
53 .example("kubectl apply -k ./")
54 .flag(
55 Flag::new("filename")
56 .short('f')
57 .usage("Filename, directory, or URL to files to use to create the resource")
58 .value_type(FlagType::String)
59 .required(),
60 )
61 .flag(
62 Flag::new("recursive")
63 .short('R')
64 .usage("Process the directory used in -f, --filename recursively")
65 .value_type(FlagType::Bool),
66 )
67 .build(),
68 )
69 .subcommand(
70 CommandBuilder::new("delete")
71 .short("Delete resources by file names, stdin, resources and names, or by resources and label selector")
72 .aliases(vec!["del", "remove", "rm"])
73 .group_id("Basic Commands")
74 .example("kubectl delete pod my-pod")
75 .example("kubectl delete -f ./pod.yaml")
76 .example("kubectl delete pods --all")
77 .build(),
78 )
79 .subcommand(
80 CommandBuilder::new("logs")
81 .short("Print the logs for a container in a pod")
82 .group_id("Troubleshooting and Debugging Commands")
83 .long("Print the logs for a container in a pod or specified resource. If the pod has\n\
84 only one container, the container name is optional.")
85 .aliases(vec!["log"])
86 .example("kubectl logs my-pod")
87 .example("kubectl logs my-pod -c my-container")
88 .example("kubectl logs -f my-pod")
89 .example("kubectl logs --tail=20 my-pod")
90 .flag(
91 Flag::new("follow")
92 .short('f')
93 .usage("Specify if the logs should be streamed")
94 .value_type(FlagType::Bool),
95 )
96 .flag(
97 Flag::new("tail")
98 .usage("Lines of recent log file to display")
99 .value_type(FlagType::Int)
100 .default(flag_rs::FlagValue::Int(-1)),
101 )
102 .build(),
103 )
104 .subcommand(
105 CommandBuilder::new("describe")
106 .short("Show details of a specific resource or group of resources")
107 .group_id("Troubleshooting and Debugging Commands")
108 .example("kubectl describe pod my-pod")
109 .example("kubectl describe nodes")
110 .build(),
111 )
112 .subcommand(
113 CommandBuilder::new("exec")
114 .short("Execute a command in a container")
115 .group_id("Troubleshooting and Debugging Commands")
116 .example("kubectl exec -it my-pod -- /bin/bash")
117 .example("kubectl exec my-pod -- ls /app")
118 .build(),
119 )
120 .subcommand(
121 CommandBuilder::new("config")
122 .short("Modify kubeconfig files")
123 .group_id("Settings Commands")
124 .example("kubectl config view")
125 .example("kubectl config use-context my-context")
126 .build(),
127 )
128 .subcommand(
129 CommandBuilder::new("version")
130 .short("Print the client and server version information")
131 .example("kubectl version")
132 .example("kubectl version --short")
133 .build(),
134 )
135 .build();
136
137 let args: Vec<String> = std::env::args().skip(1).collect();
138 if let Err(e) = app.execute(args) {
139 eprintln!("{}", e);
140 std::process::exit(1);
141 }
142}Sourcepub fn short(self, short: impl Into<String>) -> Self
pub fn short(self, short: impl Into<String>) -> Self
Sets the short description for this command
The short description is shown in the parent command’s help output.
Examples found in repository?
50fn create_simple_cli() -> flag_rs::Command {
51 CommandBuilder::new("bench")
52 .short("Benchmark CLI")
53 .flag(
54 Flag::new("verbose")
55 .short('v')
56 .value_type(FlagType::Bool)
57 .default(FlagValue::Bool(false)),
58 )
59 .flag(Flag::new("output").short('o').value_type(FlagType::String))
60 .build()
61}
62
63/// Creates a complex nested command structure
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
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}
78
79fn build_get_pods() -> Command {
80 CommandBuilder::new("pods")
81 .aliases(vec!["po", "pod"])
82 .short("List pods")
83 .arg_completion(|ctx, prefix| {
84 // This is the key feature - dynamic completion based on runtime state!
85 let namespace = ctx
86 .flag("namespace")
87 .map(String::as_str)
88 .unwrap_or("default");
89
90 // In real kubectl, this would query the K8s API
91 let pods = get_pods_with_status(namespace);
92 let mut result = CompletionResult::new();
93
94 for (pod_name, status) in pods {
95 if pod_name.starts_with(prefix) {
96 result = result.add_with_description(pod_name, status);
97 }
98 }
99
100 Ok(result)
101 })
102 .run(|ctx| {
103 let namespace = ctx
104 .flag("namespace")
105 .map(String::as_str)
106 .unwrap_or("default");
107
108 println!("Listing pods in namespace: {namespace}");
109
110 if let Some(pod_name) = ctx.args().first() {
111 println!("Getting specific pod: {pod_name}");
112 } else {
113 for pod in get_pods_in_namespace(namespace) {
114 println!("pod/{}", pod);
115 }
116 }
117
118 Ok(())
119 })
120 .build()
121}
122
123fn build_get_services() -> Command {
124 CommandBuilder::new("services")
125 .aliases(vec!["svc", "service"])
126 .short("List services")
127 .arg_completion(|ctx, prefix| {
128 let namespace = ctx
129 .flag("namespace")
130 .map(String::as_str)
131 .unwrap_or("default");
132
133 let services = get_services_in_namespace(namespace);
134 Ok(CompletionResult::new()
135 .extend(services.into_iter().filter(|svc| svc.starts_with(prefix))))
136 })
137 .run(|ctx| {
138 let namespace = ctx
139 .flag("namespace")
140 .map(String::as_str)
141 .unwrap_or("default");
142
143 println!("Listing services in namespace: {namespace}");
144 Ok(())
145 })
146 .build()
147}
148
149fn build_get_deployments() -> Command {
150 CommandBuilder::new("deployments")
151 .aliases(vec!["deploy", "deployment"])
152 .short("List deployments")
153 .run(|ctx| {
154 let namespace = ctx
155 .flag("namespace")
156 .map(String::as_str)
157 .unwrap_or("default");
158
159 println!("Listing deployments in namespace: {namespace}");
160 Ok(())
161 })
162 .build()
163}
164
165fn build_describe_command() -> Command {
166 CommandBuilder::new("describe")
167 .short("Show details of a specific resource")
168 .run(|_ctx| {
169 println!("Describe command - add resource type subcommands");
170 Ok(())
171 })
172 .build()
173}
174
175fn build_delete_command() -> Command {
176 CommandBuilder::new("delete")
177 .short("Delete resources")
178 .run(|_ctx| {
179 println!("Delete command - add resource type subcommands");
180 Ok(())
181 })
182 .build()
183}
184
185// Mock functions that would normally query the Kubernetes API
186fn get_namespaces_with_descriptions() -> Vec<(String, String)> {
187 vec![
188 (
189 "default".to_string(),
190 "Default namespace for user workloads".to_string(),
191 ),
192 (
193 "kube-system".to_string(),
194 "Kubernetes system components".to_string(),
195 ),
196 (
197 "kube-public".to_string(),
198 "Public resources accessible to all users".to_string(),
199 ),
200 (
201 "development".to_string(),
202 "Development environment".to_string(),
203 ),
204 (
205 "staging".to_string(),
206 "Staging environment for pre-production testing".to_string(),
207 ),
208 (
209 "production".to_string(),
210 "Production environment (CAUTION)".to_string(),
211 ),
212 ]
213}
214
215fn get_pods_with_status(namespace: &str) -> Vec<(String, String)> {
216 use std::time::{SystemTime, UNIX_EPOCH};
217
218 // Generate random suffix based on current time
219 let timestamp = SystemTime::now()
220 .duration_since(UNIX_EPOCH)
221 .unwrap()
222 .as_secs();
223
224 // Simple pseudo-random generation
225 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
226 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
227 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
228
229 match namespace {
230 "default" => vec![
231 (
232 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
233 "Running (2/2 containers)".to_string(),
234 ),
235 (
236 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
237 "Running (2/2 containers)".to_string(),
238 ),
239 (
240 "redis-master-0".to_string(),
241 "Running (1/1 containers)".to_string(),
242 ),
243 (
244 "redis-slave-0".to_string(),
245 "Running (1/1 containers)".to_string(),
246 ),
247 ("redis-slave-1".to_string(), "Pending".to_string()),
248 ],
249 "kube-system" => vec![
250 (
251 format!("coredns-5d78c9869d-{:05x}", rand1),
252 "Running (1/1 containers)".to_string(),
253 ),
254 (
255 format!("coredns-5d78c9869d-{:05x}", rand2),
256 "Running (1/1 containers)".to_string(),
257 ),
258 ("etcd-minikube".to_string(), "Running".to_string()),
259 ("kube-apiserver-minikube".to_string(), "Running".to_string()),
260 (
261 "kube-controller-manager-minikube".to_string(),
262 "Running".to_string(),
263 ),
264 (format!("kube-proxy-{:05x}", rand3), "Running".to_string()),
265 ("kube-scheduler-minikube".to_string(), "Running".to_string()),
266 ],
267 _ => vec![],
268 }
269}
270
271fn get_pods_in_namespace(namespace: &str) -> Vec<String> {
272 use std::time::{SystemTime, UNIX_EPOCH};
273
274 // Generate random suffix based on current time
275 let timestamp = SystemTime::now()
276 .duration_since(UNIX_EPOCH)
277 .unwrap()
278 .as_secs();
279
280 // Simple pseudo-random generation
281 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
282 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
283 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
284
285 match namespace {
286 "default" => vec![
287 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
288 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
289 format!("redis-master-0"),
290 format!("redis-slave-0"),
291 format!("redis-slave-1"),
292 ],
293 "kube-system" => vec![
294 format!("coredns-5d78c9869d-{:05x}", rand1),
295 format!("coredns-5d78c9869d-{:05x}", rand2),
296 format!("etcd-minikube"),
297 format!("kube-apiserver-minikube"),
298 format!("kube-controller-manager-minikube"),
299 format!("kube-proxy-{:05x}", rand3),
300 format!("kube-scheduler-minikube"),
301 ],
302 _ => vec![],
303 }
304}
305
306fn get_services_in_namespace(namespace: &str) -> Vec<String> {
307 match namespace {
308 "default" => vec![
309 "kubernetes".to_string(),
310 "nginx-service".to_string(),
311 "redis-master".to_string(),
312 "redis-slave".to_string(),
313 ],
314 "kube-system" => vec!["kube-dns".to_string()],
315 _ => vec![],
316 }
317}
318
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}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}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}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/memory_optimization_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 long(self, long: impl Into<String>) -> Self
pub fn long(self, long: impl Into<String>) -> Self
Sets the long description for this command
The long description is shown in this command’s help output.
Examples found in repository?
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}
78
79fn build_get_pods() -> Command {
80 CommandBuilder::new("pods")
81 .aliases(vec!["po", "pod"])
82 .short("List pods")
83 .arg_completion(|ctx, prefix| {
84 // This is the key feature - dynamic completion based on runtime state!
85 let namespace = ctx
86 .flag("namespace")
87 .map(String::as_str)
88 .unwrap_or("default");
89
90 // In real kubectl, this would query the K8s API
91 let pods = get_pods_with_status(namespace);
92 let mut result = CompletionResult::new();
93
94 for (pod_name, status) in pods {
95 if pod_name.starts_with(prefix) {
96 result = result.add_with_description(pod_name, status);
97 }
98 }
99
100 Ok(result)
101 })
102 .run(|ctx| {
103 let namespace = ctx
104 .flag("namespace")
105 .map(String::as_str)
106 .unwrap_or("default");
107
108 println!("Listing pods in namespace: {namespace}");
109
110 if let Some(pod_name) = ctx.args().first() {
111 println!("Getting specific pod: {pod_name}");
112 } else {
113 for pod in get_pods_in_namespace(namespace) {
114 println!("pod/{}", pod);
115 }
116 }
117
118 Ok(())
119 })
120 .build()
121}
122
123fn build_get_services() -> Command {
124 CommandBuilder::new("services")
125 .aliases(vec!["svc", "service"])
126 .short("List services")
127 .arg_completion(|ctx, prefix| {
128 let namespace = ctx
129 .flag("namespace")
130 .map(String::as_str)
131 .unwrap_or("default");
132
133 let services = get_services_in_namespace(namespace);
134 Ok(CompletionResult::new()
135 .extend(services.into_iter().filter(|svc| svc.starts_with(prefix))))
136 })
137 .run(|ctx| {
138 let namespace = ctx
139 .flag("namespace")
140 .map(String::as_str)
141 .unwrap_or("default");
142
143 println!("Listing services in namespace: {namespace}");
144 Ok(())
145 })
146 .build()
147}
148
149fn build_get_deployments() -> Command {
150 CommandBuilder::new("deployments")
151 .aliases(vec!["deploy", "deployment"])
152 .short("List deployments")
153 .run(|ctx| {
154 let namespace = ctx
155 .flag("namespace")
156 .map(String::as_str)
157 .unwrap_or("default");
158
159 println!("Listing deployments in namespace: {namespace}");
160 Ok(())
161 })
162 .build()
163}
164
165fn build_describe_command() -> Command {
166 CommandBuilder::new("describe")
167 .short("Show details of a specific resource")
168 .run(|_ctx| {
169 println!("Describe command - add resource type subcommands");
170 Ok(())
171 })
172 .build()
173}
174
175fn build_delete_command() -> Command {
176 CommandBuilder::new("delete")
177 .short("Delete resources")
178 .run(|_ctx| {
179 println!("Delete command - add resource type subcommands");
180 Ok(())
181 })
182 .build()
183}
184
185// Mock functions that would normally query the Kubernetes API
186fn get_namespaces_with_descriptions() -> Vec<(String, String)> {
187 vec![
188 (
189 "default".to_string(),
190 "Default namespace for user workloads".to_string(),
191 ),
192 (
193 "kube-system".to_string(),
194 "Kubernetes system components".to_string(),
195 ),
196 (
197 "kube-public".to_string(),
198 "Public resources accessible to all users".to_string(),
199 ),
200 (
201 "development".to_string(),
202 "Development environment".to_string(),
203 ),
204 (
205 "staging".to_string(),
206 "Staging environment for pre-production testing".to_string(),
207 ),
208 (
209 "production".to_string(),
210 "Production environment (CAUTION)".to_string(),
211 ),
212 ]
213}
214
215fn get_pods_with_status(namespace: &str) -> Vec<(String, String)> {
216 use std::time::{SystemTime, UNIX_EPOCH};
217
218 // Generate random suffix based on current time
219 let timestamp = SystemTime::now()
220 .duration_since(UNIX_EPOCH)
221 .unwrap()
222 .as_secs();
223
224 // Simple pseudo-random generation
225 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
226 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
227 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
228
229 match namespace {
230 "default" => vec![
231 (
232 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
233 "Running (2/2 containers)".to_string(),
234 ),
235 (
236 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
237 "Running (2/2 containers)".to_string(),
238 ),
239 (
240 "redis-master-0".to_string(),
241 "Running (1/1 containers)".to_string(),
242 ),
243 (
244 "redis-slave-0".to_string(),
245 "Running (1/1 containers)".to_string(),
246 ),
247 ("redis-slave-1".to_string(), "Pending".to_string()),
248 ],
249 "kube-system" => vec![
250 (
251 format!("coredns-5d78c9869d-{:05x}", rand1),
252 "Running (1/1 containers)".to_string(),
253 ),
254 (
255 format!("coredns-5d78c9869d-{:05x}", rand2),
256 "Running (1/1 containers)".to_string(),
257 ),
258 ("etcd-minikube".to_string(), "Running".to_string()),
259 ("kube-apiserver-minikube".to_string(), "Running".to_string()),
260 (
261 "kube-controller-manager-minikube".to_string(),
262 "Running".to_string(),
263 ),
264 (format!("kube-proxy-{:05x}", rand3), "Running".to_string()),
265 ("kube-scheduler-minikube".to_string(), "Running".to_string()),
266 ],
267 _ => vec![],
268 }
269}
270
271fn get_pods_in_namespace(namespace: &str) -> Vec<String> {
272 use std::time::{SystemTime, UNIX_EPOCH};
273
274 // Generate random suffix based on current time
275 let timestamp = SystemTime::now()
276 .duration_since(UNIX_EPOCH)
277 .unwrap()
278 .as_secs();
279
280 // Simple pseudo-random generation
281 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
282 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
283 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
284
285 match namespace {
286 "default" => vec![
287 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
288 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
289 format!("redis-master-0"),
290 format!("redis-slave-0"),
291 format!("redis-slave-1"),
292 ],
293 "kube-system" => vec![
294 format!("coredns-5d78c9869d-{:05x}", rand1),
295 format!("coredns-5d78c9869d-{:05x}", rand2),
296 format!("etcd-minikube"),
297 format!("kube-apiserver-minikube"),
298 format!("kube-controller-manager-minikube"),
299 format!("kube-proxy-{:05x}", rand3),
300 format!("kube-scheduler-minikube"),
301 ],
302 _ => vec![],
303 }
304}
305
306fn get_services_in_namespace(namespace: &str) -> Vec<String> {
307 match namespace {
308 "default" => vec![
309 "kubernetes".to_string(),
310 "nginx-service".to_string(),
311 "redis-master".to_string(),
312 "redis-slave".to_string(),
313 ],
314 "kube-system" => vec!["kube-dns".to_string()],
315 _ => vec![],
316 }
317}
318
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}More examples
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}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}12fn main() {
13 let app = CommandBuilder::new("error-demo")
14 .short("Demonstrates error handling")
15 .long("This example shows various error scenarios and how flag-rs handles them")
16 .subcommand(
17 CommandBuilder::new("deploy")
18 .short("Deploy the application")
19 .args(ArgValidator::ExactArgs(1))
20 .run(|ctx| {
21 let env = ctx.args().first().unwrap();
22 if !["dev", "staging", "production"].contains(&env.as_str()) {
23 return Err(Error::Validation(format!(
24 "invalid environment '{}', must be one of: dev, staging, production",
25 env
26 )));
27 }
28 println!("Deploying to {}", env);
29 Ok(())
30 })
31 .build(),
32 )
33 .subcommand(
34 CommandBuilder::new("serve")
35 .short("Start the server")
36 .flag(
37 Flag::new("port")
38 .short('p')
39 .usage("Port to listen on")
40 .value_type(FlagType::Int)
41 .required(),
42 )
43 .run(|ctx| {
44 let port = ctx.flag("port").unwrap();
45 println!("Server starting on port {}", port);
46 Ok(())
47 })
48 .build(),
49 )
50 .subcommand(
51 CommandBuilder::new("migrate")
52 .short("Run database migrations")
53 .subcommand(
54 CommandBuilder::new("up")
55 .short("Run pending migrations")
56 .run(|_| {
57 println!("Running migrations...");
58 Ok(())
59 })
60 .build(),
61 )
62 .subcommand(
63 CommandBuilder::new("down")
64 .short("Rollback migrations")
65 .args(ArgValidator::MaximumArgs(1))
66 .run(|ctx| {
67 let steps = ctx
68 .args()
69 .first()
70 .and_then(|s| s.parse::<u32>().ok())
71 .unwrap_or(1);
72 println!(
73 "Rolling back {} migration{}",
74 steps,
75 if steps == 1 { "" } else { "s" }
76 );
77 Ok(())
78 })
79 .build(),
80 )
81 .build(),
82 )
83 .subcommand(
84 CommandBuilder::new("config")
85 .short("Manage configuration")
86 .aliases(vec!["cfg", "conf"])
87 .run(|_| {
88 println!("Configuration management");
89 Ok(())
90 })
91 .build(),
92 )
93 .build();
94
95 println!("=== Error Handling Demo ===\n");
96 println!("This demo shows how flag-rs handles various error scenarios.\n");
97 println!("Try these commands to see different error types:\n");
98 println!(
99 "1. {} deploi (typo - will show suggestions)",
100 std::env::args().next().unwrap_or_default()
101 );
102 println!(
103 "2. {} deploy (missing required argument)",
104 std::env::args().next().unwrap_or_default()
105 );
106 println!(
107 "3. {} deploy dev staging (too many arguments)",
108 std::env::args().next().unwrap_or_default()
109 );
110 println!(
111 "4. {} deploy test (invalid environment)",
112 std::env::args().next().unwrap_or_default()
113 );
114 println!(
115 "5. {} serve (missing required flag)",
116 std::env::args().next().unwrap_or_default()
117 );
118 println!(
119 "6. {} serve -p abc (invalid flag value)",
120 std::env::args().next().unwrap_or_default()
121 );
122 println!(
123 "7. {} migrate (subcommand required)",
124 std::env::args().next().unwrap_or_default()
125 );
126 println!(
127 "8. {} confi (typo - will suggest 'config')",
128 std::env::args().next().unwrap_or_default()
129 );
130 println!("\n---\n");
131
132 let args: Vec<String> = std::env::args().skip(1).collect();
133 if let Err(e) = app.execute(args) {
134 eprintln!("{}", e);
135 std::process::exit(1);
136 }
137}10fn main() {
11 let app = CommandBuilder::new("kubectl")
12 .short("Kubernetes command-line tool")
13 .long("kubectl controls the Kubernetes cluster manager.\n\n\
14 Find more information at: https://kubernetes.io/docs/reference/kubectl/")
15 .example("kubectl get pods")
16 .example("kubectl apply -f deployment.yaml")
17 .example("kubectl logs -f my-pod")
18 .flag(
19 Flag::new("namespace")
20 .short('n')
21 .usage("The namespace scope for this CLI request")
22 .value_type(FlagType::String)
23 .default(flag_rs::FlagValue::String("default".to_string())),
24 )
25 .flag(
26 Flag::new("kubeconfig")
27 .usage("Path to the kubeconfig file to use for CLI requests")
28 .value_type(FlagType::String),
29 )
30 .subcommand(
31 CommandBuilder::new("get")
32 .short("Display one or many resources")
33 .group_id("Basic Commands")
34 .long("Prints a table of the most important information about the specified resources.\n\n\
35 You can filter the list using a label selector and the --selector flag. If the\n\
36 desired resource type is namespaced you will only see results in your current\n\
37 namespace unless you pass --all-namespaces.")
38 .example("kubectl get pods")
39 .example("kubectl get pods -n kube-system")
40 .example("kubectl get pods --selector=app=nginx")
41 .example("kubectl get pods,services")
42 .build(),
43 )
44 .subcommand(
45 CommandBuilder::new("apply")
46 .short("Apply a configuration to a resource by file name or stdin")
47 .group_id("Basic Commands")
48 .long("Apply a configuration to a resource by file name or stdin. The resource name must\n\
49 be specified. This resource will be created if it doesn't exist yet. To use 'apply',\n\
50 always create the resource initially with either 'apply' or 'create --save-config'.")
51 .example("kubectl apply -f ./pod.yaml")
52 .example("kubectl apply -f https://example.com/manifest.yaml")
53 .example("kubectl apply -k ./")
54 .flag(
55 Flag::new("filename")
56 .short('f')
57 .usage("Filename, directory, or URL to files to use to create the resource")
58 .value_type(FlagType::String)
59 .required(),
60 )
61 .flag(
62 Flag::new("recursive")
63 .short('R')
64 .usage("Process the directory used in -f, --filename recursively")
65 .value_type(FlagType::Bool),
66 )
67 .build(),
68 )
69 .subcommand(
70 CommandBuilder::new("delete")
71 .short("Delete resources by file names, stdin, resources and names, or by resources and label selector")
72 .aliases(vec!["del", "remove", "rm"])
73 .group_id("Basic Commands")
74 .example("kubectl delete pod my-pod")
75 .example("kubectl delete -f ./pod.yaml")
76 .example("kubectl delete pods --all")
77 .build(),
78 )
79 .subcommand(
80 CommandBuilder::new("logs")
81 .short("Print the logs for a container in a pod")
82 .group_id("Troubleshooting and Debugging Commands")
83 .long("Print the logs for a container in a pod or specified resource. If the pod has\n\
84 only one container, the container name is optional.")
85 .aliases(vec!["log"])
86 .example("kubectl logs my-pod")
87 .example("kubectl logs my-pod -c my-container")
88 .example("kubectl logs -f my-pod")
89 .example("kubectl logs --tail=20 my-pod")
90 .flag(
91 Flag::new("follow")
92 .short('f')
93 .usage("Specify if the logs should be streamed")
94 .value_type(FlagType::Bool),
95 )
96 .flag(
97 Flag::new("tail")
98 .usage("Lines of recent log file to display")
99 .value_type(FlagType::Int)
100 .default(flag_rs::FlagValue::Int(-1)),
101 )
102 .build(),
103 )
104 .subcommand(
105 CommandBuilder::new("describe")
106 .short("Show details of a specific resource or group of resources")
107 .group_id("Troubleshooting and Debugging Commands")
108 .example("kubectl describe pod my-pod")
109 .example("kubectl describe nodes")
110 .build(),
111 )
112 .subcommand(
113 CommandBuilder::new("exec")
114 .short("Execute a command in a container")
115 .group_id("Troubleshooting and Debugging Commands")
116 .example("kubectl exec -it my-pod -- /bin/bash")
117 .example("kubectl exec my-pod -- ls /app")
118 .build(),
119 )
120 .subcommand(
121 CommandBuilder::new("config")
122 .short("Modify kubeconfig files")
123 .group_id("Settings Commands")
124 .example("kubectl config view")
125 .example("kubectl config use-context my-context")
126 .build(),
127 )
128 .subcommand(
129 CommandBuilder::new("version")
130 .short("Print the client and server version information")
131 .example("kubectl version")
132 .example("kubectl version --short")
133 .build(),
134 )
135 .build();
136
137 let args: Vec<String> = std::env::args().skip(1).collect();
138 if let Err(e) = app.execute(args) {
139 eprintln!("{}", e);
140 std::process::exit(1);
141 }
142}Sourcepub fn example(self, example: impl Into<String>) -> Self
pub fn example(self, example: impl Into<String>) -> Self
Adds an example for this command
Examples are shown in the help output to demonstrate command usage.
§Examples
use flag_rs::CommandBuilder;
let cmd = CommandBuilder::new("deploy")
.short("Deploy the application")
.example("deploy --env production")
.example("deploy --env staging --dry-run")
.build();Examples found in repository?
10fn main() {
11 let app = CommandBuilder::new("kubectl")
12 .short("Kubernetes command-line tool")
13 .long("kubectl controls the Kubernetes cluster manager.\n\n\
14 Find more information at: https://kubernetes.io/docs/reference/kubectl/")
15 .example("kubectl get pods")
16 .example("kubectl apply -f deployment.yaml")
17 .example("kubectl logs -f my-pod")
18 .flag(
19 Flag::new("namespace")
20 .short('n')
21 .usage("The namespace scope for this CLI request")
22 .value_type(FlagType::String)
23 .default(flag_rs::FlagValue::String("default".to_string())),
24 )
25 .flag(
26 Flag::new("kubeconfig")
27 .usage("Path to the kubeconfig file to use for CLI requests")
28 .value_type(FlagType::String),
29 )
30 .subcommand(
31 CommandBuilder::new("get")
32 .short("Display one or many resources")
33 .group_id("Basic Commands")
34 .long("Prints a table of the most important information about the specified resources.\n\n\
35 You can filter the list using a label selector and the --selector flag. If the\n\
36 desired resource type is namespaced you will only see results in your current\n\
37 namespace unless you pass --all-namespaces.")
38 .example("kubectl get pods")
39 .example("kubectl get pods -n kube-system")
40 .example("kubectl get pods --selector=app=nginx")
41 .example("kubectl get pods,services")
42 .build(),
43 )
44 .subcommand(
45 CommandBuilder::new("apply")
46 .short("Apply a configuration to a resource by file name or stdin")
47 .group_id("Basic Commands")
48 .long("Apply a configuration to a resource by file name or stdin. The resource name must\n\
49 be specified. This resource will be created if it doesn't exist yet. To use 'apply',\n\
50 always create the resource initially with either 'apply' or 'create --save-config'.")
51 .example("kubectl apply -f ./pod.yaml")
52 .example("kubectl apply -f https://example.com/manifest.yaml")
53 .example("kubectl apply -k ./")
54 .flag(
55 Flag::new("filename")
56 .short('f')
57 .usage("Filename, directory, or URL to files to use to create the resource")
58 .value_type(FlagType::String)
59 .required(),
60 )
61 .flag(
62 Flag::new("recursive")
63 .short('R')
64 .usage("Process the directory used in -f, --filename recursively")
65 .value_type(FlagType::Bool),
66 )
67 .build(),
68 )
69 .subcommand(
70 CommandBuilder::new("delete")
71 .short("Delete resources by file names, stdin, resources and names, or by resources and label selector")
72 .aliases(vec!["del", "remove", "rm"])
73 .group_id("Basic Commands")
74 .example("kubectl delete pod my-pod")
75 .example("kubectl delete -f ./pod.yaml")
76 .example("kubectl delete pods --all")
77 .build(),
78 )
79 .subcommand(
80 CommandBuilder::new("logs")
81 .short("Print the logs for a container in a pod")
82 .group_id("Troubleshooting and Debugging Commands")
83 .long("Print the logs for a container in a pod or specified resource. If the pod has\n\
84 only one container, the container name is optional.")
85 .aliases(vec!["log"])
86 .example("kubectl logs my-pod")
87 .example("kubectl logs my-pod -c my-container")
88 .example("kubectl logs -f my-pod")
89 .example("kubectl logs --tail=20 my-pod")
90 .flag(
91 Flag::new("follow")
92 .short('f')
93 .usage("Specify if the logs should be streamed")
94 .value_type(FlagType::Bool),
95 )
96 .flag(
97 Flag::new("tail")
98 .usage("Lines of recent log file to display")
99 .value_type(FlagType::Int)
100 .default(flag_rs::FlagValue::Int(-1)),
101 )
102 .build(),
103 )
104 .subcommand(
105 CommandBuilder::new("describe")
106 .short("Show details of a specific resource or group of resources")
107 .group_id("Troubleshooting and Debugging Commands")
108 .example("kubectl describe pod my-pod")
109 .example("kubectl describe nodes")
110 .build(),
111 )
112 .subcommand(
113 CommandBuilder::new("exec")
114 .short("Execute a command in a container")
115 .group_id("Troubleshooting and Debugging Commands")
116 .example("kubectl exec -it my-pod -- /bin/bash")
117 .example("kubectl exec my-pod -- ls /app")
118 .build(),
119 )
120 .subcommand(
121 CommandBuilder::new("config")
122 .short("Modify kubeconfig files")
123 .group_id("Settings Commands")
124 .example("kubectl config view")
125 .example("kubectl config use-context my-context")
126 .build(),
127 )
128 .subcommand(
129 CommandBuilder::new("version")
130 .short("Print the client and server version information")
131 .example("kubectl version")
132 .example("kubectl version --short")
133 .build(),
134 )
135 .build();
136
137 let args: Vec<String> = std::env::args().skip(1).collect();
138 if let Err(e) = app.execute(args) {
139 eprintln!("{}", e);
140 std::process::exit(1);
141 }
142}More examples
7fn main() {
8 let app = CommandBuilder::new("helpdemo")
9 .short("A demo of enhanced help formatting")
10 .long("This is a comprehensive demonstration of the enhanced help formatting system in flag-rs. \
11 It shows how help text is beautifully formatted with proper grouping, constraint \
12 information, and visual hierarchy.")
13 .group_id("utilities")
14 .example("helpdemo config --format=yaml --output=config.yaml")
15 .example("helpdemo process --workers=4 --verbose data.json")
16 .example("helpdemo serve --port=8080 --ssl --ssl-cert=cert.pem")
17 .flag(
18 Flag::new("verbose")
19 .short('v')
20 .value_type(FlagType::Bool)
21 .usage("Enable verbose output (can be repeated for more verbosity)"),
22 )
23 .flag(
24 Flag::new("config")
25 .short('c')
26 .value_type(FlagType::File)
27 .default(FlagValue::String("~/.config/app.toml".to_string()))
28 .usage("Configuration file path"),
29 )
30 .flag(
31 Flag::new("workers")
32 .short('w')
33 .value_type(FlagType::Range(1, 16))
34 .default(FlagValue::Int(4))
35 .usage("Number of worker threads"),
36 )
37 .flag(
38 Flag::new("timeout")
39 .short('t')
40 .value_type(FlagType::Int)
41 .default(FlagValue::Int(30))
42 .usage("Request timeout in seconds"),
43 )
44 .subcommand(
45 CommandBuilder::new("config")
46 .short("Manage configuration")
47 .long("The config command allows you to view, edit, and validate configuration files. \
48 It supports multiple output formats and can merge configurations from various sources.")
49 .alias("cfg")
50 .alias("conf")
51 .example("helpdemo config show")
52 .example("helpdemo config validate --strict")
53 .flag(
54 Flag::new("format")
55 .short('f')
56 .value_type(FlagType::Choice(vec![
57 "json".to_string(),
58 "yaml".to_string(),
59 "toml".to_string(),
60 "ini".to_string(),
61 ]))
62 .default(FlagValue::String("yaml".to_string()))
63 .usage("Configuration file format"),
64 )
65 .flag(
66 Flag::new("output")
67 .short('o')
68 .value_type(FlagType::String)
69 .required()
70 .usage("Output file path"),
71 )
72 .flag(
73 Flag::new("merge")
74 .value_type(FlagType::StringArray)
75 .usage("Additional config files to merge"),
76 )
77 .flag(
78 Flag::new("strict")
79 .value_type(FlagType::Bool)
80 .usage("Enable strict validation mode"),
81 )
82 .subcommand(
83 CommandBuilder::new("show")
84 .short("Display current configuration")
85 .run(|_| Ok(()))
86 .build(),
87 )
88 .subcommand(
89 CommandBuilder::new("validate")
90 .short("Validate configuration syntax")
91 .run(|_| Ok(()))
92 .build(),
93 )
94 .build(),
95 )
96 .subcommand(
97 CommandBuilder::new("process")
98 .short("Process data files")
99 .group_id("operations")
100 .flag(
101 Flag::new("input")
102 .short('i')
103 .value_type(FlagType::File)
104 .required()
105 .usage("Input data file"),
106 )
107 .flag(
108 Flag::new("output")
109 .short('o')
110 .value_type(FlagType::String)
111 .required()
112 .usage("Output file path"),
113 )
114 .flag(
115 Flag::new("format")
116 .short('f')
117 .value_type(FlagType::Choice(vec![
118 "csv".to_string(),
119 "json".to_string(),
120 "parquet".to_string(),
121 ]))
122 .usage("Output format"),
123 )
124 .flag(
125 Flag::new("compress")
126 .value_type(FlagType::Bool)
127 .usage("Compress output"),
128 )
129 .flag(
130 Flag::new("compression-level")
131 .value_type(FlagType::Range(1, 9))
132 .default(FlagValue::Int(6))
133 .constraint(FlagConstraint::RequiredIf("compress".to_string()))
134 .usage("Compression level"),
135 )
136 .run(|_| Ok(()))
137 .build(),
138 )
139 .subcommand(
140 CommandBuilder::new("serve")
141 .short("Start the server")
142 .group_id("operations")
143 .flag(
144 Flag::new("port")
145 .short('p')
146 .value_type(FlagType::Range(1, 65535))
147 .default(FlagValue::Int(8080))
148 .usage("Port to listen on"),
149 )
150 .flag(
151 Flag::new("host")
152 .short('h')
153 .value_type(FlagType::String)
154 .default(FlagValue::String("127.0.0.1".to_string()))
155 .usage("Host to bind to"),
156 )
157 .flag(
158 Flag::new("ssl")
159 .value_type(FlagType::Bool)
160 .usage("Enable SSL/TLS"),
161 )
162 .flag(
163 Flag::new("ssl-cert")
164 .value_type(FlagType::File)
165 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
166 .usage("SSL certificate file"),
167 )
168 .flag(
169 Flag::new("ssl-key")
170 .value_type(FlagType::File)
171 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
172 .usage("SSL private key file"),
173 )
174 .flag(
175 Flag::new("http2")
176 .value_type(FlagType::Bool)
177 .constraint(FlagConstraint::Requires(vec!["ssl".to_string()]))
178 .usage("Enable HTTP/2 support"),
179 )
180 .flag(
181 Flag::new("debug")
182 .short('d')
183 .value_type(FlagType::Bool)
184 .constraint(FlagConstraint::ConflictsWith(vec!["production".to_string()]))
185 .usage("Enable debug mode"),
186 )
187 .flag(
188 Flag::new("production")
189 .value_type(FlagType::Bool)
190 .usage("Enable production mode"),
191 )
192 .run(|_| Ok(()))
193 .build(),
194 )
195 .build();
196
197 println!("This example demonstrates enhanced help formatting.");
198 println!("Try these commands to see different help layouts:\n");
199 println!(" # Main help with command groups");
200 println!(" cargo run --example help_formatting_demo -- --help\n");
201
202 println!(" # Subcommand help with required flags");
203 println!(" cargo run --example help_formatting_demo -- config --help\n");
204
205 println!(" # Subcommand with flag constraints");
206 println!(" cargo run --example help_formatting_demo -- serve --help\n");
207
208 let args: Vec<String> = std::env::args().skip(1).collect();
209 if !args.is_empty() {
210 if let Err(e) = app.execute(args) {
211 eprintln!("{}", e);
212 std::process::exit(1);
213 }
214 }
215}Sourcepub fn group_id(self, group_id: impl Into<String>) -> Self
pub fn group_id(self, group_id: impl Into<String>) -> Self
Sets the group ID for this command
Commands with the same group ID will be displayed together in help output.
§Examples
use flag_rs::CommandBuilder;
let app = CommandBuilder::new("kubectl")
.subcommand(
CommandBuilder::new("get")
.short("Display resources")
.group_id("Basic Commands")
.build()
)
.subcommand(
CommandBuilder::new("create")
.short("Create resources")
.group_id("Basic Commands")
.build()
)
.subcommand(
CommandBuilder::new("config")
.short("Modify kubeconfig files")
.group_id("Settings Commands")
.build()
)
.build();Examples found in repository?
10fn main() {
11 let app = CommandBuilder::new("kubectl")
12 .short("Kubernetes command-line tool")
13 .long("kubectl controls the Kubernetes cluster manager.\n\n\
14 Find more information at: https://kubernetes.io/docs/reference/kubectl/")
15 .example("kubectl get pods")
16 .example("kubectl apply -f deployment.yaml")
17 .example("kubectl logs -f my-pod")
18 .flag(
19 Flag::new("namespace")
20 .short('n')
21 .usage("The namespace scope for this CLI request")
22 .value_type(FlagType::String)
23 .default(flag_rs::FlagValue::String("default".to_string())),
24 )
25 .flag(
26 Flag::new("kubeconfig")
27 .usage("Path to the kubeconfig file to use for CLI requests")
28 .value_type(FlagType::String),
29 )
30 .subcommand(
31 CommandBuilder::new("get")
32 .short("Display one or many resources")
33 .group_id("Basic Commands")
34 .long("Prints a table of the most important information about the specified resources.\n\n\
35 You can filter the list using a label selector and the --selector flag. If the\n\
36 desired resource type is namespaced you will only see results in your current\n\
37 namespace unless you pass --all-namespaces.")
38 .example("kubectl get pods")
39 .example("kubectl get pods -n kube-system")
40 .example("kubectl get pods --selector=app=nginx")
41 .example("kubectl get pods,services")
42 .build(),
43 )
44 .subcommand(
45 CommandBuilder::new("apply")
46 .short("Apply a configuration to a resource by file name or stdin")
47 .group_id("Basic Commands")
48 .long("Apply a configuration to a resource by file name or stdin. The resource name must\n\
49 be specified. This resource will be created if it doesn't exist yet. To use 'apply',\n\
50 always create the resource initially with either 'apply' or 'create --save-config'.")
51 .example("kubectl apply -f ./pod.yaml")
52 .example("kubectl apply -f https://example.com/manifest.yaml")
53 .example("kubectl apply -k ./")
54 .flag(
55 Flag::new("filename")
56 .short('f')
57 .usage("Filename, directory, or URL to files to use to create the resource")
58 .value_type(FlagType::String)
59 .required(),
60 )
61 .flag(
62 Flag::new("recursive")
63 .short('R')
64 .usage("Process the directory used in -f, --filename recursively")
65 .value_type(FlagType::Bool),
66 )
67 .build(),
68 )
69 .subcommand(
70 CommandBuilder::new("delete")
71 .short("Delete resources by file names, stdin, resources and names, or by resources and label selector")
72 .aliases(vec!["del", "remove", "rm"])
73 .group_id("Basic Commands")
74 .example("kubectl delete pod my-pod")
75 .example("kubectl delete -f ./pod.yaml")
76 .example("kubectl delete pods --all")
77 .build(),
78 )
79 .subcommand(
80 CommandBuilder::new("logs")
81 .short("Print the logs for a container in a pod")
82 .group_id("Troubleshooting and Debugging Commands")
83 .long("Print the logs for a container in a pod or specified resource. If the pod has\n\
84 only one container, the container name is optional.")
85 .aliases(vec!["log"])
86 .example("kubectl logs my-pod")
87 .example("kubectl logs my-pod -c my-container")
88 .example("kubectl logs -f my-pod")
89 .example("kubectl logs --tail=20 my-pod")
90 .flag(
91 Flag::new("follow")
92 .short('f')
93 .usage("Specify if the logs should be streamed")
94 .value_type(FlagType::Bool),
95 )
96 .flag(
97 Flag::new("tail")
98 .usage("Lines of recent log file to display")
99 .value_type(FlagType::Int)
100 .default(flag_rs::FlagValue::Int(-1)),
101 )
102 .build(),
103 )
104 .subcommand(
105 CommandBuilder::new("describe")
106 .short("Show details of a specific resource or group of resources")
107 .group_id("Troubleshooting and Debugging Commands")
108 .example("kubectl describe pod my-pod")
109 .example("kubectl describe nodes")
110 .build(),
111 )
112 .subcommand(
113 CommandBuilder::new("exec")
114 .short("Execute a command in a container")
115 .group_id("Troubleshooting and Debugging Commands")
116 .example("kubectl exec -it my-pod -- /bin/bash")
117 .example("kubectl exec my-pod -- ls /app")
118 .build(),
119 )
120 .subcommand(
121 CommandBuilder::new("config")
122 .short("Modify kubeconfig files")
123 .group_id("Settings Commands")
124 .example("kubectl config view")
125 .example("kubectl config use-context my-context")
126 .build(),
127 )
128 .subcommand(
129 CommandBuilder::new("version")
130 .short("Print the client and server version information")
131 .example("kubectl version")
132 .example("kubectl version --short")
133 .build(),
134 )
135 .build();
136
137 let args: Vec<String> = std::env::args().skip(1).collect();
138 if let Err(e) = app.execute(args) {
139 eprintln!("{}", e);
140 std::process::exit(1);
141 }
142}More examples
7fn main() {
8 let app = CommandBuilder::new("helpdemo")
9 .short("A demo of enhanced help formatting")
10 .long("This is a comprehensive demonstration of the enhanced help formatting system in flag-rs. \
11 It shows how help text is beautifully formatted with proper grouping, constraint \
12 information, and visual hierarchy.")
13 .group_id("utilities")
14 .example("helpdemo config --format=yaml --output=config.yaml")
15 .example("helpdemo process --workers=4 --verbose data.json")
16 .example("helpdemo serve --port=8080 --ssl --ssl-cert=cert.pem")
17 .flag(
18 Flag::new("verbose")
19 .short('v')
20 .value_type(FlagType::Bool)
21 .usage("Enable verbose output (can be repeated for more verbosity)"),
22 )
23 .flag(
24 Flag::new("config")
25 .short('c')
26 .value_type(FlagType::File)
27 .default(FlagValue::String("~/.config/app.toml".to_string()))
28 .usage("Configuration file path"),
29 )
30 .flag(
31 Flag::new("workers")
32 .short('w')
33 .value_type(FlagType::Range(1, 16))
34 .default(FlagValue::Int(4))
35 .usage("Number of worker threads"),
36 )
37 .flag(
38 Flag::new("timeout")
39 .short('t')
40 .value_type(FlagType::Int)
41 .default(FlagValue::Int(30))
42 .usage("Request timeout in seconds"),
43 )
44 .subcommand(
45 CommandBuilder::new("config")
46 .short("Manage configuration")
47 .long("The config command allows you to view, edit, and validate configuration files. \
48 It supports multiple output formats and can merge configurations from various sources.")
49 .alias("cfg")
50 .alias("conf")
51 .example("helpdemo config show")
52 .example("helpdemo config validate --strict")
53 .flag(
54 Flag::new("format")
55 .short('f')
56 .value_type(FlagType::Choice(vec![
57 "json".to_string(),
58 "yaml".to_string(),
59 "toml".to_string(),
60 "ini".to_string(),
61 ]))
62 .default(FlagValue::String("yaml".to_string()))
63 .usage("Configuration file format"),
64 )
65 .flag(
66 Flag::new("output")
67 .short('o')
68 .value_type(FlagType::String)
69 .required()
70 .usage("Output file path"),
71 )
72 .flag(
73 Flag::new("merge")
74 .value_type(FlagType::StringArray)
75 .usage("Additional config files to merge"),
76 )
77 .flag(
78 Flag::new("strict")
79 .value_type(FlagType::Bool)
80 .usage("Enable strict validation mode"),
81 )
82 .subcommand(
83 CommandBuilder::new("show")
84 .short("Display current configuration")
85 .run(|_| Ok(()))
86 .build(),
87 )
88 .subcommand(
89 CommandBuilder::new("validate")
90 .short("Validate configuration syntax")
91 .run(|_| Ok(()))
92 .build(),
93 )
94 .build(),
95 )
96 .subcommand(
97 CommandBuilder::new("process")
98 .short("Process data files")
99 .group_id("operations")
100 .flag(
101 Flag::new("input")
102 .short('i')
103 .value_type(FlagType::File)
104 .required()
105 .usage("Input data file"),
106 )
107 .flag(
108 Flag::new("output")
109 .short('o')
110 .value_type(FlagType::String)
111 .required()
112 .usage("Output file path"),
113 )
114 .flag(
115 Flag::new("format")
116 .short('f')
117 .value_type(FlagType::Choice(vec![
118 "csv".to_string(),
119 "json".to_string(),
120 "parquet".to_string(),
121 ]))
122 .usage("Output format"),
123 )
124 .flag(
125 Flag::new("compress")
126 .value_type(FlagType::Bool)
127 .usage("Compress output"),
128 )
129 .flag(
130 Flag::new("compression-level")
131 .value_type(FlagType::Range(1, 9))
132 .default(FlagValue::Int(6))
133 .constraint(FlagConstraint::RequiredIf("compress".to_string()))
134 .usage("Compression level"),
135 )
136 .run(|_| Ok(()))
137 .build(),
138 )
139 .subcommand(
140 CommandBuilder::new("serve")
141 .short("Start the server")
142 .group_id("operations")
143 .flag(
144 Flag::new("port")
145 .short('p')
146 .value_type(FlagType::Range(1, 65535))
147 .default(FlagValue::Int(8080))
148 .usage("Port to listen on"),
149 )
150 .flag(
151 Flag::new("host")
152 .short('h')
153 .value_type(FlagType::String)
154 .default(FlagValue::String("127.0.0.1".to_string()))
155 .usage("Host to bind to"),
156 )
157 .flag(
158 Flag::new("ssl")
159 .value_type(FlagType::Bool)
160 .usage("Enable SSL/TLS"),
161 )
162 .flag(
163 Flag::new("ssl-cert")
164 .value_type(FlagType::File)
165 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
166 .usage("SSL certificate file"),
167 )
168 .flag(
169 Flag::new("ssl-key")
170 .value_type(FlagType::File)
171 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
172 .usage("SSL private key file"),
173 )
174 .flag(
175 Flag::new("http2")
176 .value_type(FlagType::Bool)
177 .constraint(FlagConstraint::Requires(vec!["ssl".to_string()]))
178 .usage("Enable HTTP/2 support"),
179 )
180 .flag(
181 Flag::new("debug")
182 .short('d')
183 .value_type(FlagType::Bool)
184 .constraint(FlagConstraint::ConflictsWith(vec!["production".to_string()]))
185 .usage("Enable debug mode"),
186 )
187 .flag(
188 Flag::new("production")
189 .value_type(FlagType::Bool)
190 .usage("Enable production mode"),
191 )
192 .run(|_| Ok(()))
193 .build(),
194 )
195 .build();
196
197 println!("This example demonstrates enhanced help formatting.");
198 println!("Try these commands to see different help layouts:\n");
199 println!(" # Main help with command groups");
200 println!(" cargo run --example help_formatting_demo -- --help\n");
201
202 println!(" # Subcommand help with required flags");
203 println!(" cargo run --example help_formatting_demo -- config --help\n");
204
205 println!(" # Subcommand with flag constraints");
206 println!(" cargo run --example help_formatting_demo -- serve --help\n");
207
208 let args: Vec<String> = std::env::args().skip(1).collect();
209 if !args.is_empty() {
210 if let Err(e) = app.execute(args) {
211 eprintln!("{}", e);
212 std::process::exit(1);
213 }
214 }
215}Sourcepub fn subcommand(self, cmd: Command) -> Self
pub fn subcommand(self, cmd: Command) -> Self
Adds a subcommand to this command
§Examples
use flag_rs::CommandBuilder;
let app = CommandBuilder::new("myapp")
.subcommand(
CommandBuilder::new("init")
.short("Initialize a new project")
.build()
)
.subcommand(
CommandBuilder::new("build")
.short("Build the project")
.build()
)
.build();Examples found in repository?
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}More examples
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}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}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}7fn main() {
8 let app = CommandBuilder::new("errordemo")
9 .short("Demo of enhanced error messages")
10 .flag(
11 Flag::new("verbose")
12 .short('v')
13 .value_type(FlagType::Bool)
14 .usage("Enable verbose output"),
15 )
16 .flag(
17 Flag::new("workers")
18 .short('w')
19 .value_type(FlagType::Range(1, 10))
20 .usage("Number of worker threads"),
21 )
22 .flag(
23 Flag::new("format")
24 .short('f')
25 .value_type(FlagType::Choice(vec![
26 "json".to_string(),
27 "yaml".to_string(),
28 "xml".to_string(),
29 "toml".to_string(),
30 ]))
31 .usage("Output format"),
32 )
33 .flag(
34 Flag::new("config")
35 .short('c')
36 .value_type(FlagType::File)
37 .usage("Configuration file"),
38 )
39 .flag(
40 Flag::new("outdir")
41 .short('o')
42 .value_type(FlagType::Directory)
43 .usage("Output directory"),
44 )
45 .flag(
46 Flag::new("ssl")
47 .value_type(FlagType::Bool)
48 .usage("Enable SSL"),
49 )
50 .flag(
51 Flag::new("ssl-cert")
52 .value_type(FlagType::File)
53 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
54 .usage("SSL certificate file"),
55 )
56 .flag(
57 Flag::new("json")
58 .value_type(FlagType::Bool)
59 .constraint(FlagConstraint::ConflictsWith(vec!["xml".to_string()]))
60 .usage("Output in JSON format"),
61 )
62 .flag(
63 Flag::new("xml")
64 .value_type(FlagType::Bool)
65 .usage("Output in XML format"),
66 )
67 .flag(
68 Flag::new("required-flag")
69 .value_type(FlagType::String)
70 .required()
71 .usage("This flag is required"),
72 )
73 .subcommand(
74 CommandBuilder::new("process")
75 .short("Process data")
76 .run(|ctx| {
77 println!("Processing with flags: {:?}", ctx.flags());
78 Ok(())
79 })
80 .build(),
81 )
82 .build();
83
84 println!("Try these commands to see enhanced error messages:\n");
85 println!(" # Boolean parsing error");
86 println!(" cargo run --example enhanced_errors_demo -- --verbose=maybe\n");
87
88 println!(" # Integer parsing error");
89 println!(" cargo run --example enhanced_errors_demo -- --workers=abc\n");
90
91 println!(" # Range validation error");
92 println!(" cargo run --example enhanced_errors_demo -- --workers=20\n");
93
94 println!(" # Choice validation error");
95 println!(" cargo run --example enhanced_errors_demo -- --format=csv\n");
96
97 println!(" # File not found error");
98 println!(" cargo run --example enhanced_errors_demo -- --config=/tmp/nonexistent.conf\n");
99
100 println!(" # Directory not found error");
101 println!(" cargo run --example enhanced_errors_demo -- --outdir=/tmp/nonexistent_dir\n");
102
103 println!(" # Flag constraint error (RequiredIf)");
104 println!(" cargo run --example enhanced_errors_demo -- --ssl --required-flag=test process\n");
105
106 println!(" # Flag constraint error (ConflictsWith)");
107 println!(
108 " cargo run --example enhanced_errors_demo -- --json --xml --required-flag=test process\n"
109 );
110
111 println!(" # Required flag error");
112 println!(" cargo run --example enhanced_errors_demo -- process\n");
113
114 println!(" # Unknown command error");
115 println!(" cargo run --example enhanced_errors_demo -- --required-flag=test proces\n");
116
117 let args: Vec<String> = std::env::args().skip(1).collect();
118 if !args.is_empty() {
119 println!("\n{}", "=".repeat(60));
120 println!("Running with args: {:?}\n", args);
121
122 if let Err(e) = app.execute(args) {
123 eprintln!("{}", e);
124 std::process::exit(1);
125 }
126 }
127}Sourcepub fn subcommands(self, cmds: Vec<Command>) -> Self
pub fn subcommands(self, cmds: Vec<Command>) -> Self
Adds multiple subcommands to this command at once
§Examples
use flag_rs::CommandBuilder;
let cmd = CommandBuilder::new("git")
.subcommands(vec![
CommandBuilder::new("add")
.short("Add file contents to the index")
.build(),
CommandBuilder::new("commit")
.short("Record changes to the repository")
.build(),
CommandBuilder::new("push")
.short("Update remote refs along with associated objects")
.build(),
])
.build();Examples found in repository?
4fn main() {
5 // Demonstrates all the new builder API improvements
6 let app = CommandBuilder::new("app")
7 .short("Modern CLI app with improved API")
8 .long(
9 "This example showcases the builder API improvements including:\n\
10 - Type-specific flag constructors\n\
11 - Inline flag completions\n\
12 - Bulk flag/subcommand methods\n\
13 - Type-safe context access",
14 )
15 // Bulk flag addition
16 .flags(vec![
17 // Type-specific constructors
18 Flag::bool("verbose")
19 .short('v')
20 .usage("Enable verbose output")
21 .default_bool(false),
22 Flag::bool("quiet").short('q').usage("Suppress all output"),
23 Flag::int("threads")
24 .short('t')
25 .usage("Number of worker threads")
26 .default_int(4),
27 Flag::choice("log-level", &["debug", "info", "warn", "error"])
28 .usage("Set the logging level")
29 .default_str("info")
30 .completion(|_ctx, prefix| {
31 // Inline completion with descriptions
32 let levels = vec![
33 ("debug", "Show all messages including debug"),
34 ("info", "Show informational messages and above"),
35 ("warn", "Show warnings and errors only"),
36 ("error", "Show errors only"),
37 ];
38
39 let mut result = flag_rs::CompletionResult::new();
40 for (level, desc) in levels {
41 if level.starts_with(prefix) {
42 result =
43 result.add_with_description(level.to_string(), desc.to_string());
44 }
45 }
46 Ok(result)
47 }),
48 ])
49 // Bulk subcommand addition
50 .subcommands(vec![
51 CommandBuilder::new("init")
52 .short("Initialize a new project")
53 .flags(vec![
54 Flag::string("name")
55 .short('n')
56 .usage("Project name")
57 .required(),
58 Flag::choice("template", &["basic", "web", "api", "full"])
59 .usage("Project template to use")
60 .default_str("basic"),
61 Flag::directory("path")
62 .short('p')
63 .usage("Directory to create project in")
64 .completion(|_ctx, prefix| {
65 // In a real app, list actual directories
66 let dirs = vec!["./", "../", "/tmp/", "~/projects/"];
67 Ok(flag_rs::CompletionResult::new().extend(
68 dirs.into_iter()
69 .filter(|d| d.starts_with(prefix))
70 .map(String::from),
71 ))
72 }),
73 ])
74 .run(|ctx| {
75 // Type-safe flag access
76 let name = ctx.flag_str_or("name", "my-project");
77 let template = ctx.flag_str_or("template", "basic");
78 let path = ctx.flag_str_or("path", ".");
79 let verbose = ctx.flag_bool_or("verbose", false);
80
81 if verbose {
82 println!("Initializing {} project '{}' in {}", template, name, path);
83 } else {
84 println!("Creating project '{}'...", name);
85 }
86
87 Ok(())
88 })
89 .build(),
90 CommandBuilder::new("build")
91 .short("Build the project")
92 .flags(vec![
93 Flag::bool("release")
94 .short('r')
95 .usage("Build in release mode"),
96 Flag::string_slice("features")
97 .short('f')
98 .usage("Enable features (can be specified multiple times)")
99 .completion(|_ctx, prefix| {
100 let features = vec!["async", "tls", "compression", "metrics"];
101 Ok(flag_rs::CompletionResult::new().extend(
102 features
103 .into_iter()
104 .filter(|f| f.starts_with(prefix))
105 .map(String::from),
106 ))
107 }),
108 Flag::range("jobs", 1, 32)
109 .short('j')
110 .usage("Number of parallel jobs")
111 .default_int(i64::try_from(num_cpus()).unwrap_or(4)),
112 ])
113 .run(|ctx| {
114 let release = ctx.flag_bool_or("release", false);
115 let jobs = ctx.flag_int_or("jobs", i64::try_from(num_cpus()).unwrap_or(4));
116 let quiet = ctx.flag_bool_or("quiet", false);
117
118 if !quiet {
119 println!(
120 "Building in {} mode with {} jobs",
121 if release { "release" } else { "debug" },
122 jobs
123 );
124
125 if let Some(features) = ctx.flag("features") {
126 println!("Features: {}", features);
127 }
128 }
129
130 Ok(())
131 })
132 .build(),
133 CommandBuilder::new("test")
134 .short("Run tests")
135 .flags(vec![
136 Flag::string("filter").usage("Only run tests matching this pattern"),
137 Flag::bool("nocapture").usage("Don't capture test output"),
138 Flag::int("test-threads")
139 .usage("Number of threads to use for running tests")
140 .default_int(1),
141 ])
142 .run(|ctx| {
143 let threads = ctx.flag_int_or("test-threads", 1);
144 let nocapture = ctx.flag_bool_or("nocapture", false);
145 let verbose = ctx.flag_bool_or("verbose", false);
146
147 if verbose {
148 println!("Running tests with {} thread(s)", threads);
149 if nocapture {
150 println!("Output capture disabled");
151 }
152
153 if let Some(filter) = ctx.flag("filter") {
154 println!("Filter: {}", filter);
155 }
156 }
157
158 println!("Running tests...");
159 Ok(())
160 })
161 .build(),
162 ])
163 .run(|_ctx| {
164 // Root command - show help by default
165 println!("Use --help for usage information");
166 Ok(())
167 })
168 .build();
169
170 let args: Vec<String> = std::env::args().skip(1).collect();
171 if let Err(e) = app.execute(args) {
172 eprintln!("Error: {}", e);
173 std::process::exit(1);
174 }
175}Sourcepub fn flag(self, flag: Flag) -> Self
pub fn flag(self, flag: Flag) -> Self
Adds a flag to this command
§Examples
use flag_rs::{CommandBuilder, Flag, FlagType};
let cmd = CommandBuilder::new("deploy")
.flag(
Flag::new("force")
.short('f')
.usage("Force deployment without confirmation")
.value_type(FlagType::Bool)
)
.build();Examples found in repository?
50fn create_simple_cli() -> flag_rs::Command {
51 CommandBuilder::new("bench")
52 .short("Benchmark CLI")
53 .flag(
54 Flag::new("verbose")
55 .short('v')
56 .value_type(FlagType::Bool)
57 .default(FlagValue::Bool(false)),
58 )
59 .flag(Flag::new("output").short('o').value_type(FlagType::String))
60 .build()
61}
62
63/// Creates a complex nested command structure
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}
98
99fn bench_command_creation() {
100 println!("\n=== Command Creation Benchmarks ===");
101
102 let bench = Benchmark::new("Simple command creation", 10_000);
103 let duration = bench.run(|| {
104 let _ = create_simple_cli();
105 });
106 bench.report(duration);
107
108 let bench = Benchmark::new("Complex command creation (50 subs)", 100);
109 let duration = bench.run(|| {
110 let _ = create_complex_cli();
111 });
112 bench.report(duration);
113
114 let bench = Benchmark::new("CommandBuilder with 10 flags", 1_000);
115 let duration = bench.run(|| {
116 let mut cmd = CommandBuilder::new("test");
117 for i in 0..10 {
118 cmd = cmd.flag(Flag::new(format!("flag{i}")).value_type(FlagType::String));
119 }
120 let _ = cmd.build();
121 });
122 bench.report(duration);
123}
124
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}More examples
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}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}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}7fn main() {
8 let app = CommandBuilder::new("errordemo")
9 .short("Demo of enhanced error messages")
10 .flag(
11 Flag::new("verbose")
12 .short('v')
13 .value_type(FlagType::Bool)
14 .usage("Enable verbose output"),
15 )
16 .flag(
17 Flag::new("workers")
18 .short('w')
19 .value_type(FlagType::Range(1, 10))
20 .usage("Number of worker threads"),
21 )
22 .flag(
23 Flag::new("format")
24 .short('f')
25 .value_type(FlagType::Choice(vec![
26 "json".to_string(),
27 "yaml".to_string(),
28 "xml".to_string(),
29 "toml".to_string(),
30 ]))
31 .usage("Output format"),
32 )
33 .flag(
34 Flag::new("config")
35 .short('c')
36 .value_type(FlagType::File)
37 .usage("Configuration file"),
38 )
39 .flag(
40 Flag::new("outdir")
41 .short('o')
42 .value_type(FlagType::Directory)
43 .usage("Output directory"),
44 )
45 .flag(
46 Flag::new("ssl")
47 .value_type(FlagType::Bool)
48 .usage("Enable SSL"),
49 )
50 .flag(
51 Flag::new("ssl-cert")
52 .value_type(FlagType::File)
53 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
54 .usage("SSL certificate file"),
55 )
56 .flag(
57 Flag::new("json")
58 .value_type(FlagType::Bool)
59 .constraint(FlagConstraint::ConflictsWith(vec!["xml".to_string()]))
60 .usage("Output in JSON format"),
61 )
62 .flag(
63 Flag::new("xml")
64 .value_type(FlagType::Bool)
65 .usage("Output in XML format"),
66 )
67 .flag(
68 Flag::new("required-flag")
69 .value_type(FlagType::String)
70 .required()
71 .usage("This flag is required"),
72 )
73 .subcommand(
74 CommandBuilder::new("process")
75 .short("Process data")
76 .run(|ctx| {
77 println!("Processing with flags: {:?}", ctx.flags());
78 Ok(())
79 })
80 .build(),
81 )
82 .build();
83
84 println!("Try these commands to see enhanced error messages:\n");
85 println!(" # Boolean parsing error");
86 println!(" cargo run --example enhanced_errors_demo -- --verbose=maybe\n");
87
88 println!(" # Integer parsing error");
89 println!(" cargo run --example enhanced_errors_demo -- --workers=abc\n");
90
91 println!(" # Range validation error");
92 println!(" cargo run --example enhanced_errors_demo -- --workers=20\n");
93
94 println!(" # Choice validation error");
95 println!(" cargo run --example enhanced_errors_demo -- --format=csv\n");
96
97 println!(" # File not found error");
98 println!(" cargo run --example enhanced_errors_demo -- --config=/tmp/nonexistent.conf\n");
99
100 println!(" # Directory not found error");
101 println!(" cargo run --example enhanced_errors_demo -- --outdir=/tmp/nonexistent_dir\n");
102
103 println!(" # Flag constraint error (RequiredIf)");
104 println!(" cargo run --example enhanced_errors_demo -- --ssl --required-flag=test process\n");
105
106 println!(" # Flag constraint error (ConflictsWith)");
107 println!(
108 " cargo run --example enhanced_errors_demo -- --json --xml --required-flag=test process\n"
109 );
110
111 println!(" # Required flag error");
112 println!(" cargo run --example enhanced_errors_demo -- process\n");
113
114 println!(" # Unknown command error");
115 println!(" cargo run --example enhanced_errors_demo -- --required-flag=test proces\n");
116
117 let args: Vec<String> = std::env::args().skip(1).collect();
118 if !args.is_empty() {
119 println!("\n{}", "=".repeat(60));
120 println!("Running with args: {:?}\n", args);
121
122 if let Err(e) = app.execute(args) {
123 eprintln!("{}", e);
124 std::process::exit(1);
125 }
126 }
127}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 flags(self, flags: Vec<Flag>) -> Self
pub fn flags(self, flags: Vec<Flag>) -> Self
Adds multiple flags to this command at once
§Examples
use flag_rs::{CommandBuilder, Flag};
let cmd = CommandBuilder::new("server")
.flags(vec![
Flag::bool("verbose").short('v').usage("Enable verbose output"),
Flag::bool("quiet").short('q').usage("Suppress output"),
Flag::int("port").short('p').usage("Port to listen on").default_int(8080),
])
.build();Examples found in repository?
4fn main() {
5 // Demonstrates all the new builder API improvements
6 let app = CommandBuilder::new("app")
7 .short("Modern CLI app with improved API")
8 .long(
9 "This example showcases the builder API improvements including:\n\
10 - Type-specific flag constructors\n\
11 - Inline flag completions\n\
12 - Bulk flag/subcommand methods\n\
13 - Type-safe context access",
14 )
15 // Bulk flag addition
16 .flags(vec![
17 // Type-specific constructors
18 Flag::bool("verbose")
19 .short('v')
20 .usage("Enable verbose output")
21 .default_bool(false),
22 Flag::bool("quiet").short('q').usage("Suppress all output"),
23 Flag::int("threads")
24 .short('t')
25 .usage("Number of worker threads")
26 .default_int(4),
27 Flag::choice("log-level", &["debug", "info", "warn", "error"])
28 .usage("Set the logging level")
29 .default_str("info")
30 .completion(|_ctx, prefix| {
31 // Inline completion with descriptions
32 let levels = vec![
33 ("debug", "Show all messages including debug"),
34 ("info", "Show informational messages and above"),
35 ("warn", "Show warnings and errors only"),
36 ("error", "Show errors only"),
37 ];
38
39 let mut result = flag_rs::CompletionResult::new();
40 for (level, desc) in levels {
41 if level.starts_with(prefix) {
42 result =
43 result.add_with_description(level.to_string(), desc.to_string());
44 }
45 }
46 Ok(result)
47 }),
48 ])
49 // Bulk subcommand addition
50 .subcommands(vec![
51 CommandBuilder::new("init")
52 .short("Initialize a new project")
53 .flags(vec![
54 Flag::string("name")
55 .short('n')
56 .usage("Project name")
57 .required(),
58 Flag::choice("template", &["basic", "web", "api", "full"])
59 .usage("Project template to use")
60 .default_str("basic"),
61 Flag::directory("path")
62 .short('p')
63 .usage("Directory to create project in")
64 .completion(|_ctx, prefix| {
65 // In a real app, list actual directories
66 let dirs = vec!["./", "../", "/tmp/", "~/projects/"];
67 Ok(flag_rs::CompletionResult::new().extend(
68 dirs.into_iter()
69 .filter(|d| d.starts_with(prefix))
70 .map(String::from),
71 ))
72 }),
73 ])
74 .run(|ctx| {
75 // Type-safe flag access
76 let name = ctx.flag_str_or("name", "my-project");
77 let template = ctx.flag_str_or("template", "basic");
78 let path = ctx.flag_str_or("path", ".");
79 let verbose = ctx.flag_bool_or("verbose", false);
80
81 if verbose {
82 println!("Initializing {} project '{}' in {}", template, name, path);
83 } else {
84 println!("Creating project '{}'...", name);
85 }
86
87 Ok(())
88 })
89 .build(),
90 CommandBuilder::new("build")
91 .short("Build the project")
92 .flags(vec![
93 Flag::bool("release")
94 .short('r')
95 .usage("Build in release mode"),
96 Flag::string_slice("features")
97 .short('f')
98 .usage("Enable features (can be specified multiple times)")
99 .completion(|_ctx, prefix| {
100 let features = vec!["async", "tls", "compression", "metrics"];
101 Ok(flag_rs::CompletionResult::new().extend(
102 features
103 .into_iter()
104 .filter(|f| f.starts_with(prefix))
105 .map(String::from),
106 ))
107 }),
108 Flag::range("jobs", 1, 32)
109 .short('j')
110 .usage("Number of parallel jobs")
111 .default_int(i64::try_from(num_cpus()).unwrap_or(4)),
112 ])
113 .run(|ctx| {
114 let release = ctx.flag_bool_or("release", false);
115 let jobs = ctx.flag_int_or("jobs", i64::try_from(num_cpus()).unwrap_or(4));
116 let quiet = ctx.flag_bool_or("quiet", false);
117
118 if !quiet {
119 println!(
120 "Building in {} mode with {} jobs",
121 if release { "release" } else { "debug" },
122 jobs
123 );
124
125 if let Some(features) = ctx.flag("features") {
126 println!("Features: {}", features);
127 }
128 }
129
130 Ok(())
131 })
132 .build(),
133 CommandBuilder::new("test")
134 .short("Run tests")
135 .flags(vec![
136 Flag::string("filter").usage("Only run tests matching this pattern"),
137 Flag::bool("nocapture").usage("Don't capture test output"),
138 Flag::int("test-threads")
139 .usage("Number of threads to use for running tests")
140 .default_int(1),
141 ])
142 .run(|ctx| {
143 let threads = ctx.flag_int_or("test-threads", 1);
144 let nocapture = ctx.flag_bool_or("nocapture", false);
145 let verbose = ctx.flag_bool_or("verbose", false);
146
147 if verbose {
148 println!("Running tests with {} thread(s)", threads);
149 if nocapture {
150 println!("Output capture disabled");
151 }
152
153 if let Some(filter) = ctx.flag("filter") {
154 println!("Filter: {}", filter);
155 }
156 }
157
158 println!("Running tests...");
159 Ok(())
160 })
161 .build(),
162 ])
163 .run(|_ctx| {
164 // Root command - show help by default
165 println!("Use --help for usage information");
166 Ok(())
167 })
168 .build();
169
170 let args: Vec<String> = std::env::args().skip(1).collect();
171 if let Err(e) = app.execute(args) {
172 eprintln!("Error: {}", e);
173 std::process::exit(1);
174 }
175}Sourcepub fn run<F>(self, f: F) -> Self
pub fn run<F>(self, f: F) -> Self
Sets the function to run when this command is executed
The run function receives a mutable reference to the Context
which provides access to parsed flags and arguments.
§Examples
use flag_rs::CommandBuilder;
let cmd = CommandBuilder::new("greet")
.run(|ctx| {
let name = ctx.args().first()
.map(|s| s.as_str())
.unwrap_or("World");
println!("Hello, {}!", name);
Ok(())
})
.build();Examples found in repository?
79fn build_get_pods() -> Command {
80 CommandBuilder::new("pods")
81 .aliases(vec!["po", "pod"])
82 .short("List pods")
83 .arg_completion(|ctx, prefix| {
84 // This is the key feature - dynamic completion based on runtime state!
85 let namespace = ctx
86 .flag("namespace")
87 .map(String::as_str)
88 .unwrap_or("default");
89
90 // In real kubectl, this would query the K8s API
91 let pods = get_pods_with_status(namespace);
92 let mut result = CompletionResult::new();
93
94 for (pod_name, status) in pods {
95 if pod_name.starts_with(prefix) {
96 result = result.add_with_description(pod_name, status);
97 }
98 }
99
100 Ok(result)
101 })
102 .run(|ctx| {
103 let namespace = ctx
104 .flag("namespace")
105 .map(String::as_str)
106 .unwrap_or("default");
107
108 println!("Listing pods in namespace: {namespace}");
109
110 if let Some(pod_name) = ctx.args().first() {
111 println!("Getting specific pod: {pod_name}");
112 } else {
113 for pod in get_pods_in_namespace(namespace) {
114 println!("pod/{}", pod);
115 }
116 }
117
118 Ok(())
119 })
120 .build()
121}
122
123fn build_get_services() -> Command {
124 CommandBuilder::new("services")
125 .aliases(vec!["svc", "service"])
126 .short("List services")
127 .arg_completion(|ctx, prefix| {
128 let namespace = ctx
129 .flag("namespace")
130 .map(String::as_str)
131 .unwrap_or("default");
132
133 let services = get_services_in_namespace(namespace);
134 Ok(CompletionResult::new()
135 .extend(services.into_iter().filter(|svc| svc.starts_with(prefix))))
136 })
137 .run(|ctx| {
138 let namespace = ctx
139 .flag("namespace")
140 .map(String::as_str)
141 .unwrap_or("default");
142
143 println!("Listing services in namespace: {namespace}");
144 Ok(())
145 })
146 .build()
147}
148
149fn build_get_deployments() -> Command {
150 CommandBuilder::new("deployments")
151 .aliases(vec!["deploy", "deployment"])
152 .short("List deployments")
153 .run(|ctx| {
154 let namespace = ctx
155 .flag("namespace")
156 .map(String::as_str)
157 .unwrap_or("default");
158
159 println!("Listing deployments in namespace: {namespace}");
160 Ok(())
161 })
162 .build()
163}
164
165fn build_describe_command() -> Command {
166 CommandBuilder::new("describe")
167 .short("Show details of a specific resource")
168 .run(|_ctx| {
169 println!("Describe command - add resource type subcommands");
170 Ok(())
171 })
172 .build()
173}
174
175fn build_delete_command() -> Command {
176 CommandBuilder::new("delete")
177 .short("Delete resources")
178 .run(|_ctx| {
179 println!("Delete command - add resource type subcommands");
180 Ok(())
181 })
182 .build()
183}
184
185// Mock functions that would normally query the Kubernetes API
186fn get_namespaces_with_descriptions() -> Vec<(String, String)> {
187 vec![
188 (
189 "default".to_string(),
190 "Default namespace for user workloads".to_string(),
191 ),
192 (
193 "kube-system".to_string(),
194 "Kubernetes system components".to_string(),
195 ),
196 (
197 "kube-public".to_string(),
198 "Public resources accessible to all users".to_string(),
199 ),
200 (
201 "development".to_string(),
202 "Development environment".to_string(),
203 ),
204 (
205 "staging".to_string(),
206 "Staging environment for pre-production testing".to_string(),
207 ),
208 (
209 "production".to_string(),
210 "Production environment (CAUTION)".to_string(),
211 ),
212 ]
213}
214
215fn get_pods_with_status(namespace: &str) -> Vec<(String, String)> {
216 use std::time::{SystemTime, UNIX_EPOCH};
217
218 // Generate random suffix based on current time
219 let timestamp = SystemTime::now()
220 .duration_since(UNIX_EPOCH)
221 .unwrap()
222 .as_secs();
223
224 // Simple pseudo-random generation
225 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
226 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
227 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
228
229 match namespace {
230 "default" => vec![
231 (
232 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
233 "Running (2/2 containers)".to_string(),
234 ),
235 (
236 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
237 "Running (2/2 containers)".to_string(),
238 ),
239 (
240 "redis-master-0".to_string(),
241 "Running (1/1 containers)".to_string(),
242 ),
243 (
244 "redis-slave-0".to_string(),
245 "Running (1/1 containers)".to_string(),
246 ),
247 ("redis-slave-1".to_string(), "Pending".to_string()),
248 ],
249 "kube-system" => vec![
250 (
251 format!("coredns-5d78c9869d-{:05x}", rand1),
252 "Running (1/1 containers)".to_string(),
253 ),
254 (
255 format!("coredns-5d78c9869d-{:05x}", rand2),
256 "Running (1/1 containers)".to_string(),
257 ),
258 ("etcd-minikube".to_string(), "Running".to_string()),
259 ("kube-apiserver-minikube".to_string(), "Running".to_string()),
260 (
261 "kube-controller-manager-minikube".to_string(),
262 "Running".to_string(),
263 ),
264 (format!("kube-proxy-{:05x}", rand3), "Running".to_string()),
265 ("kube-scheduler-minikube".to_string(), "Running".to_string()),
266 ],
267 _ => vec![],
268 }
269}
270
271fn get_pods_in_namespace(namespace: &str) -> Vec<String> {
272 use std::time::{SystemTime, UNIX_EPOCH};
273
274 // Generate random suffix based on current time
275 let timestamp = SystemTime::now()
276 .duration_since(UNIX_EPOCH)
277 .unwrap()
278 .as_secs();
279
280 // Simple pseudo-random generation
281 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
282 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
283 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
284
285 match namespace {
286 "default" => vec![
287 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
288 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
289 format!("redis-master-0"),
290 format!("redis-slave-0"),
291 format!("redis-slave-1"),
292 ],
293 "kube-system" => vec![
294 format!("coredns-5d78c9869d-{:05x}", rand1),
295 format!("coredns-5d78c9869d-{:05x}", rand2),
296 format!("etcd-minikube"),
297 format!("kube-apiserver-minikube"),
298 format!("kube-controller-manager-minikube"),
299 format!("kube-proxy-{:05x}", rand3),
300 format!("kube-scheduler-minikube"),
301 ],
302 _ => vec![],
303 }
304}
305
306fn get_services_in_namespace(namespace: &str) -> Vec<String> {
307 match namespace {
308 "default" => vec![
309 "kubernetes".to_string(),
310 "nginx-service".to_string(),
311 "redis-master".to_string(),
312 "redis-slave".to_string(),
313 ],
314 "kube-system" => vec!["kube-dns".to_string()],
315 _ => vec![],
316 }
317}
318
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}More examples
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}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}7fn main() {
8 let app = CommandBuilder::new("errordemo")
9 .short("Demo of enhanced error messages")
10 .flag(
11 Flag::new("verbose")
12 .short('v')
13 .value_type(FlagType::Bool)
14 .usage("Enable verbose output"),
15 )
16 .flag(
17 Flag::new("workers")
18 .short('w')
19 .value_type(FlagType::Range(1, 10))
20 .usage("Number of worker threads"),
21 )
22 .flag(
23 Flag::new("format")
24 .short('f')
25 .value_type(FlagType::Choice(vec![
26 "json".to_string(),
27 "yaml".to_string(),
28 "xml".to_string(),
29 "toml".to_string(),
30 ]))
31 .usage("Output format"),
32 )
33 .flag(
34 Flag::new("config")
35 .short('c')
36 .value_type(FlagType::File)
37 .usage("Configuration file"),
38 )
39 .flag(
40 Flag::new("outdir")
41 .short('o')
42 .value_type(FlagType::Directory)
43 .usage("Output directory"),
44 )
45 .flag(
46 Flag::new("ssl")
47 .value_type(FlagType::Bool)
48 .usage("Enable SSL"),
49 )
50 .flag(
51 Flag::new("ssl-cert")
52 .value_type(FlagType::File)
53 .constraint(FlagConstraint::RequiredIf("ssl".to_string()))
54 .usage("SSL certificate file"),
55 )
56 .flag(
57 Flag::new("json")
58 .value_type(FlagType::Bool)
59 .constraint(FlagConstraint::ConflictsWith(vec!["xml".to_string()]))
60 .usage("Output in JSON format"),
61 )
62 .flag(
63 Flag::new("xml")
64 .value_type(FlagType::Bool)
65 .usage("Output in XML format"),
66 )
67 .flag(
68 Flag::new("required-flag")
69 .value_type(FlagType::String)
70 .required()
71 .usage("This flag is required"),
72 )
73 .subcommand(
74 CommandBuilder::new("process")
75 .short("Process data")
76 .run(|ctx| {
77 println!("Processing with flags: {:?}", ctx.flags());
78 Ok(())
79 })
80 .build(),
81 )
82 .build();
83
84 println!("Try these commands to see enhanced error messages:\n");
85 println!(" # Boolean parsing error");
86 println!(" cargo run --example enhanced_errors_demo -- --verbose=maybe\n");
87
88 println!(" # Integer parsing error");
89 println!(" cargo run --example enhanced_errors_demo -- --workers=abc\n");
90
91 println!(" # Range validation error");
92 println!(" cargo run --example enhanced_errors_demo -- --workers=20\n");
93
94 println!(" # Choice validation error");
95 println!(" cargo run --example enhanced_errors_demo -- --format=csv\n");
96
97 println!(" # File not found error");
98 println!(" cargo run --example enhanced_errors_demo -- --config=/tmp/nonexistent.conf\n");
99
100 println!(" # Directory not found error");
101 println!(" cargo run --example enhanced_errors_demo -- --outdir=/tmp/nonexistent_dir\n");
102
103 println!(" # Flag constraint error (RequiredIf)");
104 println!(" cargo run --example enhanced_errors_demo -- --ssl --required-flag=test process\n");
105
106 println!(" # Flag constraint error (ConflictsWith)");
107 println!(
108 " cargo run --example enhanced_errors_demo -- --json --xml --required-flag=test process\n"
109 );
110
111 println!(" # Required flag error");
112 println!(" cargo run --example enhanced_errors_demo -- process\n");
113
114 println!(" # Unknown command error");
115 println!(" cargo run --example enhanced_errors_demo -- --required-flag=test proces\n");
116
117 let args: Vec<String> = std::env::args().skip(1).collect();
118 if !args.is_empty() {
119 println!("\n{}", "=".repeat(60));
120 println!("Running with args: {:?}\n", args);
121
122 if let Err(e) = app.execute(args) {
123 eprintln!("{}", e);
124 std::process::exit(1);
125 }
126 }
127}12fn main() {
13 let app = CommandBuilder::new("error-demo")
14 .short("Demonstrates error handling")
15 .long("This example shows various error scenarios and how flag-rs handles them")
16 .subcommand(
17 CommandBuilder::new("deploy")
18 .short("Deploy the application")
19 .args(ArgValidator::ExactArgs(1))
20 .run(|ctx| {
21 let env = ctx.args().first().unwrap();
22 if !["dev", "staging", "production"].contains(&env.as_str()) {
23 return Err(Error::Validation(format!(
24 "invalid environment '{}', must be one of: dev, staging, production",
25 env
26 )));
27 }
28 println!("Deploying to {}", env);
29 Ok(())
30 })
31 .build(),
32 )
33 .subcommand(
34 CommandBuilder::new("serve")
35 .short("Start the server")
36 .flag(
37 Flag::new("port")
38 .short('p')
39 .usage("Port to listen on")
40 .value_type(FlagType::Int)
41 .required(),
42 )
43 .run(|ctx| {
44 let port = ctx.flag("port").unwrap();
45 println!("Server starting on port {}", port);
46 Ok(())
47 })
48 .build(),
49 )
50 .subcommand(
51 CommandBuilder::new("migrate")
52 .short("Run database migrations")
53 .subcommand(
54 CommandBuilder::new("up")
55 .short("Run pending migrations")
56 .run(|_| {
57 println!("Running migrations...");
58 Ok(())
59 })
60 .build(),
61 )
62 .subcommand(
63 CommandBuilder::new("down")
64 .short("Rollback migrations")
65 .args(ArgValidator::MaximumArgs(1))
66 .run(|ctx| {
67 let steps = ctx
68 .args()
69 .first()
70 .and_then(|s| s.parse::<u32>().ok())
71 .unwrap_or(1);
72 println!(
73 "Rolling back {} migration{}",
74 steps,
75 if steps == 1 { "" } else { "s" }
76 );
77 Ok(())
78 })
79 .build(),
80 )
81 .build(),
82 )
83 .subcommand(
84 CommandBuilder::new("config")
85 .short("Manage configuration")
86 .aliases(vec!["cfg", "conf"])
87 .run(|_| {
88 println!("Configuration management");
89 Ok(())
90 })
91 .build(),
92 )
93 .build();
94
95 println!("=== Error Handling Demo ===\n");
96 println!("This demo shows how flag-rs handles various error scenarios.\n");
97 println!("Try these commands to see different error types:\n");
98 println!(
99 "1. {} deploi (typo - will show suggestions)",
100 std::env::args().next().unwrap_or_default()
101 );
102 println!(
103 "2. {} deploy (missing required argument)",
104 std::env::args().next().unwrap_or_default()
105 );
106 println!(
107 "3. {} deploy dev staging (too many arguments)",
108 std::env::args().next().unwrap_or_default()
109 );
110 println!(
111 "4. {} deploy test (invalid environment)",
112 std::env::args().next().unwrap_or_default()
113 );
114 println!(
115 "5. {} serve (missing required flag)",
116 std::env::args().next().unwrap_or_default()
117 );
118 println!(
119 "6. {} serve -p abc (invalid flag value)",
120 std::env::args().next().unwrap_or_default()
121 );
122 println!(
123 "7. {} migrate (subcommand required)",
124 std::env::args().next().unwrap_or_default()
125 );
126 println!(
127 "8. {} confi (typo - will suggest 'config')",
128 std::env::args().next().unwrap_or_default()
129 );
130 println!("\n---\n");
131
132 let args: Vec<String> = std::env::args().skip(1).collect();
133 if let Err(e) = app.execute(args) {
134 eprintln!("{}", e);
135 std::process::exit(1);
136 }
137}Sourcepub fn args(self, validator: ArgValidator) -> Self
pub fn args(self, validator: ArgValidator) -> Self
Sets the argument validator for this command
The validator will be called before the run function to ensure arguments meet the specified constraints.
§Examples
use flag_rs::{CommandBuilder, ArgValidator};
let cmd = CommandBuilder::new("delete")
.args(ArgValidator::MinimumArgs(1))
.run(|ctx| {
for file in ctx.args() {
println!("Deleting: {}", file);
}
Ok(())
})
.build();Examples found in repository?
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}More examples
12fn main() {
13 let app = CommandBuilder::new("error-demo")
14 .short("Demonstrates error handling")
15 .long("This example shows various error scenarios and how flag-rs handles them")
16 .subcommand(
17 CommandBuilder::new("deploy")
18 .short("Deploy the application")
19 .args(ArgValidator::ExactArgs(1))
20 .run(|ctx| {
21 let env = ctx.args().first().unwrap();
22 if !["dev", "staging", "production"].contains(&env.as_str()) {
23 return Err(Error::Validation(format!(
24 "invalid environment '{}', must be one of: dev, staging, production",
25 env
26 )));
27 }
28 println!("Deploying to {}", env);
29 Ok(())
30 })
31 .build(),
32 )
33 .subcommand(
34 CommandBuilder::new("serve")
35 .short("Start the server")
36 .flag(
37 Flag::new("port")
38 .short('p')
39 .usage("Port to listen on")
40 .value_type(FlagType::Int)
41 .required(),
42 )
43 .run(|ctx| {
44 let port = ctx.flag("port").unwrap();
45 println!("Server starting on port {}", port);
46 Ok(())
47 })
48 .build(),
49 )
50 .subcommand(
51 CommandBuilder::new("migrate")
52 .short("Run database migrations")
53 .subcommand(
54 CommandBuilder::new("up")
55 .short("Run pending migrations")
56 .run(|_| {
57 println!("Running migrations...");
58 Ok(())
59 })
60 .build(),
61 )
62 .subcommand(
63 CommandBuilder::new("down")
64 .short("Rollback migrations")
65 .args(ArgValidator::MaximumArgs(1))
66 .run(|ctx| {
67 let steps = ctx
68 .args()
69 .first()
70 .and_then(|s| s.parse::<u32>().ok())
71 .unwrap_or(1);
72 println!(
73 "Rolling back {} migration{}",
74 steps,
75 if steps == 1 { "" } else { "s" }
76 );
77 Ok(())
78 })
79 .build(),
80 )
81 .build(),
82 )
83 .subcommand(
84 CommandBuilder::new("config")
85 .short("Manage configuration")
86 .aliases(vec!["cfg", "conf"])
87 .run(|_| {
88 println!("Configuration management");
89 Ok(())
90 })
91 .build(),
92 )
93 .build();
94
95 println!("=== Error Handling Demo ===\n");
96 println!("This demo shows how flag-rs handles various error scenarios.\n");
97 println!("Try these commands to see different error types:\n");
98 println!(
99 "1. {} deploi (typo - will show suggestions)",
100 std::env::args().next().unwrap_or_default()
101 );
102 println!(
103 "2. {} deploy (missing required argument)",
104 std::env::args().next().unwrap_or_default()
105 );
106 println!(
107 "3. {} deploy dev staging (too many arguments)",
108 std::env::args().next().unwrap_or_default()
109 );
110 println!(
111 "4. {} deploy test (invalid environment)",
112 std::env::args().next().unwrap_or_default()
113 );
114 println!(
115 "5. {} serve (missing required flag)",
116 std::env::args().next().unwrap_or_default()
117 );
118 println!(
119 "6. {} serve -p abc (invalid flag value)",
120 std::env::args().next().unwrap_or_default()
121 );
122 println!(
123 "7. {} migrate (subcommand required)",
124 std::env::args().next().unwrap_or_default()
125 );
126 println!(
127 "8. {} confi (typo - will suggest 'config')",
128 std::env::args().next().unwrap_or_default()
129 );
130 println!("\n---\n");
131
132 let args: Vec<String> = std::env::args().skip(1).collect();
133 if let Err(e) = app.execute(args) {
134 eprintln!("{}", e);
135 std::process::exit(1);
136 }
137}Sourcepub fn persistent_pre_run<F>(self, f: F) -> Self
pub fn persistent_pre_run<F>(self, f: F) -> Self
Sets the persistent pre-run hook for this command
This hook runs before the command and all its subcommands. It’s inherited by all subcommands and runs in parent-to-child order.
§Examples
use flag_rs::CommandBuilder;
let cmd = CommandBuilder::new("app")
.persistent_pre_run(|ctx| {
println!("Setting up logging...");
Ok(())
})
.build();Examples found in repository?
17fn main() {
18 let app = CommandBuilder::new("lifecycle-demo")
19 .short("Demonstrates lifecycle hooks")
20 .long("This example shows how lifecycle hooks execute in the proper order")
21 .flag(
22 Flag::new("verbose")
23 .short('v')
24 .usage("Enable verbose output")
25 .value_type(FlagType::Bool),
26 )
27 // Persistent hooks run for this command and all subcommands
28 .persistent_pre_run(|ctx| {
29 log_hook("Root PersistentPreRun");
30 if ctx.flag("verbose").is_some() {
31 println!(" -> Verbose mode enabled globally");
32 }
33 Ok(())
34 })
35 .persistent_post_run(|ctx| {
36 log_hook("Root PersistentPostRun");
37 if ctx.flag("verbose").is_some() {
38 println!(" -> Cleaning up verbose logging");
39 }
40 Ok(())
41 })
42 .subcommand(
43 CommandBuilder::new("server")
44 .short("Server management commands")
45 .persistent_pre_run(|_ctx| {
46 log_hook("Server PersistentPreRun");
47 println!(" -> Initializing server module");
48 Ok(())
49 })
50 .persistent_post_run(|_ctx| {
51 log_hook("Server PersistentPostRun");
52 println!(" -> Cleaning up server module");
53 Ok(())
54 })
55 .subcommand(
56 CommandBuilder::new("start")
57 .short("Start the server")
58 .flag(
59 Flag::new("port")
60 .short('p')
61 .usage("Port to listen on")
62 .value_type(FlagType::Int),
63 )
64 .pre_run(|ctx| {
65 log_hook("Start PreRun");
66 let port = ctx
67 .flag("port")
68 .and_then(|s| s.parse::<u16>().ok())
69 .unwrap_or(8080);
70 println!(" -> Validating port {}", port);
71 Ok(())
72 })
73 .run(|ctx| {
74 log_hook("Start Run");
75 let port = ctx
76 .flag("port")
77 .and_then(|s| s.parse::<u16>().ok())
78 .unwrap_or(8080);
79 println!(" -> Starting server on port {}", port);
80 Ok(())
81 })
82 .post_run(|_ctx| {
83 log_hook("Start PostRun");
84 println!(" -> Server started successfully");
85 Ok(())
86 })
87 .build(),
88 )
89 .subcommand(
90 CommandBuilder::new("stop")
91 .short("Stop the server")
92 .pre_run(|_ctx| {
93 log_hook("Stop PreRun");
94 println!(" -> Checking if server is running");
95 Ok(())
96 })
97 .run(|_ctx| {
98 log_hook("Stop Run");
99 println!(" -> Stopping server");
100 Ok(())
101 })
102 .post_run(|_ctx| {
103 log_hook("Stop PostRun");
104 println!(" -> Server stopped");
105 Ok(())
106 })
107 .build(),
108 )
109 .build(),
110 )
111 .subcommand(
112 CommandBuilder::new("database")
113 .short("Database management commands")
114 .persistent_pre_run(|_ctx| {
115 log_hook("Database PersistentPreRun");
116 println!(" -> Connecting to database");
117 Ok(())
118 })
119 .persistent_post_run(|_ctx| {
120 log_hook("Database PersistentPostRun");
121 println!(" -> Closing database connection");
122 Ok(())
123 })
124 .subcommand(
125 CommandBuilder::new("migrate")
126 .short("Run database migrations")
127 .pre_run(|_ctx| {
128 log_hook("Migrate PreRun");
129 println!(" -> Checking migration status");
130 Ok(())
131 })
132 .run(|_ctx| {
133 log_hook("Migrate Run");
134 println!(" -> Running migrations");
135 Ok(())
136 })
137 .post_run(|_ctx| {
138 log_hook("Migrate PostRun");
139 println!(" -> Migrations completed");
140 Ok(())
141 })
142 .build(),
143 )
144 .build(),
145 )
146 .build();
147
148 println!("=== Lifecycle Hooks Demo ===\n");
149 println!("This demo shows the execution order of lifecycle hooks.");
150 println!("Try these commands to see different hook patterns:\n");
151 println!(
152 "1. {} server start",
153 std::env::args().next().unwrap_or_default()
154 );
155 println!(
156 "2. {} -v server start -p 3000",
157 std::env::args().next().unwrap_or_default()
158 );
159 println!(
160 "3. {} database migrate",
161 std::env::args().next().unwrap_or_default()
162 );
163 println!("\n---\n");
164
165 // Reset counter before execution
166 COUNTER.store(0, Ordering::SeqCst);
167
168 let args: Vec<String> = std::env::args().skip(1).collect();
169 if let Err(e) = app.execute(args) {
170 eprintln!("Error: {}", e);
171 std::process::exit(1);
172 }
173
174 println!("\n=== Hook Execution Summary ===");
175 println!("Total hooks executed: {}", COUNTER.load(Ordering::SeqCst));
176 println!("\nExecution order:");
177 println!("1. Parent PersistentPreRun (root → child)");
178 println!("2. Command PreRun");
179 println!("3. Command Run");
180 println!("4. Command PostRun");
181 println!("5. Parent PersistentPostRun (child → root)");
182}Sourcepub fn pre_run<F>(self, f: F) -> Self
pub fn pre_run<F>(self, f: F) -> Self
Sets the pre-run hook for this command
This hook runs only for this specific command, after any persistent pre-run hooks but before the main run function.
§Examples
use flag_rs::CommandBuilder;
let cmd = CommandBuilder::new("deploy")
.pre_run(|ctx| {
println!("Validating deployment configuration...");
Ok(())
})
.run(|ctx| {
println!("Deploying application...");
Ok(())
})
.build();Examples found in repository?
17fn main() {
18 let app = CommandBuilder::new("lifecycle-demo")
19 .short("Demonstrates lifecycle hooks")
20 .long("This example shows how lifecycle hooks execute in the proper order")
21 .flag(
22 Flag::new("verbose")
23 .short('v')
24 .usage("Enable verbose output")
25 .value_type(FlagType::Bool),
26 )
27 // Persistent hooks run for this command and all subcommands
28 .persistent_pre_run(|ctx| {
29 log_hook("Root PersistentPreRun");
30 if ctx.flag("verbose").is_some() {
31 println!(" -> Verbose mode enabled globally");
32 }
33 Ok(())
34 })
35 .persistent_post_run(|ctx| {
36 log_hook("Root PersistentPostRun");
37 if ctx.flag("verbose").is_some() {
38 println!(" -> Cleaning up verbose logging");
39 }
40 Ok(())
41 })
42 .subcommand(
43 CommandBuilder::new("server")
44 .short("Server management commands")
45 .persistent_pre_run(|_ctx| {
46 log_hook("Server PersistentPreRun");
47 println!(" -> Initializing server module");
48 Ok(())
49 })
50 .persistent_post_run(|_ctx| {
51 log_hook("Server PersistentPostRun");
52 println!(" -> Cleaning up server module");
53 Ok(())
54 })
55 .subcommand(
56 CommandBuilder::new("start")
57 .short("Start the server")
58 .flag(
59 Flag::new("port")
60 .short('p')
61 .usage("Port to listen on")
62 .value_type(FlagType::Int),
63 )
64 .pre_run(|ctx| {
65 log_hook("Start PreRun");
66 let port = ctx
67 .flag("port")
68 .and_then(|s| s.parse::<u16>().ok())
69 .unwrap_or(8080);
70 println!(" -> Validating port {}", port);
71 Ok(())
72 })
73 .run(|ctx| {
74 log_hook("Start Run");
75 let port = ctx
76 .flag("port")
77 .and_then(|s| s.parse::<u16>().ok())
78 .unwrap_or(8080);
79 println!(" -> Starting server on port {}", port);
80 Ok(())
81 })
82 .post_run(|_ctx| {
83 log_hook("Start PostRun");
84 println!(" -> Server started successfully");
85 Ok(())
86 })
87 .build(),
88 )
89 .subcommand(
90 CommandBuilder::new("stop")
91 .short("Stop the server")
92 .pre_run(|_ctx| {
93 log_hook("Stop PreRun");
94 println!(" -> Checking if server is running");
95 Ok(())
96 })
97 .run(|_ctx| {
98 log_hook("Stop Run");
99 println!(" -> Stopping server");
100 Ok(())
101 })
102 .post_run(|_ctx| {
103 log_hook("Stop PostRun");
104 println!(" -> Server stopped");
105 Ok(())
106 })
107 .build(),
108 )
109 .build(),
110 )
111 .subcommand(
112 CommandBuilder::new("database")
113 .short("Database management commands")
114 .persistent_pre_run(|_ctx| {
115 log_hook("Database PersistentPreRun");
116 println!(" -> Connecting to database");
117 Ok(())
118 })
119 .persistent_post_run(|_ctx| {
120 log_hook("Database PersistentPostRun");
121 println!(" -> Closing database connection");
122 Ok(())
123 })
124 .subcommand(
125 CommandBuilder::new("migrate")
126 .short("Run database migrations")
127 .pre_run(|_ctx| {
128 log_hook("Migrate PreRun");
129 println!(" -> Checking migration status");
130 Ok(())
131 })
132 .run(|_ctx| {
133 log_hook("Migrate Run");
134 println!(" -> Running migrations");
135 Ok(())
136 })
137 .post_run(|_ctx| {
138 log_hook("Migrate PostRun");
139 println!(" -> Migrations completed");
140 Ok(())
141 })
142 .build(),
143 )
144 .build(),
145 )
146 .build();
147
148 println!("=== Lifecycle Hooks Demo ===\n");
149 println!("This demo shows the execution order of lifecycle hooks.");
150 println!("Try these commands to see different hook patterns:\n");
151 println!(
152 "1. {} server start",
153 std::env::args().next().unwrap_or_default()
154 );
155 println!(
156 "2. {} -v server start -p 3000",
157 std::env::args().next().unwrap_or_default()
158 );
159 println!(
160 "3. {} database migrate",
161 std::env::args().next().unwrap_or_default()
162 );
163 println!("\n---\n");
164
165 // Reset counter before execution
166 COUNTER.store(0, Ordering::SeqCst);
167
168 let args: Vec<String> = std::env::args().skip(1).collect();
169 if let Err(e) = app.execute(args) {
170 eprintln!("Error: {}", e);
171 std::process::exit(1);
172 }
173
174 println!("\n=== Hook Execution Summary ===");
175 println!("Total hooks executed: {}", COUNTER.load(Ordering::SeqCst));
176 println!("\nExecution order:");
177 println!("1. Parent PersistentPreRun (root → child)");
178 println!("2. Command PreRun");
179 println!("3. Command Run");
180 println!("4. Command PostRun");
181 println!("5. Parent PersistentPostRun (child → root)");
182}Sourcepub fn post_run<F>(self, f: F) -> Self
pub fn post_run<F>(self, f: F) -> Self
Sets the post-run hook for this command
This hook runs only for this specific command, after the main run function but before any persistent post-run hooks.
§Examples
use flag_rs::CommandBuilder;
let cmd = CommandBuilder::new("test")
.run(|ctx| {
println!("Running tests...");
Ok(())
})
.post_run(|ctx| {
println!("Generating test report...");
Ok(())
})
.build();Examples found in repository?
17fn main() {
18 let app = CommandBuilder::new("lifecycle-demo")
19 .short("Demonstrates lifecycle hooks")
20 .long("This example shows how lifecycle hooks execute in the proper order")
21 .flag(
22 Flag::new("verbose")
23 .short('v')
24 .usage("Enable verbose output")
25 .value_type(FlagType::Bool),
26 )
27 // Persistent hooks run for this command and all subcommands
28 .persistent_pre_run(|ctx| {
29 log_hook("Root PersistentPreRun");
30 if ctx.flag("verbose").is_some() {
31 println!(" -> Verbose mode enabled globally");
32 }
33 Ok(())
34 })
35 .persistent_post_run(|ctx| {
36 log_hook("Root PersistentPostRun");
37 if ctx.flag("verbose").is_some() {
38 println!(" -> Cleaning up verbose logging");
39 }
40 Ok(())
41 })
42 .subcommand(
43 CommandBuilder::new("server")
44 .short("Server management commands")
45 .persistent_pre_run(|_ctx| {
46 log_hook("Server PersistentPreRun");
47 println!(" -> Initializing server module");
48 Ok(())
49 })
50 .persistent_post_run(|_ctx| {
51 log_hook("Server PersistentPostRun");
52 println!(" -> Cleaning up server module");
53 Ok(())
54 })
55 .subcommand(
56 CommandBuilder::new("start")
57 .short("Start the server")
58 .flag(
59 Flag::new("port")
60 .short('p')
61 .usage("Port to listen on")
62 .value_type(FlagType::Int),
63 )
64 .pre_run(|ctx| {
65 log_hook("Start PreRun");
66 let port = ctx
67 .flag("port")
68 .and_then(|s| s.parse::<u16>().ok())
69 .unwrap_or(8080);
70 println!(" -> Validating port {}", port);
71 Ok(())
72 })
73 .run(|ctx| {
74 log_hook("Start Run");
75 let port = ctx
76 .flag("port")
77 .and_then(|s| s.parse::<u16>().ok())
78 .unwrap_or(8080);
79 println!(" -> Starting server on port {}", port);
80 Ok(())
81 })
82 .post_run(|_ctx| {
83 log_hook("Start PostRun");
84 println!(" -> Server started successfully");
85 Ok(())
86 })
87 .build(),
88 )
89 .subcommand(
90 CommandBuilder::new("stop")
91 .short("Stop the server")
92 .pre_run(|_ctx| {
93 log_hook("Stop PreRun");
94 println!(" -> Checking if server is running");
95 Ok(())
96 })
97 .run(|_ctx| {
98 log_hook("Stop Run");
99 println!(" -> Stopping server");
100 Ok(())
101 })
102 .post_run(|_ctx| {
103 log_hook("Stop PostRun");
104 println!(" -> Server stopped");
105 Ok(())
106 })
107 .build(),
108 )
109 .build(),
110 )
111 .subcommand(
112 CommandBuilder::new("database")
113 .short("Database management commands")
114 .persistent_pre_run(|_ctx| {
115 log_hook("Database PersistentPreRun");
116 println!(" -> Connecting to database");
117 Ok(())
118 })
119 .persistent_post_run(|_ctx| {
120 log_hook("Database PersistentPostRun");
121 println!(" -> Closing database connection");
122 Ok(())
123 })
124 .subcommand(
125 CommandBuilder::new("migrate")
126 .short("Run database migrations")
127 .pre_run(|_ctx| {
128 log_hook("Migrate PreRun");
129 println!(" -> Checking migration status");
130 Ok(())
131 })
132 .run(|_ctx| {
133 log_hook("Migrate Run");
134 println!(" -> Running migrations");
135 Ok(())
136 })
137 .post_run(|_ctx| {
138 log_hook("Migrate PostRun");
139 println!(" -> Migrations completed");
140 Ok(())
141 })
142 .build(),
143 )
144 .build(),
145 )
146 .build();
147
148 println!("=== Lifecycle Hooks Demo ===\n");
149 println!("This demo shows the execution order of lifecycle hooks.");
150 println!("Try these commands to see different hook patterns:\n");
151 println!(
152 "1. {} server start",
153 std::env::args().next().unwrap_or_default()
154 );
155 println!(
156 "2. {} -v server start -p 3000",
157 std::env::args().next().unwrap_or_default()
158 );
159 println!(
160 "3. {} database migrate",
161 std::env::args().next().unwrap_or_default()
162 );
163 println!("\n---\n");
164
165 // Reset counter before execution
166 COUNTER.store(0, Ordering::SeqCst);
167
168 let args: Vec<String> = std::env::args().skip(1).collect();
169 if let Err(e) = app.execute(args) {
170 eprintln!("Error: {}", e);
171 std::process::exit(1);
172 }
173
174 println!("\n=== Hook Execution Summary ===");
175 println!("Total hooks executed: {}", COUNTER.load(Ordering::SeqCst));
176 println!("\nExecution order:");
177 println!("1. Parent PersistentPreRun (root → child)");
178 println!("2. Command PreRun");
179 println!("3. Command Run");
180 println!("4. Command PostRun");
181 println!("5. Parent PersistentPostRun (child → root)");
182}Sourcepub fn persistent_post_run<F>(self, f: F) -> Self
pub fn persistent_post_run<F>(self, f: F) -> Self
Sets the persistent post-run hook for this command
This hook runs after the command and all its subcommands. It’s inherited by all subcommands and runs in child-to-parent order.
§Examples
use flag_rs::CommandBuilder;
let cmd = CommandBuilder::new("app")
.persistent_post_run(|ctx| {
println!("Cleaning up resources...");
Ok(())
})
.build();Examples found in repository?
17fn main() {
18 let app = CommandBuilder::new("lifecycle-demo")
19 .short("Demonstrates lifecycle hooks")
20 .long("This example shows how lifecycle hooks execute in the proper order")
21 .flag(
22 Flag::new("verbose")
23 .short('v')
24 .usage("Enable verbose output")
25 .value_type(FlagType::Bool),
26 )
27 // Persistent hooks run for this command and all subcommands
28 .persistent_pre_run(|ctx| {
29 log_hook("Root PersistentPreRun");
30 if ctx.flag("verbose").is_some() {
31 println!(" -> Verbose mode enabled globally");
32 }
33 Ok(())
34 })
35 .persistent_post_run(|ctx| {
36 log_hook("Root PersistentPostRun");
37 if ctx.flag("verbose").is_some() {
38 println!(" -> Cleaning up verbose logging");
39 }
40 Ok(())
41 })
42 .subcommand(
43 CommandBuilder::new("server")
44 .short("Server management commands")
45 .persistent_pre_run(|_ctx| {
46 log_hook("Server PersistentPreRun");
47 println!(" -> Initializing server module");
48 Ok(())
49 })
50 .persistent_post_run(|_ctx| {
51 log_hook("Server PersistentPostRun");
52 println!(" -> Cleaning up server module");
53 Ok(())
54 })
55 .subcommand(
56 CommandBuilder::new("start")
57 .short("Start the server")
58 .flag(
59 Flag::new("port")
60 .short('p')
61 .usage("Port to listen on")
62 .value_type(FlagType::Int),
63 )
64 .pre_run(|ctx| {
65 log_hook("Start PreRun");
66 let port = ctx
67 .flag("port")
68 .and_then(|s| s.parse::<u16>().ok())
69 .unwrap_or(8080);
70 println!(" -> Validating port {}", port);
71 Ok(())
72 })
73 .run(|ctx| {
74 log_hook("Start Run");
75 let port = ctx
76 .flag("port")
77 .and_then(|s| s.parse::<u16>().ok())
78 .unwrap_or(8080);
79 println!(" -> Starting server on port {}", port);
80 Ok(())
81 })
82 .post_run(|_ctx| {
83 log_hook("Start PostRun");
84 println!(" -> Server started successfully");
85 Ok(())
86 })
87 .build(),
88 )
89 .subcommand(
90 CommandBuilder::new("stop")
91 .short("Stop the server")
92 .pre_run(|_ctx| {
93 log_hook("Stop PreRun");
94 println!(" -> Checking if server is running");
95 Ok(())
96 })
97 .run(|_ctx| {
98 log_hook("Stop Run");
99 println!(" -> Stopping server");
100 Ok(())
101 })
102 .post_run(|_ctx| {
103 log_hook("Stop PostRun");
104 println!(" -> Server stopped");
105 Ok(())
106 })
107 .build(),
108 )
109 .build(),
110 )
111 .subcommand(
112 CommandBuilder::new("database")
113 .short("Database management commands")
114 .persistent_pre_run(|_ctx| {
115 log_hook("Database PersistentPreRun");
116 println!(" -> Connecting to database");
117 Ok(())
118 })
119 .persistent_post_run(|_ctx| {
120 log_hook("Database PersistentPostRun");
121 println!(" -> Closing database connection");
122 Ok(())
123 })
124 .subcommand(
125 CommandBuilder::new("migrate")
126 .short("Run database migrations")
127 .pre_run(|_ctx| {
128 log_hook("Migrate PreRun");
129 println!(" -> Checking migration status");
130 Ok(())
131 })
132 .run(|_ctx| {
133 log_hook("Migrate Run");
134 println!(" -> Running migrations");
135 Ok(())
136 })
137 .post_run(|_ctx| {
138 log_hook("Migrate PostRun");
139 println!(" -> Migrations completed");
140 Ok(())
141 })
142 .build(),
143 )
144 .build(),
145 )
146 .build();
147
148 println!("=== Lifecycle Hooks Demo ===\n");
149 println!("This demo shows the execution order of lifecycle hooks.");
150 println!("Try these commands to see different hook patterns:\n");
151 println!(
152 "1. {} server start",
153 std::env::args().next().unwrap_or_default()
154 );
155 println!(
156 "2. {} -v server start -p 3000",
157 std::env::args().next().unwrap_or_default()
158 );
159 println!(
160 "3. {} database migrate",
161 std::env::args().next().unwrap_or_default()
162 );
163 println!("\n---\n");
164
165 // Reset counter before execution
166 COUNTER.store(0, Ordering::SeqCst);
167
168 let args: Vec<String> = std::env::args().skip(1).collect();
169 if let Err(e) = app.execute(args) {
170 eprintln!("Error: {}", e);
171 std::process::exit(1);
172 }
173
174 println!("\n=== Hook Execution Summary ===");
175 println!("Total hooks executed: {}", COUNTER.load(Ordering::SeqCst));
176 println!("\nExecution order:");
177 println!("1. Parent PersistentPreRun (root → child)");
178 println!("2. Command PreRun");
179 println!("3. Command Run");
180 println!("4. Command PostRun");
181 println!("5. Parent PersistentPostRun (child → root)");
182}Sourcepub fn arg_completion<F>(self, f: F) -> Self
pub fn arg_completion<F>(self, f: F) -> Self
Sets the argument completion function
This function is called when the user presses TAB to complete arguments. It enables dynamic completions based on runtime state.
§Examples
use flag_rs::{CommandBuilder, CompletionResult};
let cmd = CommandBuilder::new("edit")
.arg_completion(|ctx, prefix| {
// In a real app, list files from the filesystem
let files = vec!["main.rs", "lib.rs", "Cargo.toml"];
Ok(CompletionResult::new().extend(
files.into_iter()
.filter(|f| f.starts_with(prefix))
.map(String::from)
))
})
.build();Examples found in repository?
79fn build_get_pods() -> Command {
80 CommandBuilder::new("pods")
81 .aliases(vec!["po", "pod"])
82 .short("List pods")
83 .arg_completion(|ctx, prefix| {
84 // This is the key feature - dynamic completion based on runtime state!
85 let namespace = ctx
86 .flag("namespace")
87 .map(String::as_str)
88 .unwrap_or("default");
89
90 // In real kubectl, this would query the K8s API
91 let pods = get_pods_with_status(namespace);
92 let mut result = CompletionResult::new();
93
94 for (pod_name, status) in pods {
95 if pod_name.starts_with(prefix) {
96 result = result.add_with_description(pod_name, status);
97 }
98 }
99
100 Ok(result)
101 })
102 .run(|ctx| {
103 let namespace = ctx
104 .flag("namespace")
105 .map(String::as_str)
106 .unwrap_or("default");
107
108 println!("Listing pods in namespace: {namespace}");
109
110 if let Some(pod_name) = ctx.args().first() {
111 println!("Getting specific pod: {pod_name}");
112 } else {
113 for pod in get_pods_in_namespace(namespace) {
114 println!("pod/{}", pod);
115 }
116 }
117
118 Ok(())
119 })
120 .build()
121}
122
123fn build_get_services() -> Command {
124 CommandBuilder::new("services")
125 .aliases(vec!["svc", "service"])
126 .short("List services")
127 .arg_completion(|ctx, prefix| {
128 let namespace = ctx
129 .flag("namespace")
130 .map(String::as_str)
131 .unwrap_or("default");
132
133 let services = get_services_in_namespace(namespace);
134 Ok(CompletionResult::new()
135 .extend(services.into_iter().filter(|svc| svc.starts_with(prefix))))
136 })
137 .run(|ctx| {
138 let namespace = ctx
139 .flag("namespace")
140 .map(String::as_str)
141 .unwrap_or("default");
142
143 println!("Listing services in namespace: {namespace}");
144 Ok(())
145 })
146 .build()
147}
148
149fn build_get_deployments() -> Command {
150 CommandBuilder::new("deployments")
151 .aliases(vec!["deploy", "deployment"])
152 .short("List deployments")
153 .run(|ctx| {
154 let namespace = ctx
155 .flag("namespace")
156 .map(String::as_str)
157 .unwrap_or("default");
158
159 println!("Listing deployments in namespace: {namespace}");
160 Ok(())
161 })
162 .build()
163}
164
165fn build_describe_command() -> Command {
166 CommandBuilder::new("describe")
167 .short("Show details of a specific resource")
168 .run(|_ctx| {
169 println!("Describe command - add resource type subcommands");
170 Ok(())
171 })
172 .build()
173}
174
175fn build_delete_command() -> Command {
176 CommandBuilder::new("delete")
177 .short("Delete resources")
178 .run(|_ctx| {
179 println!("Delete command - add resource type subcommands");
180 Ok(())
181 })
182 .build()
183}
184
185// Mock functions that would normally query the Kubernetes API
186fn get_namespaces_with_descriptions() -> Vec<(String, String)> {
187 vec![
188 (
189 "default".to_string(),
190 "Default namespace for user workloads".to_string(),
191 ),
192 (
193 "kube-system".to_string(),
194 "Kubernetes system components".to_string(),
195 ),
196 (
197 "kube-public".to_string(),
198 "Public resources accessible to all users".to_string(),
199 ),
200 (
201 "development".to_string(),
202 "Development environment".to_string(),
203 ),
204 (
205 "staging".to_string(),
206 "Staging environment for pre-production testing".to_string(),
207 ),
208 (
209 "production".to_string(),
210 "Production environment (CAUTION)".to_string(),
211 ),
212 ]
213}
214
215fn get_pods_with_status(namespace: &str) -> Vec<(String, String)> {
216 use std::time::{SystemTime, UNIX_EPOCH};
217
218 // Generate random suffix based on current time
219 let timestamp = SystemTime::now()
220 .duration_since(UNIX_EPOCH)
221 .unwrap()
222 .as_secs();
223
224 // Simple pseudo-random generation
225 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
226 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
227 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
228
229 match namespace {
230 "default" => vec![
231 (
232 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
233 "Running (2/2 containers)".to_string(),
234 ),
235 (
236 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
237 "Running (2/2 containers)".to_string(),
238 ),
239 (
240 "redis-master-0".to_string(),
241 "Running (1/1 containers)".to_string(),
242 ),
243 (
244 "redis-slave-0".to_string(),
245 "Running (1/1 containers)".to_string(),
246 ),
247 ("redis-slave-1".to_string(), "Pending".to_string()),
248 ],
249 "kube-system" => vec![
250 (
251 format!("coredns-5d78c9869d-{:05x}", rand1),
252 "Running (1/1 containers)".to_string(),
253 ),
254 (
255 format!("coredns-5d78c9869d-{:05x}", rand2),
256 "Running (1/1 containers)".to_string(),
257 ),
258 ("etcd-minikube".to_string(), "Running".to_string()),
259 ("kube-apiserver-minikube".to_string(), "Running".to_string()),
260 (
261 "kube-controller-manager-minikube".to_string(),
262 "Running".to_string(),
263 ),
264 (format!("kube-proxy-{:05x}", rand3), "Running".to_string()),
265 ("kube-scheduler-minikube".to_string(), "Running".to_string()),
266 ],
267 _ => vec![],
268 }
269}
270
271fn get_pods_in_namespace(namespace: &str) -> Vec<String> {
272 use std::time::{SystemTime, UNIX_EPOCH};
273
274 // Generate random suffix based on current time
275 let timestamp = SystemTime::now()
276 .duration_since(UNIX_EPOCH)
277 .unwrap()
278 .as_secs();
279
280 // Simple pseudo-random generation
281 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
282 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
283 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
284
285 match namespace {
286 "default" => vec![
287 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
288 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
289 format!("redis-master-0"),
290 format!("redis-slave-0"),
291 format!("redis-slave-1"),
292 ],
293 "kube-system" => vec![
294 format!("coredns-5d78c9869d-{:05x}", rand1),
295 format!("coredns-5d78c9869d-{:05x}", rand2),
296 format!("etcd-minikube"),
297 format!("kube-apiserver-minikube"),
298 format!("kube-controller-manager-minikube"),
299 format!("kube-proxy-{:05x}", rand3),
300 format!("kube-scheduler-minikube"),
301 ],
302 _ => vec![],
303 }
304}
305
306fn get_services_in_namespace(namespace: &str) -> Vec<String> {
307 match namespace {
308 "default" => vec![
309 "kubernetes".to_string(),
310 "nginx-service".to_string(),
311 "redis-master".to_string(),
312 "redis-slave".to_string(),
313 ],
314 "kube-system" => vec!["kube-dns".to_string()],
315 _ => vec![],
316 }
317}
318
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}More examples
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}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}30fn main() {
31 let app = CommandBuilder::new("cloud-cli")
32 .short("Cloud CLI with timeout-protected completions")
33 .flag(
34 Flag::new("region")
35 .short('r')
36 .usage("Cloud region")
37 .value_type(FlagType::String)
38 .default(flag_rs::FlagValue::String("us-east-1".to_string())),
39 )
40 .subcommand(
41 CommandBuilder::new("instances")
42 .short("Manage cloud instances")
43 .subcommand(
44 CommandBuilder::new("list")
45 .short("List instances (fast completion)")
46 .arg_completion(|_ctx, prefix| {
47 // Fast completion - no timeout needed
48 let instances = vec![
49 "web-server-001",
50 "web-server-002",
51 "db-primary",
52 "db-replica",
53 "cache-node-1",
54 "cache-node-2",
55 ];
56
57 Ok(CompletionResult::new().extend(
58 instances
59 .into_iter()
60 .filter(|i| i.starts_with(prefix))
61 .map(String::from),
62 ))
63 })
64 .build(),
65 )
66 .subcommand(
67 CommandBuilder::new("describe")
68 .short("Describe instance (slow completion with timeout)")
69 .arg_completion({
70 // Wrap slow completion with timeout
71 make_timeout_completion(
72 Duration::from_millis(500), // 500ms timeout
73 |ctx, prefix| {
74 eprintln!("Fetching instance details from API...");
75
76 // Simulate slow API call (1 second)
77 let instances = slow_completion(prefix, 1000);
78
79 let mut result = CompletionResult::new();
80 for instance in instances {
81 result = result.add_with_description(
82 instance.clone(),
83 format!(
84 "Instance in region {}",
85 ctx.flag("region")
86 .unwrap_or(&"us-east-1".to_string())
87 ),
88 );
89 }
90
91 if prefix.is_empty() {
92 result = result.add_help_text(
93 "Fetching live instance data from cloud API...",
94 );
95 }
96
97 Ok(result)
98 },
99 )
100 })
101 .build(),
102 )
103 .subcommand(
104 CommandBuilder::new("create")
105 .short("Create instance (very slow completion)")
106 .arg_completion({
107 // Use default timeout (2 seconds)
108 make_timeout_completion(DEFAULT_COMPLETION_TIMEOUT, |_ctx, prefix| {
109 eprintln!("Checking available instance types...");
110
111 // Simulate very slow operation (3 seconds)
112 let types = slow_completion(prefix, 3000);
113
114 Ok(CompletionResult::new()
115 .extend(types)
116 .add_help_text("Available instance types"))
117 })
118 })
119 .build(),
120 )
121 .build(),
122 )
123 .build();
124
125 println!("=== Timeout Completion Demo ===\n");
126 println!("This demo shows how timeouts protect against slow completion operations.\n");
127 println!("To test timeout handling:");
128 println!("1. Set up bash completion:");
129 println!(
130 " source <({} completion bash)",
131 std::env::args().next().unwrap_or_default()
132 );
133 println!();
134 println!("2. Test different completion speeds:");
135 println!();
136 println!(" Fast (no timeout needed):");
137 println!(
138 " {} instances list <TAB>",
139 std::env::args().next().unwrap_or_default()
140 );
141 println!();
142 println!(" Slow (500ms timeout, 1s operation - will timeout):");
143 println!(
144 " {} instances describe <TAB>",
145 std::env::args().next().unwrap_or_default()
146 );
147 println!();
148 println!(" Very slow (2s timeout, 3s operation - will timeout):");
149 println!(
150 " {} instances create <TAB>",
151 std::env::args().next().unwrap_or_default()
152 );
153 println!();
154 println!("When a timeout occurs, you'll see:");
155 println!("- A warning message about the timeout");
156 println!("- Any partial results that were available");
157 println!("- Suggestion to use a more specific prefix");
158 println!("\n---\n");
159
160 let args: Vec<String> = std::env::args().skip(1).collect();
161 if let Err(e) = app.execute(args) {
162 eprintln!("{}", e);
163 std::process::exit(1);
164 }
165}38fn main() {
39 // Create a shared cache with 10-second TTL
40 let cache = Arc::new(CompletionCache::new(Duration::from_secs(10)));
41
42 let app = CommandBuilder::new("kubectl")
43 .short("Kubernetes command-line tool with cached completions")
44 .flag(
45 Flag::new("namespace")
46 .short('n')
47 .usage("The namespace scope for this CLI request")
48 .value_type(FlagType::String)
49 .default(flag_rs::FlagValue::String("default".to_string())),
50 )
51 .subcommand({
52 let cache_clone = Arc::clone(&cache);
53 CommandBuilder::new("get")
54 .short("Display one or many resources")
55 .subcommand({
56 let cache_for_pods = Arc::clone(&cache_clone);
57 CommandBuilder::new("pods")
58 .short("List pods with cached completion")
59 .arg_completion(move |ctx, prefix| {
60 let start = Instant::now();
61
62 // Generate cache key
63 let cache_key = CompletionCache::make_key(
64 &["kubectl".to_string(), "get".to_string(), "pods".to_string()],
65 prefix,
66 ctx.flags(),
67 );
68
69 // Try to get from cache first
70 if let Some(cached_result) = cache_for_pods.get(&cache_key) {
71 eprintln!("✓ Cache hit! Completed in {:?}", start.elapsed());
72 return Ok(cached_result);
73 }
74
75 eprintln!("✗ Cache miss - fetching completions...");
76
77 // Perform expensive operation
78 let items = expensive_completion(prefix);
79 let mut result = CompletionResult::new();
80
81 for item in items {
82 result = result.add_with_description(
83 item.clone(),
84 format!(
85 "Pod in namespace {}",
86 ctx.flag("namespace").unwrap_or(&"default".to_string())
87 ),
88 );
89 }
90
91 // Add contextual help
92 if prefix.is_empty() {
93 result =
94 result.add_help_text("Tip: Start typing to filter pod names");
95 }
96
97 // Cache the result
98 cache_for_pods.put(cache_key, result.clone());
99
100 eprintln!(
101 "✓ Completed in {:?} (cached for future use)",
102 start.elapsed()
103 );
104 Ok(result)
105 })
106 .build()
107 })
108 .subcommand({
109 let cache_for_services = Arc::clone(&cache_clone);
110 CommandBuilder::new("services")
111 .short("List services with cached completion")
112 .arg_completion(move |ctx, prefix| {
113 let cache_key = CompletionCache::make_key(
114 &[
115 "kubectl".to_string(),
116 "get".to_string(),
117 "services".to_string(),
118 ],
119 prefix,
120 ctx.flags(),
121 );
122
123 if let Some(cached) = cache_for_services.get(&cache_key) {
124 eprintln!("✓ Using cached service completions");
125 return Ok(cached);
126 }
127
128 // Simulate expensive operation
129 let services = expensive_completion(prefix);
130 let result = CompletionResult::new().extend(services);
131
132 cache_for_services.put(cache_key, result.clone());
133 Ok(result)
134 })
135 .build()
136 })
137 .build()
138 })
139 .build();
140
141 println!("=== Cached Completion Demo ===\n");
142 println!("This demo shows how completion caching improves performance.\n");
143 println!("To test completions with caching:");
144 println!("1. Set up bash completion:");
145 println!(
146 " source <({} completion bash)",
147 std::env::args().next().unwrap_or_default()
148 );
149 println!();
150 println!("2. Try tab completion multiple times:");
151 println!(
152 " {} get pods n<TAB> # First time: ~500ms (cache miss)",
153 std::env::args().next().unwrap_or_default()
154 );
155 println!(
156 " {} get pods n<TAB> # Second time: <1ms (cache hit!)",
157 std::env::args().next().unwrap_or_default()
158 );
159 println!();
160 println!("3. Different prefixes have separate cache entries:");
161 println!(
162 " {} get pods r<TAB> # Different prefix = cache miss",
163 std::env::args().next().unwrap_or_default()
164 );
165 println!();
166 println!("4. Flags affect the cache key:");
167 println!(
168 " {} -n prod get pods n<TAB> # Different namespace = different cache",
169 std::env::args().next().unwrap_or_default()
170 );
171 println!();
172 println!("Note: Cache entries expire after 10 seconds in this demo.\n");
173 println!("---\n");
174
175 let args: Vec<String> = std::env::args().skip(1).collect();
176
177 // Handle completion requests
178 if std::env::var("KUBECTL_COMPLETE").is_ok() {
179 // When testing completions, show cache status
180 eprintln!("Cache size: {} entries", cache.size());
181 }
182
183 if let Err(e) = app.execute(args) {
184 eprintln!("{}", e);
185 std::process::exit(1);
186 }
187}8fn main() {
9 let app = CommandBuilder::new("active-help-demo")
10 .short("Demonstrates ActiveHelp system")
11 .long("This example shows how ActiveHelp provides contextual hints during tab completion")
12 .flag(
13 Flag::new("namespace")
14 .short('n')
15 .usage("Kubernetes namespace")
16 .value_type(FlagType::String),
17 )
18 .flag(
19 Flag::new("output")
20 .short('o')
21 .usage("Output format")
22 .value_type(FlagType::String),
23 )
24 .subcommand(
25 CommandBuilder::new("get")
26 .short("Get resources")
27 .subcommand(
28 CommandBuilder::new("pods")
29 .short("Get pods")
30 .arg_completion(|ctx, prefix| {
31 let mut result = CompletionResult::new();
32
33 // Add contextual help based on current state
34 if ctx.flag("namespace").is_none() {
35 result = result.add_help_text(
36 "Tip: Use -n <namespace> to list pods from a specific namespace",
37 );
38 }
39
40 if ctx.flag("output").is_none() {
41 result = result.add_conditional_help(
42 "Use -o json for machine-readable output",
43 |_| true, // Always show this tip
44 );
45 }
46
47 // Simulate pod completions
48 let namespace = ctx
49 .flag("namespace")
50 .map(String::as_str)
51 .unwrap_or("default");
52
53 let pods = match namespace {
54 "default" => vec![
55 ("nginx-abc123", "Running"),
56 ("redis-def456", "Running"),
57 ("postgres-ghi789", "CrashLoopBackOff"),
58 ],
59 "kube-system" => vec![
60 ("coredns-xyz789", "Running"),
61 ("kube-proxy-abc123", "Running"),
62 ("etcd-master", "Running"),
63 ],
64 _ => vec![],
65 };
66
67 // Add pods with status descriptions
68 for (pod, status) in &pods {
69 if pod.starts_with(prefix) {
70 result = result.add_with_description(
71 *pod,
72 format!("Status: {status}"),
73 );
74 }
75 }
76
77 // Add help if there's a problematic pod
78 if pods.iter().any(|(_, status)| status == &"CrashLoopBackOff") {
79 result = result.add_help_text(
80 "Warning: Some pods are in CrashLoopBackOff state. Use 'describe' to investigate.",
81 );
82 }
83
84 Ok(result)
85 })
86 .run(|ctx| {
87 let namespace = ctx
88 .flag("namespace")
89 .map(String::as_str)
90 .unwrap_or("default");
91 let output = ctx.flag("output").map(String::as_str).unwrap_or("table");
92
93 println!("Getting pods from namespace: {namespace}");
94 println!("Output format: {output}");
95
96 if let Some(pod) = ctx.args().first() {
97 println!("Getting specific pod: {pod}");
98 } else {
99 println!("Listing all pods");
100 }
101 Ok(())
102 })
103 .build(),
104 )
105 .subcommand(
106 CommandBuilder::new("services")
107 .short("Get services")
108 .arg_completion(|_ctx, _prefix| {
109 let mut result = CompletionResult::new()
110 .add_with_description("nginx-service", "Type: LoadBalancer")
111 .add_with_description("redis-service", "Type: ClusterIP")
112 .add_with_description("postgres-service", "Type: NodePort");
113
114 // Add conditional help based on flags
115 result = result.add_conditional_help(
116 "Tip: Use -o wide to see more details about services",
117 |ctx| {
118 ctx.flag("output")
119 .map(|o| o != "wide")
120 .unwrap_or(true)
121 },
122 );
123
124 Ok(result)
125 })
126 .build(),
127 )
128 .build(),
129 )
130 .subcommand(
131 CommandBuilder::new("create")
132 .short("Create resources")
133 .flag(
134 Flag::new("file")
135 .short('f')
136 .usage("Filename to use to create the resource")
137 .value_type(FlagType::String)
138 .required(),
139 )
140 .flag_completion("file", |_ctx, prefix| {
141 let mut result = CompletionResult::new();
142
143 // Simulate file completions
144 let files = vec![
145 ("deployment.yaml", "Deployment configuration"),
146 ("service.yaml", "Service configuration"),
147 ("configmap.yaml", "ConfigMap configuration"),
148 ("secret.yaml", "Secret configuration"),
149 ("pod.yaml", "Pod configuration"),
150 ];
151
152 for (file, desc) in files {
153 if file.starts_with(prefix) {
154 result = result.add_with_description(file, desc);
155 }
156 }
157
158 // Add contextual help
159 result = result.add_help_text(
160 "Files should be valid Kubernetes manifests in YAML or JSON format",
161 );
162
163 if std::path::Path::new(prefix)
164 .extension()
165 .is_some_and(|ext| ext.eq_ignore_ascii_case("json"))
166 {
167 result = result.add_help_text(
168 "Note: JSON format is supported but YAML is more common in Kubernetes",
169 );
170 }
171
172 Ok(result)
173 })
174 .run(|ctx| {
175 let file = ctx.flag("file").expect("File is required");
176 println!("Creating resources from file: {file}");
177 Ok(())
178 })
179 .build(),
180 )
181 .subcommand(
182 CommandBuilder::new("debug")
183 .short("Debug and troubleshoot resources")
184 .arg_completion(|_ctx, _prefix| {
185 Ok(CompletionResult::new()
186 .add_with_description("pod/nginx-abc123", "Debug a specific pod")
187 .add_with_description("node/worker-1", "Debug a node")
188 .add_with_description("deployment/nginx", "Debug a deployment")
189 .add_help_text("Debug creates an interactive debugging session")
190 .add_help_text("Common debugging commands: kubectl logs, kubectl exec, kubectl describe")
191 .add_conditional_help(
192 "Tip: Use 'kubectl logs -f' to follow log output in real-time",
193 |ctx| ctx.args().is_empty(),
194 ))
195 })
196 .build(),
197 )
198 .build();
199
200 let args: Vec<String> = std::env::args().skip(1).collect();
201 if let Err(e) = app.execute(args) {
202 eprintln!("Error: {e}");
203 std::process::exit(1);
204 }
205}Sourcepub fn flag_completion<F>(self, flag_name: impl Into<String>, f: F) -> Self
pub fn flag_completion<F>(self, flag_name: impl Into<String>, f: F) -> Self
Sets the completion function for a specific flag
§Examples
use flag_rs::{CommandBuilder, CompletionResult, Flag, FlagType};
let cmd = CommandBuilder::new("connect")
.flag(
Flag::new("server")
.usage("Server to connect to")
.value_type(FlagType::String)
)
.flag_completion("server", |ctx, prefix| {
// In a real app, discover available servers
let servers = vec!["prod-1", "prod-2", "staging", "dev"];
Ok(CompletionResult::new().extend(
servers.into_iter()
.filter(|s| s.starts_with(prefix))
.map(String::from)
))
})
.build();Examples found in repository?
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}More examples
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}8fn main() {
9 let app = CommandBuilder::new("active-help-demo")
10 .short("Demonstrates ActiveHelp system")
11 .long("This example shows how ActiveHelp provides contextual hints during tab completion")
12 .flag(
13 Flag::new("namespace")
14 .short('n')
15 .usage("Kubernetes namespace")
16 .value_type(FlagType::String),
17 )
18 .flag(
19 Flag::new("output")
20 .short('o')
21 .usage("Output format")
22 .value_type(FlagType::String),
23 )
24 .subcommand(
25 CommandBuilder::new("get")
26 .short("Get resources")
27 .subcommand(
28 CommandBuilder::new("pods")
29 .short("Get pods")
30 .arg_completion(|ctx, prefix| {
31 let mut result = CompletionResult::new();
32
33 // Add contextual help based on current state
34 if ctx.flag("namespace").is_none() {
35 result = result.add_help_text(
36 "Tip: Use -n <namespace> to list pods from a specific namespace",
37 );
38 }
39
40 if ctx.flag("output").is_none() {
41 result = result.add_conditional_help(
42 "Use -o json for machine-readable output",
43 |_| true, // Always show this tip
44 );
45 }
46
47 // Simulate pod completions
48 let namespace = ctx
49 .flag("namespace")
50 .map(String::as_str)
51 .unwrap_or("default");
52
53 let pods = match namespace {
54 "default" => vec![
55 ("nginx-abc123", "Running"),
56 ("redis-def456", "Running"),
57 ("postgres-ghi789", "CrashLoopBackOff"),
58 ],
59 "kube-system" => vec![
60 ("coredns-xyz789", "Running"),
61 ("kube-proxy-abc123", "Running"),
62 ("etcd-master", "Running"),
63 ],
64 _ => vec![],
65 };
66
67 // Add pods with status descriptions
68 for (pod, status) in &pods {
69 if pod.starts_with(prefix) {
70 result = result.add_with_description(
71 *pod,
72 format!("Status: {status}"),
73 );
74 }
75 }
76
77 // Add help if there's a problematic pod
78 if pods.iter().any(|(_, status)| status == &"CrashLoopBackOff") {
79 result = result.add_help_text(
80 "Warning: Some pods are in CrashLoopBackOff state. Use 'describe' to investigate.",
81 );
82 }
83
84 Ok(result)
85 })
86 .run(|ctx| {
87 let namespace = ctx
88 .flag("namespace")
89 .map(String::as_str)
90 .unwrap_or("default");
91 let output = ctx.flag("output").map(String::as_str).unwrap_or("table");
92
93 println!("Getting pods from namespace: {namespace}");
94 println!("Output format: {output}");
95
96 if let Some(pod) = ctx.args().first() {
97 println!("Getting specific pod: {pod}");
98 } else {
99 println!("Listing all pods");
100 }
101 Ok(())
102 })
103 .build(),
104 )
105 .subcommand(
106 CommandBuilder::new("services")
107 .short("Get services")
108 .arg_completion(|_ctx, _prefix| {
109 let mut result = CompletionResult::new()
110 .add_with_description("nginx-service", "Type: LoadBalancer")
111 .add_with_description("redis-service", "Type: ClusterIP")
112 .add_with_description("postgres-service", "Type: NodePort");
113
114 // Add conditional help based on flags
115 result = result.add_conditional_help(
116 "Tip: Use -o wide to see more details about services",
117 |ctx| {
118 ctx.flag("output")
119 .map(|o| o != "wide")
120 .unwrap_or(true)
121 },
122 );
123
124 Ok(result)
125 })
126 .build(),
127 )
128 .build(),
129 )
130 .subcommand(
131 CommandBuilder::new("create")
132 .short("Create resources")
133 .flag(
134 Flag::new("file")
135 .short('f')
136 .usage("Filename to use to create the resource")
137 .value_type(FlagType::String)
138 .required(),
139 )
140 .flag_completion("file", |_ctx, prefix| {
141 let mut result = CompletionResult::new();
142
143 // Simulate file completions
144 let files = vec![
145 ("deployment.yaml", "Deployment configuration"),
146 ("service.yaml", "Service configuration"),
147 ("configmap.yaml", "ConfigMap configuration"),
148 ("secret.yaml", "Secret configuration"),
149 ("pod.yaml", "Pod configuration"),
150 ];
151
152 for (file, desc) in files {
153 if file.starts_with(prefix) {
154 result = result.add_with_description(file, desc);
155 }
156 }
157
158 // Add contextual help
159 result = result.add_help_text(
160 "Files should be valid Kubernetes manifests in YAML or JSON format",
161 );
162
163 if std::path::Path::new(prefix)
164 .extension()
165 .is_some_and(|ext| ext.eq_ignore_ascii_case("json"))
166 {
167 result = result.add_help_text(
168 "Note: JSON format is supported but YAML is more common in Kubernetes",
169 );
170 }
171
172 Ok(result)
173 })
174 .run(|ctx| {
175 let file = ctx.flag("file").expect("File is required");
176 println!("Creating resources from file: {file}");
177 Ok(())
178 })
179 .build(),
180 )
181 .subcommand(
182 CommandBuilder::new("debug")
183 .short("Debug and troubleshoot resources")
184 .arg_completion(|_ctx, _prefix| {
185 Ok(CompletionResult::new()
186 .add_with_description("pod/nginx-abc123", "Debug a specific pod")
187 .add_with_description("node/worker-1", "Debug a node")
188 .add_with_description("deployment/nginx", "Debug a deployment")
189 .add_help_text("Debug creates an interactive debugging session")
190 .add_help_text("Common debugging commands: kubectl logs, kubectl exec, kubectl describe")
191 .add_conditional_help(
192 "Tip: Use 'kubectl logs -f' to follow log output in real-time",
193 |ctx| ctx.args().is_empty(),
194 ))
195 })
196 .build(),
197 )
198 .build();
199
200 let args: Vec<String> = std::env::args().skip(1).collect();
201 if let Err(e) = app.execute(args) {
202 eprintln!("Error: {e}");
203 std::process::exit(1);
204 }
205}Sourcepub fn suggestions(self, enabled: bool) -> Self
pub fn suggestions(self, enabled: bool) -> Self
Enables or disables command suggestions
When enabled, the framework will suggest similar commands when a user types an unknown command.
§Examples
use flag_rs::CommandBuilder;
let cmd = CommandBuilder::new("myapp")
.suggestions(true) // Enable suggestions (default)
.build();Sourcepub fn suggestion_distance(self, distance: usize) -> Self
pub fn suggestion_distance(self, distance: usize) -> Self
Sets the maximum Levenshtein distance for suggestions
Commands within this distance will be suggested as alternatives. Default is 2.
§Examples
use flag_rs::CommandBuilder;
let cmd = CommandBuilder::new("myapp")
.suggestion_distance(3) // Allow more distant suggestions
.build();Sourcepub fn build(self) -> Command
pub fn build(self) -> Command
Builds and returns the completed Command
Examples found in repository?
50fn create_simple_cli() -> flag_rs::Command {
51 CommandBuilder::new("bench")
52 .short("Benchmark CLI")
53 .flag(
54 Flag::new("verbose")
55 .short('v')
56 .value_type(FlagType::Bool)
57 .default(FlagValue::Bool(false)),
58 )
59 .flag(Flag::new("output").short('o').value_type(FlagType::String))
60 .build()
61}
62
63/// Creates a complex nested command structure
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}
98
99fn bench_command_creation() {
100 println!("\n=== Command Creation Benchmarks ===");
101
102 let bench = Benchmark::new("Simple command creation", 10_000);
103 let duration = bench.run(|| {
104 let _ = create_simple_cli();
105 });
106 bench.report(duration);
107
108 let bench = Benchmark::new("Complex command creation (50 subs)", 100);
109 let duration = bench.run(|| {
110 let _ = create_complex_cli();
111 });
112 bench.report(duration);
113
114 let bench = Benchmark::new("CommandBuilder with 10 flags", 1_000);
115 let duration = bench.run(|| {
116 let mut cmd = CommandBuilder::new("test");
117 for i in 0..10 {
118 cmd = cmd.flag(Flag::new(format!("flag{i}")).value_type(FlagType::String));
119 }
120 let _ = cmd.build();
121 });
122 bench.report(duration);
123}
124
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}More examples
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}
78
79fn build_get_pods() -> Command {
80 CommandBuilder::new("pods")
81 .aliases(vec!["po", "pod"])
82 .short("List pods")
83 .arg_completion(|ctx, prefix| {
84 // This is the key feature - dynamic completion based on runtime state!
85 let namespace = ctx
86 .flag("namespace")
87 .map(String::as_str)
88 .unwrap_or("default");
89
90 // In real kubectl, this would query the K8s API
91 let pods = get_pods_with_status(namespace);
92 let mut result = CompletionResult::new();
93
94 for (pod_name, status) in pods {
95 if pod_name.starts_with(prefix) {
96 result = result.add_with_description(pod_name, status);
97 }
98 }
99
100 Ok(result)
101 })
102 .run(|ctx| {
103 let namespace = ctx
104 .flag("namespace")
105 .map(String::as_str)
106 .unwrap_or("default");
107
108 println!("Listing pods in namespace: {namespace}");
109
110 if let Some(pod_name) = ctx.args().first() {
111 println!("Getting specific pod: {pod_name}");
112 } else {
113 for pod in get_pods_in_namespace(namespace) {
114 println!("pod/{}", pod);
115 }
116 }
117
118 Ok(())
119 })
120 .build()
121}
122
123fn build_get_services() -> Command {
124 CommandBuilder::new("services")
125 .aliases(vec!["svc", "service"])
126 .short("List services")
127 .arg_completion(|ctx, prefix| {
128 let namespace = ctx
129 .flag("namespace")
130 .map(String::as_str)
131 .unwrap_or("default");
132
133 let services = get_services_in_namespace(namespace);
134 Ok(CompletionResult::new()
135 .extend(services.into_iter().filter(|svc| svc.starts_with(prefix))))
136 })
137 .run(|ctx| {
138 let namespace = ctx
139 .flag("namespace")
140 .map(String::as_str)
141 .unwrap_or("default");
142
143 println!("Listing services in namespace: {namespace}");
144 Ok(())
145 })
146 .build()
147}
148
149fn build_get_deployments() -> Command {
150 CommandBuilder::new("deployments")
151 .aliases(vec!["deploy", "deployment"])
152 .short("List deployments")
153 .run(|ctx| {
154 let namespace = ctx
155 .flag("namespace")
156 .map(String::as_str)
157 .unwrap_or("default");
158
159 println!("Listing deployments in namespace: {namespace}");
160 Ok(())
161 })
162 .build()
163}
164
165fn build_describe_command() -> Command {
166 CommandBuilder::new("describe")
167 .short("Show details of a specific resource")
168 .run(|_ctx| {
169 println!("Describe command - add resource type subcommands");
170 Ok(())
171 })
172 .build()
173}
174
175fn build_delete_command() -> Command {
176 CommandBuilder::new("delete")
177 .short("Delete resources")
178 .run(|_ctx| {
179 println!("Delete command - add resource type subcommands");
180 Ok(())
181 })
182 .build()
183}
184
185// Mock functions that would normally query the Kubernetes API
186fn get_namespaces_with_descriptions() -> Vec<(String, String)> {
187 vec![
188 (
189 "default".to_string(),
190 "Default namespace for user workloads".to_string(),
191 ),
192 (
193 "kube-system".to_string(),
194 "Kubernetes system components".to_string(),
195 ),
196 (
197 "kube-public".to_string(),
198 "Public resources accessible to all users".to_string(),
199 ),
200 (
201 "development".to_string(),
202 "Development environment".to_string(),
203 ),
204 (
205 "staging".to_string(),
206 "Staging environment for pre-production testing".to_string(),
207 ),
208 (
209 "production".to_string(),
210 "Production environment (CAUTION)".to_string(),
211 ),
212 ]
213}
214
215fn get_pods_with_status(namespace: &str) -> Vec<(String, String)> {
216 use std::time::{SystemTime, UNIX_EPOCH};
217
218 // Generate random suffix based on current time
219 let timestamp = SystemTime::now()
220 .duration_since(UNIX_EPOCH)
221 .unwrap()
222 .as_secs();
223
224 // Simple pseudo-random generation
225 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
226 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
227 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
228
229 match namespace {
230 "default" => vec![
231 (
232 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
233 "Running (2/2 containers)".to_string(),
234 ),
235 (
236 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
237 "Running (2/2 containers)".to_string(),
238 ),
239 (
240 "redis-master-0".to_string(),
241 "Running (1/1 containers)".to_string(),
242 ),
243 (
244 "redis-slave-0".to_string(),
245 "Running (1/1 containers)".to_string(),
246 ),
247 ("redis-slave-1".to_string(), "Pending".to_string()),
248 ],
249 "kube-system" => vec![
250 (
251 format!("coredns-5d78c9869d-{:05x}", rand1),
252 "Running (1/1 containers)".to_string(),
253 ),
254 (
255 format!("coredns-5d78c9869d-{:05x}", rand2),
256 "Running (1/1 containers)".to_string(),
257 ),
258 ("etcd-minikube".to_string(), "Running".to_string()),
259 ("kube-apiserver-minikube".to_string(), "Running".to_string()),
260 (
261 "kube-controller-manager-minikube".to_string(),
262 "Running".to_string(),
263 ),
264 (format!("kube-proxy-{:05x}", rand3), "Running".to_string()),
265 ("kube-scheduler-minikube".to_string(), "Running".to_string()),
266 ],
267 _ => vec![],
268 }
269}
270
271fn get_pods_in_namespace(namespace: &str) -> Vec<String> {
272 use std::time::{SystemTime, UNIX_EPOCH};
273
274 // Generate random suffix based on current time
275 let timestamp = SystemTime::now()
276 .duration_since(UNIX_EPOCH)
277 .unwrap()
278 .as_secs();
279
280 // Simple pseudo-random generation
281 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
282 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
283 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
284
285 match namespace {
286 "default" => vec![
287 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
288 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
289 format!("redis-master-0"),
290 format!("redis-slave-0"),
291 format!("redis-slave-1"),
292 ],
293 "kube-system" => vec![
294 format!("coredns-5d78c9869d-{:05x}", rand1),
295 format!("coredns-5d78c9869d-{:05x}", rand2),
296 format!("etcd-minikube"),
297 format!("kube-apiserver-minikube"),
298 format!("kube-controller-manager-minikube"),
299 format!("kube-proxy-{:05x}", rand3),
300 format!("kube-scheduler-minikube"),
301 ],
302 _ => vec![],
303 }
304}
305
306fn get_services_in_namespace(namespace: &str) -> Vec<String> {
307 match namespace {
308 "default" => vec![
309 "kubernetes".to_string(),
310 "nginx-service".to_string(),
311 "redis-master".to_string(),
312 "redis-slave".to_string(),
313 ],
314 "kube-system" => vec!["kube-dns".to_string()],
315 _ => vec![],
316 }
317}
318
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}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}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}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/memory_optimization_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