Context

Struct Context 

Source
pub struct Context { /* private fields */ }
Expand description

Context passed to command handlers

Context provides access to:

  • Command arguments
  • Parsed flag values
  • Arbitrary typed values for sharing state between commands

§Examples

use flag_rs::context::Context;
use std::collections::HashMap;

// Create a context with arguments
let mut ctx = Context::new(vec!["file1.txt".to_string(), "file2.txt".to_string()]);

// Access arguments
assert_eq!(ctx.args(), &["file1.txt", "file2.txt"]);

// Set and retrieve flags
ctx.set_flag("verbose".to_string(), "true".to_string());
assert_eq!(ctx.flag("verbose"), Some(&"true".to_string()));

// Store typed values
#[derive(Debug, PartialEq)]
struct Config {
    api_key: String,
}

ctx.set(Config { api_key: "secret".to_string() });
let config = ctx.get::<Config>().unwrap();
assert_eq!(config.api_key, "secret");

Implementations§

Source§

impl Context

Source

pub fn new(args: Vec<String>) -> Self

Creates a new context with the given arguments

§Arguments
  • args - The command-line arguments (without the command path)
Examples found in repository?
examples/benchmark.rs (line 213)
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}
Source

pub fn args(&self) -> &[String]

Returns a slice of the command arguments

§Examples
use flag_rs::context::Context;

let ctx = Context::new(vec!["file.txt".to_string()]);
assert_eq!(ctx.args(), &["file.txt"]);
Examples found in repository?
examples/kubectl.rs (line 110)
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
Hide additional examples
examples/validator_demo.rs (line 12)
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/error_handling_demo.rs (line 21)
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}
examples/active_help_demo.rs (line 96)
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}
Source

pub fn args_mut(&mut self) -> &mut Vec<String>

Returns a mutable reference to the command arguments

§Examples
use flag_rs::context::Context;

let mut ctx = Context::new(vec!["file.txt".to_string()]);
ctx.args_mut().push("another.txt".to_string());
assert_eq!(ctx.args().len(), 2);
Source

pub fn flag(&self, name: &str) -> Option<&String>

Gets the value of a flag by name

§Arguments
  • name - The name of the flag
§Returns

Returns Some(&String) if the flag exists, None otherwise

Examples found in repository?
examples/kubectl.rs (line 86)
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
Hide additional examples
examples/error_handling_demo.rs (line 44)
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}
examples/timeout_completion_demo.rs (line 85)
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}
examples/flag_completion_demo.rs (line 130)
4fn main() {
5    let app = CommandBuilder::new("deploy")
6        .short("Deploy application to various environments")
7        .flag(
8            Flag::choice("environment", &["dev", "staging", "prod"])
9                .short('e')
10                .usage("Target environment")
11                .required()
12                .completion(|_ctx, prefix| {
13                    // Dynamic completion - could check available environments
14                    let environments = vec![
15                        ("dev", "Development environment"),
16                        ("staging", "Staging environment (pre-production)"),
17                        ("prod", "Production environment"),
18                    ];
19
20                    let mut result = CompletionResult::new();
21                    for (env, desc) in environments {
22                        if env.starts_with(prefix) {
23                            result = result.add_with_description(env.to_string(), desc.to_string());
24                        }
25                    }
26
27                    // Add active help if no prefix
28                    if prefix.is_empty() {
29                        result = result.add_help_text("Available environments: dev, staging, prod");
30                    }
31
32                    Ok(result)
33                }),
34        )
35        .flag(
36            Flag::file("config")
37                .short('c')
38                .usage("Configuration file")
39                .default_str("config.yaml")
40                .completion(|_ctx, prefix| {
41                    // In a real app, you might list files in the config directory
42                    let configs = vec![
43                        "config.yaml",
44                        "config.dev.yaml",
45                        "config.staging.yaml",
46                        "config.prod.yaml",
47                        "secrets.yaml",
48                    ];
49
50                    Ok(CompletionResult::new().extend(
51                        configs
52                            .into_iter()
53                            .filter(|c| c.starts_with(prefix))
54                            .map(String::from),
55                    ))
56                }),
57        )
58        .flag(
59            Flag::directory("output")
60                .short('o')
61                .usage("Output directory for deployment artifacts")
62                .completion(|_ctx, prefix| {
63                    // In a real app, you might list actual directories
64                    let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66                    Ok(CompletionResult::new().extend(
67                        dirs.into_iter()
68                            .filter(|d| d.starts_with(prefix))
69                            .map(String::from),
70                    ))
71                }),
72        )
73        .flag(
74            Flag::string_slice("service")
75                .short('s')
76                .usage("Services to deploy (can be specified multiple times)")
77                .completion(|ctx, prefix| {
78                    // Context-aware completion based on environment
79                    let env = ctx.flag_str_or("environment", "dev");
80
81                    let services = match env {
82                        "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83                        "staging" => vec!["api", "web", "worker", "scheduler"],
84                        "prod" => vec!["api", "web", "worker"],
85                        _ => vec!["api", "web"],
86                    };
87
88                    let mut result = CompletionResult::new().extend(
89                        services
90                            .into_iter()
91                            .filter(|s| s.starts_with(prefix))
92                            .map(String::from),
93                    );
94
95                    // Add context-aware help
96                    if prefix.is_empty() {
97                        result = result
98                            .add_help_text(format!("Available services for {} environment", env));
99                    }
100
101                    Ok(result)
102                }),
103        )
104        .flag(
105            Flag::range("replicas", 1, 10)
106                .short('r')
107                .usage("Number of replicas to deploy")
108                .default_int(2)
109                .completion(|_ctx, _prefix| {
110                    // Suggest common replica counts
111                    Ok(CompletionResult::new()
112                        .add_with_description(
113                            "1".to_string(),
114                            "Single instance (dev/test)".to_string(),
115                        )
116                        .add_with_description("2".to_string(), "Basic redundancy".to_string())
117                        .add_with_description("3".to_string(), "Standard production".to_string())
118                        .add_with_description("5".to_string(), "High availability".to_string()))
119                }),
120        )
121        .run(|ctx| {
122            let env = ctx.flag_str_or("environment", "dev");
123            let config = ctx.flag_str_or("config", "config.yaml");
124            let replicas = ctx.flag_int_or("replicas", 2);
125
126            println!("Deploying to {} environment", env);
127            println!("Using configuration: {}", config);
128            println!("Replica count: {}", replicas);
129
130            if let Some(output) = ctx.flag("output") {
131                println!("Output directory: {}", output);
132            }
133
134            if let Some(services) = ctx.flag("service") {
135                println!("Deploying services: {}", services);
136            } else {
137                println!("Deploying all services");
138            }
139
140            Ok(())
141        })
142        .build();
143
144    let args: Vec<String> = std::env::args().skip(1).collect();
145    if let Err(e) = app.execute(args) {
146        eprintln!("Error: {}", e);
147        std::process::exit(1);
148    }
149}
examples/cached_completion_demo.rs (line 86)
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}
examples/lifecycle_hooks_demo.rs (line 30)
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}
Source

pub fn set_flag(&mut self, name: String, value: String)

Sets a flag value

§Arguments
  • name - The name of the flag
  • value - The value to set
Source

pub fn flags(&self) -> &HashMap<String, String>

Returns a reference to all flags

Examples found in repository?
examples/enhanced_errors_demo.rs (line 77)
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}
More examples
Hide additional examples
examples/cached_completion_demo.rs (line 66)
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}
Source

pub fn flag_bool(&self, name: &str) -> Option<bool>

Gets a flag value as a boolean

§Arguments
  • name - The name of the flag
§Returns

Returns Some(bool) if the flag exists and can be parsed as a boolean, None otherwise

§Examples
use flag_rs::context::Context;

let mut ctx = Context::new(vec![]);
ctx.set_flag("verbose".to_string(), "true".to_string());
ctx.set_flag("debug".to_string(), "false".to_string());

assert_eq!(ctx.flag_bool("verbose"), Some(true));
assert_eq!(ctx.flag_bool("debug"), Some(false));
assert_eq!(ctx.flag_bool("missing"), None);
Source

pub fn flag_int(&self, name: &str) -> Option<i64>

Gets a flag value as an integer

§Arguments
  • name - The name of the flag
§Returns

Returns Some(i64) if the flag exists and can be parsed as an integer, None otherwise

§Examples
use flag_rs::context::Context;

let mut ctx = Context::new(vec![]);
ctx.set_flag("port".to_string(), "8080".to_string());

assert_eq!(ctx.flag_int("port"), Some(8080));
assert_eq!(ctx.flag_int("missing"), None);
Source

pub fn flag_float(&self, name: &str) -> Option<f64>

Gets a flag value as a float

§Arguments
  • name - The name of the flag
§Returns

Returns Some(f64) if the flag exists and can be parsed as a float, None otherwise

§Examples
use flag_rs::context::Context;

let mut ctx = Context::new(vec![]);
ctx.set_flag("ratio".to_string(), "0.75".to_string());

assert_eq!(ctx.flag_float("ratio"), Some(0.75));
assert_eq!(ctx.flag_float("missing"), None);
Source

pub fn flag_str_or<'a>(&'a self, name: &str, default: &'a str) -> &'a str

Gets a flag value as a string, returning a default if not present

§Arguments
  • name - The name of the flag
  • default - The default value to return if the flag is not set
§Returns

Returns the flag value if present, or the default value

§Examples
use flag_rs::context::Context;

let mut ctx = Context::new(vec![]);
ctx.set_flag("env".to_string(), "production".to_string());

assert_eq!(ctx.flag_str_or("env", "development"), "production");
assert_eq!(ctx.flag_str_or("missing", "development"), "development");
Examples found in repository?
examples/flag_completion_demo.rs (line 79)
4fn main() {
5    let app = CommandBuilder::new("deploy")
6        .short("Deploy application to various environments")
7        .flag(
8            Flag::choice("environment", &["dev", "staging", "prod"])
9                .short('e')
10                .usage("Target environment")
11                .required()
12                .completion(|_ctx, prefix| {
13                    // Dynamic completion - could check available environments
14                    let environments = vec![
15                        ("dev", "Development environment"),
16                        ("staging", "Staging environment (pre-production)"),
17                        ("prod", "Production environment"),
18                    ];
19
20                    let mut result = CompletionResult::new();
21                    for (env, desc) in environments {
22                        if env.starts_with(prefix) {
23                            result = result.add_with_description(env.to_string(), desc.to_string());
24                        }
25                    }
26
27                    // Add active help if no prefix
28                    if prefix.is_empty() {
29                        result = result.add_help_text("Available environments: dev, staging, prod");
30                    }
31
32                    Ok(result)
33                }),
34        )
35        .flag(
36            Flag::file("config")
37                .short('c')
38                .usage("Configuration file")
39                .default_str("config.yaml")
40                .completion(|_ctx, prefix| {
41                    // In a real app, you might list files in the config directory
42                    let configs = vec![
43                        "config.yaml",
44                        "config.dev.yaml",
45                        "config.staging.yaml",
46                        "config.prod.yaml",
47                        "secrets.yaml",
48                    ];
49
50                    Ok(CompletionResult::new().extend(
51                        configs
52                            .into_iter()
53                            .filter(|c| c.starts_with(prefix))
54                            .map(String::from),
55                    ))
56                }),
57        )
58        .flag(
59            Flag::directory("output")
60                .short('o')
61                .usage("Output directory for deployment artifacts")
62                .completion(|_ctx, prefix| {
63                    // In a real app, you might list actual directories
64                    let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66                    Ok(CompletionResult::new().extend(
67                        dirs.into_iter()
68                            .filter(|d| d.starts_with(prefix))
69                            .map(String::from),
70                    ))
71                }),
72        )
73        .flag(
74            Flag::string_slice("service")
75                .short('s')
76                .usage("Services to deploy (can be specified multiple times)")
77                .completion(|ctx, prefix| {
78                    // Context-aware completion based on environment
79                    let env = ctx.flag_str_or("environment", "dev");
80
81                    let services = match env {
82                        "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83                        "staging" => vec!["api", "web", "worker", "scheduler"],
84                        "prod" => vec!["api", "web", "worker"],
85                        _ => vec!["api", "web"],
86                    };
87
88                    let mut result = CompletionResult::new().extend(
89                        services
90                            .into_iter()
91                            .filter(|s| s.starts_with(prefix))
92                            .map(String::from),
93                    );
94
95                    // Add context-aware help
96                    if prefix.is_empty() {
97                        result = result
98                            .add_help_text(format!("Available services for {} environment", env));
99                    }
100
101                    Ok(result)
102                }),
103        )
104        .flag(
105            Flag::range("replicas", 1, 10)
106                .short('r')
107                .usage("Number of replicas to deploy")
108                .default_int(2)
109                .completion(|_ctx, _prefix| {
110                    // Suggest common replica counts
111                    Ok(CompletionResult::new()
112                        .add_with_description(
113                            "1".to_string(),
114                            "Single instance (dev/test)".to_string(),
115                        )
116                        .add_with_description("2".to_string(), "Basic redundancy".to_string())
117                        .add_with_description("3".to_string(), "Standard production".to_string())
118                        .add_with_description("5".to_string(), "High availability".to_string()))
119                }),
120        )
121        .run(|ctx| {
122            let env = ctx.flag_str_or("environment", "dev");
123            let config = ctx.flag_str_or("config", "config.yaml");
124            let replicas = ctx.flag_int_or("replicas", 2);
125
126            println!("Deploying to {} environment", env);
127            println!("Using configuration: {}", config);
128            println!("Replica count: {}", replicas);
129
130            if let Some(output) = ctx.flag("output") {
131                println!("Output directory: {}", output);
132            }
133
134            if let Some(services) = ctx.flag("service") {
135                println!("Deploying services: {}", services);
136            } else {
137                println!("Deploying all services");
138            }
139
140            Ok(())
141        })
142        .build();
143
144    let args: Vec<String> = std::env::args().skip(1).collect();
145    if let Err(e) = app.execute(args) {
146        eprintln!("Error: {}", e);
147        std::process::exit(1);
148    }
149}
More examples
Hide additional examples
examples/builder_api_demo.rs (line 76)
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}
Source

pub fn flag_bool_or(&self, name: &str, default: bool) -> bool

Gets a flag value as a boolean, returning a default if not present

§Arguments
  • name - The name of the flag
  • default - The default value to return if the flag is not set or cannot be parsed
§Examples
use flag_rs::context::Context;

let mut ctx = Context::new(vec![]);
ctx.set_flag("verbose".to_string(), "true".to_string());

assert_eq!(ctx.flag_bool_or("verbose", false), true);
assert_eq!(ctx.flag_bool_or("missing", false), false);
Examples found in repository?
examples/builder_api_demo.rs (line 79)
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}
Source

pub fn flag_int_or(&self, name: &str, default: i64) -> i64

Gets a flag value as an integer, returning a default if not present

§Arguments
  • name - The name of the flag
  • default - The default value to return if the flag is not set or cannot be parsed
§Examples
use flag_rs::context::Context;

let mut ctx = Context::new(vec![]);
ctx.set_flag("port".to_string(), "8080".to_string());

assert_eq!(ctx.flag_int_or("port", 3000), 8080);
assert_eq!(ctx.flag_int_or("missing", 3000), 3000);
Examples found in repository?
examples/flag_completion_demo.rs (line 124)
4fn main() {
5    let app = CommandBuilder::new("deploy")
6        .short("Deploy application to various environments")
7        .flag(
8            Flag::choice("environment", &["dev", "staging", "prod"])
9                .short('e')
10                .usage("Target environment")
11                .required()
12                .completion(|_ctx, prefix| {
13                    // Dynamic completion - could check available environments
14                    let environments = vec![
15                        ("dev", "Development environment"),
16                        ("staging", "Staging environment (pre-production)"),
17                        ("prod", "Production environment"),
18                    ];
19
20                    let mut result = CompletionResult::new();
21                    for (env, desc) in environments {
22                        if env.starts_with(prefix) {
23                            result = result.add_with_description(env.to_string(), desc.to_string());
24                        }
25                    }
26
27                    // Add active help if no prefix
28                    if prefix.is_empty() {
29                        result = result.add_help_text("Available environments: dev, staging, prod");
30                    }
31
32                    Ok(result)
33                }),
34        )
35        .flag(
36            Flag::file("config")
37                .short('c')
38                .usage("Configuration file")
39                .default_str("config.yaml")
40                .completion(|_ctx, prefix| {
41                    // In a real app, you might list files in the config directory
42                    let configs = vec![
43                        "config.yaml",
44                        "config.dev.yaml",
45                        "config.staging.yaml",
46                        "config.prod.yaml",
47                        "secrets.yaml",
48                    ];
49
50                    Ok(CompletionResult::new().extend(
51                        configs
52                            .into_iter()
53                            .filter(|c| c.starts_with(prefix))
54                            .map(String::from),
55                    ))
56                }),
57        )
58        .flag(
59            Flag::directory("output")
60                .short('o')
61                .usage("Output directory for deployment artifacts")
62                .completion(|_ctx, prefix| {
63                    // In a real app, you might list actual directories
64                    let dirs = vec!["./build", "./dist", "./output", "/tmp/deploy"];
65
66                    Ok(CompletionResult::new().extend(
67                        dirs.into_iter()
68                            .filter(|d| d.starts_with(prefix))
69                            .map(String::from),
70                    ))
71                }),
72        )
73        .flag(
74            Flag::string_slice("service")
75                .short('s')
76                .usage("Services to deploy (can be specified multiple times)")
77                .completion(|ctx, prefix| {
78                    // Context-aware completion based on environment
79                    let env = ctx.flag_str_or("environment", "dev");
80
81                    let services = match env {
82                        "dev" => vec!["api", "web", "worker", "scheduler", "debug-panel"],
83                        "staging" => vec!["api", "web", "worker", "scheduler"],
84                        "prod" => vec!["api", "web", "worker"],
85                        _ => vec!["api", "web"],
86                    };
87
88                    let mut result = CompletionResult::new().extend(
89                        services
90                            .into_iter()
91                            .filter(|s| s.starts_with(prefix))
92                            .map(String::from),
93                    );
94
95                    // Add context-aware help
96                    if prefix.is_empty() {
97                        result = result
98                            .add_help_text(format!("Available services for {} environment", env));
99                    }
100
101                    Ok(result)
102                }),
103        )
104        .flag(
105            Flag::range("replicas", 1, 10)
106                .short('r')
107                .usage("Number of replicas to deploy")
108                .default_int(2)
109                .completion(|_ctx, _prefix| {
110                    // Suggest common replica counts
111                    Ok(CompletionResult::new()
112                        .add_with_description(
113                            "1".to_string(),
114                            "Single instance (dev/test)".to_string(),
115                        )
116                        .add_with_description("2".to_string(), "Basic redundancy".to_string())
117                        .add_with_description("3".to_string(), "Standard production".to_string())
118                        .add_with_description("5".to_string(), "High availability".to_string()))
119                }),
120        )
121        .run(|ctx| {
122            let env = ctx.flag_str_or("environment", "dev");
123            let config = ctx.flag_str_or("config", "config.yaml");
124            let replicas = ctx.flag_int_or("replicas", 2);
125
126            println!("Deploying to {} environment", env);
127            println!("Using configuration: {}", config);
128            println!("Replica count: {}", replicas);
129
130            if let Some(output) = ctx.flag("output") {
131                println!("Output directory: {}", output);
132            }
133
134            if let Some(services) = ctx.flag("service") {
135                println!("Deploying services: {}", services);
136            } else {
137                println!("Deploying all services");
138            }
139
140            Ok(())
141        })
142        .build();
143
144    let args: Vec<String> = std::env::args().skip(1).collect();
145    if let Err(e) = app.execute(args) {
146        eprintln!("Error: {}", e);
147        std::process::exit(1);
148    }
149}
More examples
Hide additional examples
examples/builder_api_demo.rs (line 115)
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}
Source

pub fn flag_float_or(&self, name: &str, default: f64) -> f64

Gets a flag value as a float, returning a default if not present

§Arguments
  • name - The name of the flag
  • default - The default value to return if the flag is not set or cannot be parsed
§Examples
use flag_rs::context::Context;

let mut ctx = Context::new(vec![]);
ctx.set_flag("ratio".to_string(), "0.75".to_string());

assert_eq!(ctx.flag_float_or("ratio", 0.5), 0.75);
assert_eq!(ctx.flag_float_or("missing", 0.5), 0.5);
Source

pub fn set<T: Any + Send + Sync>(&mut self, value: T)

Stores a typed value in the context

Values are stored by their type, so only one value of each type can be stored at a time. Storing a new value of the same type will overwrite the previous value.

§Type Parameters
  • T - The type of value to store (must be Send + Sync)
§Examples
use flag_rs::context::Context;

struct ApiClient {
    endpoint: String,
}

let mut ctx = Context::new(vec![]);
ctx.set(ApiClient { endpoint: "https://api.example.com".to_string() });
Source

pub fn get<T: Any + Send + Sync>(&self) -> Option<&T>

Retrieves a typed value from the context

§Type Parameters
  • T - The type of value to retrieve
§Returns

Returns Some(&T) if a value of that type exists, None otherwise

Source

pub fn get_mut<T: Any + Send + Sync>(&mut self) -> Option<&mut T>

Retrieves a mutable reference to a typed value from the context

§Type Parameters
  • T - The type of value to retrieve
§Returns

Returns Some(&mut T) if a value of that type exists, None otherwise

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.