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
impl Context
Sourcepub fn new(args: Vec<String>) -> Self
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?
200fn bench_completion() {
201 println!("\n=== Completion Benchmarks ===");
202
203 let cli = CommandBuilder::new("comp")
204 .arg_completion(|_ctx, prefix| {
205 let items: Vec<String> = (0..100)
206 .map(|i| format!("item{i:03}"))
207 .filter(|item| item.starts_with(prefix))
208 .collect();
209 Ok(CompletionResult::new().extend(items))
210 })
211 .build();
212
213 let ctx = flag_rs::Context::new(vec!["comp".to_string()]);
214
215 let bench = Benchmark::new("Complete with empty prefix (100 items)", 1_000);
216 let duration = bench.run(|| {
217 let _ = cli.get_completions(&ctx, "", None);
218 });
219 bench.report(duration);
220
221 let bench = Benchmark::new("Complete with prefix (filtered)", 1_000);
222 let duration = bench.run(|| {
223 let _ = cli.get_completions(&ctx, "item05", None);
224 });
225 bench.report(duration);
226
227 // Test completion with descriptions
228 let cli_desc = CommandBuilder::new("comp_desc")
229 .arg_completion(|_ctx, prefix| {
230 let mut result = CompletionResult::new();
231 for i in 0..50 {
232 let item = format!("option{i:02}");
233 if item.starts_with(prefix) {
234 result =
235 result.add_with_description(item, format!("Description for option {i}"));
236 }
237 }
238 Ok(result)
239 })
240 .build();
241
242 let bench = Benchmark::new("Complete with descriptions (50 items)", 1_000);
243 let duration = bench.run(|| {
244 let _ = cli_desc.get_completions(&ctx, "", None);
245 });
246 bench.report(duration);
247}Sourcepub fn args(&self) -> &[String]
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?
79fn build_get_pods() -> Command {
80 CommandBuilder::new("pods")
81 .aliases(vec!["po", "pod"])
82 .short("List pods")
83 .arg_completion(|ctx, prefix| {
84 // This is the key feature - dynamic completion based on runtime state!
85 let namespace = ctx
86 .flag("namespace")
87 .map(String::as_str)
88 .unwrap_or("default");
89
90 // In real kubectl, this would query the K8s API
91 let pods = get_pods_with_status(namespace);
92 let mut result = CompletionResult::new();
93
94 for (pod_name, status) in pods {
95 if pod_name.starts_with(prefix) {
96 result = result.add_with_description(pod_name, status);
97 }
98 }
99
100 Ok(result)
101 })
102 .run(|ctx| {
103 let namespace = ctx
104 .flag("namespace")
105 .map(String::as_str)
106 .unwrap_or("default");
107
108 println!("Listing pods in namespace: {namespace}");
109
110 if let Some(pod_name) = ctx.args().first() {
111 println!("Getting specific pod: {pod_name}");
112 } else {
113 for pod in get_pods_in_namespace(namespace) {
114 println!("pod/{}", pod);
115 }
116 }
117
118 Ok(())
119 })
120 .build()
121}
122
123fn build_get_services() -> Command {
124 CommandBuilder::new("services")
125 .aliases(vec!["svc", "service"])
126 .short("List services")
127 .arg_completion(|ctx, prefix| {
128 let namespace = ctx
129 .flag("namespace")
130 .map(String::as_str)
131 .unwrap_or("default");
132
133 let services = get_services_in_namespace(namespace);
134 Ok(CompletionResult::new()
135 .extend(services.into_iter().filter(|svc| svc.starts_with(prefix))))
136 })
137 .run(|ctx| {
138 let namespace = ctx
139 .flag("namespace")
140 .map(String::as_str)
141 .unwrap_or("default");
142
143 println!("Listing services in namespace: {namespace}");
144 Ok(())
145 })
146 .build()
147}
148
149fn build_get_deployments() -> Command {
150 CommandBuilder::new("deployments")
151 .aliases(vec!["deploy", "deployment"])
152 .short("List deployments")
153 .run(|ctx| {
154 let namespace = ctx
155 .flag("namespace")
156 .map(String::as_str)
157 .unwrap_or("default");
158
159 println!("Listing deployments in namespace: {namespace}");
160 Ok(())
161 })
162 .build()
163}
164
165fn build_describe_command() -> Command {
166 CommandBuilder::new("describe")
167 .short("Show details of a specific resource")
168 .run(|_ctx| {
169 println!("Describe command - add resource type subcommands");
170 Ok(())
171 })
172 .build()
173}
174
175fn build_delete_command() -> Command {
176 CommandBuilder::new("delete")
177 .short("Delete resources")
178 .run(|_ctx| {
179 println!("Delete command - add resource type subcommands");
180 Ok(())
181 })
182 .build()
183}
184
185// Mock functions that would normally query the Kubernetes API
186fn get_namespaces_with_descriptions() -> Vec<(String, String)> {
187 vec![
188 (
189 "default".to_string(),
190 "Default namespace for user workloads".to_string(),
191 ),
192 (
193 "kube-system".to_string(),
194 "Kubernetes system components".to_string(),
195 ),
196 (
197 "kube-public".to_string(),
198 "Public resources accessible to all users".to_string(),
199 ),
200 (
201 "development".to_string(),
202 "Development environment".to_string(),
203 ),
204 (
205 "staging".to_string(),
206 "Staging environment for pre-production testing".to_string(),
207 ),
208 (
209 "production".to_string(),
210 "Production environment (CAUTION)".to_string(),
211 ),
212 ]
213}
214
215fn get_pods_with_status(namespace: &str) -> Vec<(String, String)> {
216 use std::time::{SystemTime, UNIX_EPOCH};
217
218 // Generate random suffix based on current time
219 let timestamp = SystemTime::now()
220 .duration_since(UNIX_EPOCH)
221 .unwrap()
222 .as_secs();
223
224 // Simple pseudo-random generation
225 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
226 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
227 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
228
229 match namespace {
230 "default" => vec![
231 (
232 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
233 "Running (2/2 containers)".to_string(),
234 ),
235 (
236 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
237 "Running (2/2 containers)".to_string(),
238 ),
239 (
240 "redis-master-0".to_string(),
241 "Running (1/1 containers)".to_string(),
242 ),
243 (
244 "redis-slave-0".to_string(),
245 "Running (1/1 containers)".to_string(),
246 ),
247 ("redis-slave-1".to_string(), "Pending".to_string()),
248 ],
249 "kube-system" => vec![
250 (
251 format!("coredns-5d78c9869d-{:05x}", rand1),
252 "Running (1/1 containers)".to_string(),
253 ),
254 (
255 format!("coredns-5d78c9869d-{:05x}", rand2),
256 "Running (1/1 containers)".to_string(),
257 ),
258 ("etcd-minikube".to_string(), "Running".to_string()),
259 ("kube-apiserver-minikube".to_string(), "Running".to_string()),
260 (
261 "kube-controller-manager-minikube".to_string(),
262 "Running".to_string(),
263 ),
264 (format!("kube-proxy-{:05x}", rand3), "Running".to_string()),
265 ("kube-scheduler-minikube".to_string(), "Running".to_string()),
266 ],
267 _ => vec![],
268 }
269}
270
271fn get_pods_in_namespace(namespace: &str) -> Vec<String> {
272 use std::time::{SystemTime, UNIX_EPOCH};
273
274 // Generate random suffix based on current time
275 let timestamp = SystemTime::now()
276 .duration_since(UNIX_EPOCH)
277 .unwrap()
278 .as_secs();
279
280 // Simple pseudo-random generation
281 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
282 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
283 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
284
285 match namespace {
286 "default" => vec![
287 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
288 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
289 format!("redis-master-0"),
290 format!("redis-slave-0"),
291 format!("redis-slave-1"),
292 ],
293 "kube-system" => vec![
294 format!("coredns-5d78c9869d-{:05x}", rand1),
295 format!("coredns-5d78c9869d-{:05x}", rand2),
296 format!("etcd-minikube"),
297 format!("kube-apiserver-minikube"),
298 format!("kube-controller-manager-minikube"),
299 format!("kube-proxy-{:05x}", rand3),
300 format!("kube-scheduler-minikube"),
301 ],
302 _ => vec![],
303 }
304}
305
306fn get_services_in_namespace(namespace: &str) -> Vec<String> {
307 match namespace {
308 "default" => vec![
309 "kubernetes".to_string(),
310 "nginx-service".to_string(),
311 "redis-master".to_string(),
312 "redis-slave".to_string(),
313 ],
314 "kube-system" => vec!["kube-dns".to_string()],
315 _ => vec![],
316 }
317}
318
319fn build_completion_command() -> Command {
320 CommandBuilder::new("completion")
321 .short("Generate shell completion scripts")
322 .long("Generate shell completion scripts for kubectl")
323 .arg_completion(|_ctx, prefix| {
324 let shells = vec![
325 ("bash", "Bash shell completion"),
326 ("zsh", "Zsh shell completion"),
327 ("fish", "Fish shell completion"),
328 ];
329
330 let mut result = CompletionResult::new();
331 for (shell, description) in shells {
332 if shell.starts_with(prefix) {
333 result =
334 result.add_with_description(shell.to_string(), description.to_string());
335 }
336 }
337
338 Ok(result)
339 })
340 .run(|ctx| {
341 let shell_name = ctx.args().first().ok_or_else(|| {
342 flag_rs::Error::ArgumentParsing(
343 "shell name required (bash, zsh, or fish)".to_string(),
344 )
345 })?;
346
347 let shell = match shell_name.as_str() {
348 "bash" => Shell::Bash,
349 "zsh" => Shell::Zsh,
350 "fish" => Shell::Fish,
351 _ => {
352 return Err(flag_rs::Error::ArgumentParsing(format!(
353 "unsupported shell: {}",
354 shell_name
355 )));
356 }
357 };
358
359 // In a real app, you'd get the root command from a shared reference
360 // For this example, we'll recreate it
361 let root = build_kubectl();
362 println!("{}", root.generate_completion(shell));
363
364 Ok(())
365 })
366 .build()
367}More examples
4fn main() {
5 let app = CommandBuilder::new("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}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}8fn main() {
9 let app = CommandBuilder::new("active-help-demo")
10 .short("Demonstrates ActiveHelp system")
11 .long("This example shows how ActiveHelp provides contextual hints during tab completion")
12 .flag(
13 Flag::new("namespace")
14 .short('n')
15 .usage("Kubernetes namespace")
16 .value_type(FlagType::String),
17 )
18 .flag(
19 Flag::new("output")
20 .short('o')
21 .usage("Output format")
22 .value_type(FlagType::String),
23 )
24 .subcommand(
25 CommandBuilder::new("get")
26 .short("Get resources")
27 .subcommand(
28 CommandBuilder::new("pods")
29 .short("Get pods")
30 .arg_completion(|ctx, prefix| {
31 let mut result = CompletionResult::new();
32
33 // Add contextual help based on current state
34 if ctx.flag("namespace").is_none() {
35 result = result.add_help_text(
36 "Tip: Use -n <namespace> to list pods from a specific namespace",
37 );
38 }
39
40 if ctx.flag("output").is_none() {
41 result = result.add_conditional_help(
42 "Use -o json for machine-readable output",
43 |_| true, // Always show this tip
44 );
45 }
46
47 // Simulate pod completions
48 let namespace = ctx
49 .flag("namespace")
50 .map(String::as_str)
51 .unwrap_or("default");
52
53 let pods = match namespace {
54 "default" => vec![
55 ("nginx-abc123", "Running"),
56 ("redis-def456", "Running"),
57 ("postgres-ghi789", "CrashLoopBackOff"),
58 ],
59 "kube-system" => vec![
60 ("coredns-xyz789", "Running"),
61 ("kube-proxy-abc123", "Running"),
62 ("etcd-master", "Running"),
63 ],
64 _ => vec![],
65 };
66
67 // Add pods with status descriptions
68 for (pod, status) in &pods {
69 if pod.starts_with(prefix) {
70 result = result.add_with_description(
71 *pod,
72 format!("Status: {status}"),
73 );
74 }
75 }
76
77 // Add help if there's a problematic pod
78 if pods.iter().any(|(_, status)| status == &"CrashLoopBackOff") {
79 result = result.add_help_text(
80 "Warning: Some pods are in CrashLoopBackOff state. Use 'describe' to investigate.",
81 );
82 }
83
84 Ok(result)
85 })
86 .run(|ctx| {
87 let namespace = ctx
88 .flag("namespace")
89 .map(String::as_str)
90 .unwrap_or("default");
91 let output = ctx.flag("output").map(String::as_str).unwrap_or("table");
92
93 println!("Getting pods from namespace: {namespace}");
94 println!("Output format: {output}");
95
96 if let Some(pod) = ctx.args().first() {
97 println!("Getting specific pod: {pod}");
98 } else {
99 println!("Listing all pods");
100 }
101 Ok(())
102 })
103 .build(),
104 )
105 .subcommand(
106 CommandBuilder::new("services")
107 .short("Get services")
108 .arg_completion(|_ctx, _prefix| {
109 let mut result = CompletionResult::new()
110 .add_with_description("nginx-service", "Type: LoadBalancer")
111 .add_with_description("redis-service", "Type: ClusterIP")
112 .add_with_description("postgres-service", "Type: NodePort");
113
114 // Add conditional help based on flags
115 result = result.add_conditional_help(
116 "Tip: Use -o wide to see more details about services",
117 |ctx| {
118 ctx.flag("output")
119 .map(|o| o != "wide")
120 .unwrap_or(true)
121 },
122 );
123
124 Ok(result)
125 })
126 .build(),
127 )
128 .build(),
129 )
130 .subcommand(
131 CommandBuilder::new("create")
132 .short("Create resources")
133 .flag(
134 Flag::new("file")
135 .short('f')
136 .usage("Filename to use to create the resource")
137 .value_type(FlagType::String)
138 .required(),
139 )
140 .flag_completion("file", |_ctx, prefix| {
141 let mut result = CompletionResult::new();
142
143 // Simulate file completions
144 let files = vec![
145 ("deployment.yaml", "Deployment configuration"),
146 ("service.yaml", "Service configuration"),
147 ("configmap.yaml", "ConfigMap configuration"),
148 ("secret.yaml", "Secret configuration"),
149 ("pod.yaml", "Pod configuration"),
150 ];
151
152 for (file, desc) in files {
153 if file.starts_with(prefix) {
154 result = result.add_with_description(file, desc);
155 }
156 }
157
158 // Add contextual help
159 result = result.add_help_text(
160 "Files should be valid Kubernetes manifests in YAML or JSON format",
161 );
162
163 if std::path::Path::new(prefix)
164 .extension()
165 .is_some_and(|ext| ext.eq_ignore_ascii_case("json"))
166 {
167 result = result.add_help_text(
168 "Note: JSON format is supported but YAML is more common in Kubernetes",
169 );
170 }
171
172 Ok(result)
173 })
174 .run(|ctx| {
175 let file = ctx.flag("file").expect("File is required");
176 println!("Creating resources from file: {file}");
177 Ok(())
178 })
179 .build(),
180 )
181 .subcommand(
182 CommandBuilder::new("debug")
183 .short("Debug and troubleshoot resources")
184 .arg_completion(|_ctx, _prefix| {
185 Ok(CompletionResult::new()
186 .add_with_description("pod/nginx-abc123", "Debug a specific pod")
187 .add_with_description("node/worker-1", "Debug a node")
188 .add_with_description("deployment/nginx", "Debug a deployment")
189 .add_help_text("Debug creates an interactive debugging session")
190 .add_help_text("Common debugging commands: kubectl logs, kubectl exec, kubectl describe")
191 .add_conditional_help(
192 "Tip: Use 'kubectl logs -f' to follow log output in real-time",
193 |ctx| ctx.args().is_empty(),
194 ))
195 })
196 .build(),
197 )
198 .build();
199
200 let args: Vec<String> = std::env::args().skip(1).collect();
201 if let Err(e) = app.execute(args) {
202 eprintln!("Error: {e}");
203 std::process::exit(1);
204 }
205}Sourcepub fn args_mut(&mut self) -> &mut Vec<String>
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);Sourcepub fn flag(&self, name: &str) -> Option<&String>
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?
79fn build_get_pods() -> Command {
80 CommandBuilder::new("pods")
81 .aliases(vec!["po", "pod"])
82 .short("List pods")
83 .arg_completion(|ctx, prefix| {
84 // This is the key feature - dynamic completion based on runtime state!
85 let namespace = ctx
86 .flag("namespace")
87 .map(String::as_str)
88 .unwrap_or("default");
89
90 // In real kubectl, this would query the K8s API
91 let pods = get_pods_with_status(namespace);
92 let mut result = CompletionResult::new();
93
94 for (pod_name, status) in pods {
95 if pod_name.starts_with(prefix) {
96 result = result.add_with_description(pod_name, status);
97 }
98 }
99
100 Ok(result)
101 })
102 .run(|ctx| {
103 let namespace = ctx
104 .flag("namespace")
105 .map(String::as_str)
106 .unwrap_or("default");
107
108 println!("Listing pods in namespace: {namespace}");
109
110 if let Some(pod_name) = ctx.args().first() {
111 println!("Getting specific pod: {pod_name}");
112 } else {
113 for pod in get_pods_in_namespace(namespace) {
114 println!("pod/{}", pod);
115 }
116 }
117
118 Ok(())
119 })
120 .build()
121}
122
123fn build_get_services() -> Command {
124 CommandBuilder::new("services")
125 .aliases(vec!["svc", "service"])
126 .short("List services")
127 .arg_completion(|ctx, prefix| {
128 let namespace = ctx
129 .flag("namespace")
130 .map(String::as_str)
131 .unwrap_or("default");
132
133 let services = get_services_in_namespace(namespace);
134 Ok(CompletionResult::new()
135 .extend(services.into_iter().filter(|svc| svc.starts_with(prefix))))
136 })
137 .run(|ctx| {
138 let namespace = ctx
139 .flag("namespace")
140 .map(String::as_str)
141 .unwrap_or("default");
142
143 println!("Listing services in namespace: {namespace}");
144 Ok(())
145 })
146 .build()
147}
148
149fn build_get_deployments() -> Command {
150 CommandBuilder::new("deployments")
151 .aliases(vec!["deploy", "deployment"])
152 .short("List deployments")
153 .run(|ctx| {
154 let namespace = ctx
155 .flag("namespace")
156 .map(String::as_str)
157 .unwrap_or("default");
158
159 println!("Listing deployments in namespace: {namespace}");
160 Ok(())
161 })
162 .build()
163}More examples
12fn main() {
13 let app = CommandBuilder::new("error-demo")
14 .short("Demonstrates error handling")
15 .long("This example shows various error scenarios and how flag-rs handles them")
16 .subcommand(
17 CommandBuilder::new("deploy")
18 .short("Deploy the application")
19 .args(ArgValidator::ExactArgs(1))
20 .run(|ctx| {
21 let env = ctx.args().first().unwrap();
22 if !["dev", "staging", "production"].contains(&env.as_str()) {
23 return Err(Error::Validation(format!(
24 "invalid environment '{}', must be one of: dev, staging, production",
25 env
26 )));
27 }
28 println!("Deploying to {}", env);
29 Ok(())
30 })
31 .build(),
32 )
33 .subcommand(
34 CommandBuilder::new("serve")
35 .short("Start the server")
36 .flag(
37 Flag::new("port")
38 .short('p')
39 .usage("Port to listen on")
40 .value_type(FlagType::Int)
41 .required(),
42 )
43 .run(|ctx| {
44 let port = ctx.flag("port").unwrap();
45 println!("Server starting on port {}", port);
46 Ok(())
47 })
48 .build(),
49 )
50 .subcommand(
51 CommandBuilder::new("migrate")
52 .short("Run database migrations")
53 .subcommand(
54 CommandBuilder::new("up")
55 .short("Run pending migrations")
56 .run(|_| {
57 println!("Running migrations...");
58 Ok(())
59 })
60 .build(),
61 )
62 .subcommand(
63 CommandBuilder::new("down")
64 .short("Rollback migrations")
65 .args(ArgValidator::MaximumArgs(1))
66 .run(|ctx| {
67 let steps = ctx
68 .args()
69 .first()
70 .and_then(|s| s.parse::<u32>().ok())
71 .unwrap_or(1);
72 println!(
73 "Rolling back {} migration{}",
74 steps,
75 if steps == 1 { "" } else { "s" }
76 );
77 Ok(())
78 })
79 .build(),
80 )
81 .build(),
82 )
83 .subcommand(
84 CommandBuilder::new("config")
85 .short("Manage configuration")
86 .aliases(vec!["cfg", "conf"])
87 .run(|_| {
88 println!("Configuration management");
89 Ok(())
90 })
91 .build(),
92 )
93 .build();
94
95 println!("=== Error Handling Demo ===\n");
96 println!("This demo shows how flag-rs handles various error scenarios.\n");
97 println!("Try these commands to see different error types:\n");
98 println!(
99 "1. {} deploi (typo - will show suggestions)",
100 std::env::args().next().unwrap_or_default()
101 );
102 println!(
103 "2. {} deploy (missing required argument)",
104 std::env::args().next().unwrap_or_default()
105 );
106 println!(
107 "3. {} deploy dev staging (too many arguments)",
108 std::env::args().next().unwrap_or_default()
109 );
110 println!(
111 "4. {} deploy test (invalid environment)",
112 std::env::args().next().unwrap_or_default()
113 );
114 println!(
115 "5. {} serve (missing required flag)",
116 std::env::args().next().unwrap_or_default()
117 );
118 println!(
119 "6. {} serve -p abc (invalid flag value)",
120 std::env::args().next().unwrap_or_default()
121 );
122 println!(
123 "7. {} migrate (subcommand required)",
124 std::env::args().next().unwrap_or_default()
125 );
126 println!(
127 "8. {} confi (typo - will suggest 'config')",
128 std::env::args().next().unwrap_or_default()
129 );
130 println!("\n---\n");
131
132 let args: Vec<String> = std::env::args().skip(1).collect();
133 if let Err(e) = app.execute(args) {
134 eprintln!("{}", e);
135 std::process::exit(1);
136 }
137}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}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}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}17fn main() {
18 let app = CommandBuilder::new("lifecycle-demo")
19 .short("Demonstrates lifecycle hooks")
20 .long("This example shows how lifecycle hooks execute in the proper order")
21 .flag(
22 Flag::new("verbose")
23 .short('v')
24 .usage("Enable verbose output")
25 .value_type(FlagType::Bool),
26 )
27 // Persistent hooks run for this command and all subcommands
28 .persistent_pre_run(|ctx| {
29 log_hook("Root PersistentPreRun");
30 if ctx.flag("verbose").is_some() {
31 println!(" -> Verbose mode enabled globally");
32 }
33 Ok(())
34 })
35 .persistent_post_run(|ctx| {
36 log_hook("Root PersistentPostRun");
37 if ctx.flag("verbose").is_some() {
38 println!(" -> Cleaning up verbose logging");
39 }
40 Ok(())
41 })
42 .subcommand(
43 CommandBuilder::new("server")
44 .short("Server management commands")
45 .persistent_pre_run(|_ctx| {
46 log_hook("Server PersistentPreRun");
47 println!(" -> Initializing server module");
48 Ok(())
49 })
50 .persistent_post_run(|_ctx| {
51 log_hook("Server PersistentPostRun");
52 println!(" -> Cleaning up server module");
53 Ok(())
54 })
55 .subcommand(
56 CommandBuilder::new("start")
57 .short("Start the server")
58 .flag(
59 Flag::new("port")
60 .short('p')
61 .usage("Port to listen on")
62 .value_type(FlagType::Int),
63 )
64 .pre_run(|ctx| {
65 log_hook("Start PreRun");
66 let port = ctx
67 .flag("port")
68 .and_then(|s| s.parse::<u16>().ok())
69 .unwrap_or(8080);
70 println!(" -> Validating port {}", port);
71 Ok(())
72 })
73 .run(|ctx| {
74 log_hook("Start Run");
75 let port = ctx
76 .flag("port")
77 .and_then(|s| s.parse::<u16>().ok())
78 .unwrap_or(8080);
79 println!(" -> Starting server on port {}", port);
80 Ok(())
81 })
82 .post_run(|_ctx| {
83 log_hook("Start PostRun");
84 println!(" -> Server started successfully");
85 Ok(())
86 })
87 .build(),
88 )
89 .subcommand(
90 CommandBuilder::new("stop")
91 .short("Stop the server")
92 .pre_run(|_ctx| {
93 log_hook("Stop PreRun");
94 println!(" -> Checking if server is running");
95 Ok(())
96 })
97 .run(|_ctx| {
98 log_hook("Stop Run");
99 println!(" -> Stopping server");
100 Ok(())
101 })
102 .post_run(|_ctx| {
103 log_hook("Stop PostRun");
104 println!(" -> Server stopped");
105 Ok(())
106 })
107 .build(),
108 )
109 .build(),
110 )
111 .subcommand(
112 CommandBuilder::new("database")
113 .short("Database management commands")
114 .persistent_pre_run(|_ctx| {
115 log_hook("Database PersistentPreRun");
116 println!(" -> Connecting to database");
117 Ok(())
118 })
119 .persistent_post_run(|_ctx| {
120 log_hook("Database PersistentPostRun");
121 println!(" -> Closing database connection");
122 Ok(())
123 })
124 .subcommand(
125 CommandBuilder::new("migrate")
126 .short("Run database migrations")
127 .pre_run(|_ctx| {
128 log_hook("Migrate PreRun");
129 println!(" -> Checking migration status");
130 Ok(())
131 })
132 .run(|_ctx| {
133 log_hook("Migrate Run");
134 println!(" -> Running migrations");
135 Ok(())
136 })
137 .post_run(|_ctx| {
138 log_hook("Migrate PostRun");
139 println!(" -> Migrations completed");
140 Ok(())
141 })
142 .build(),
143 )
144 .build(),
145 )
146 .build();
147
148 println!("=== Lifecycle Hooks Demo ===\n");
149 println!("This demo shows the execution order of lifecycle hooks.");
150 println!("Try these commands to see different hook patterns:\n");
151 println!(
152 "1. {} server start",
153 std::env::args().next().unwrap_or_default()
154 );
155 println!(
156 "2. {} -v server start -p 3000",
157 std::env::args().next().unwrap_or_default()
158 );
159 println!(
160 "3. {} database migrate",
161 std::env::args().next().unwrap_or_default()
162 );
163 println!("\n---\n");
164
165 // Reset counter before execution
166 COUNTER.store(0, Ordering::SeqCst);
167
168 let args: Vec<String> = std::env::args().skip(1).collect();
169 if let Err(e) = app.execute(args) {
170 eprintln!("Error: {}", e);
171 std::process::exit(1);
172 }
173
174 println!("\n=== Hook Execution Summary ===");
175 println!("Total hooks executed: {}", COUNTER.load(Ordering::SeqCst));
176 println!("\nExecution order:");
177 println!("1. Parent PersistentPreRun (root → child)");
178 println!("2. Command PreRun");
179 println!("3. Command Run");
180 println!("4. Command PostRun");
181 println!("5. Parent PersistentPostRun (child → root)");
182}Sourcepub fn flags(&self) -> &HashMap<String, String>
pub fn flags(&self) -> &HashMap<String, String>
Returns a reference to all flags
Examples found in repository?
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
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}Sourcepub fn flag_bool(&self, name: &str) -> Option<bool>
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);Sourcepub fn flag_int(&self, name: &str) -> Option<i64>
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);Sourcepub fn flag_float(&self, name: &str) -> Option<f64>
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);Sourcepub fn flag_str_or<'a>(&'a self, name: &str, default: &'a str) -> &'a str
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 flagdefault- 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?
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
4fn main() {
5 // Demonstrates all the new builder API improvements
6 let app = CommandBuilder::new("app")
7 .short("Modern CLI app with improved API")
8 .long(
9 "This example showcases the builder API improvements including:\n\
10 - Type-specific flag constructors\n\
11 - Inline flag completions\n\
12 - Bulk flag/subcommand methods\n\
13 - Type-safe context access",
14 )
15 // Bulk flag addition
16 .flags(vec![
17 // Type-specific constructors
18 Flag::bool("verbose")
19 .short('v')
20 .usage("Enable verbose output")
21 .default_bool(false),
22 Flag::bool("quiet").short('q').usage("Suppress all output"),
23 Flag::int("threads")
24 .short('t')
25 .usage("Number of worker threads")
26 .default_int(4),
27 Flag::choice("log-level", &["debug", "info", "warn", "error"])
28 .usage("Set the logging level")
29 .default_str("info")
30 .completion(|_ctx, prefix| {
31 // Inline completion with descriptions
32 let levels = vec![
33 ("debug", "Show all messages including debug"),
34 ("info", "Show informational messages and above"),
35 ("warn", "Show warnings and errors only"),
36 ("error", "Show errors only"),
37 ];
38
39 let mut result = flag_rs::CompletionResult::new();
40 for (level, desc) in levels {
41 if level.starts_with(prefix) {
42 result =
43 result.add_with_description(level.to_string(), desc.to_string());
44 }
45 }
46 Ok(result)
47 }),
48 ])
49 // Bulk subcommand addition
50 .subcommands(vec![
51 CommandBuilder::new("init")
52 .short("Initialize a new project")
53 .flags(vec![
54 Flag::string("name")
55 .short('n')
56 .usage("Project name")
57 .required(),
58 Flag::choice("template", &["basic", "web", "api", "full"])
59 .usage("Project template to use")
60 .default_str("basic"),
61 Flag::directory("path")
62 .short('p')
63 .usage("Directory to create project in")
64 .completion(|_ctx, prefix| {
65 // In a real app, list actual directories
66 let dirs = vec!["./", "../", "/tmp/", "~/projects/"];
67 Ok(flag_rs::CompletionResult::new().extend(
68 dirs.into_iter()
69 .filter(|d| d.starts_with(prefix))
70 .map(String::from),
71 ))
72 }),
73 ])
74 .run(|ctx| {
75 // Type-safe flag access
76 let name = ctx.flag_str_or("name", "my-project");
77 let template = ctx.flag_str_or("template", "basic");
78 let path = ctx.flag_str_or("path", ".");
79 let verbose = ctx.flag_bool_or("verbose", false);
80
81 if verbose {
82 println!("Initializing {} project '{}' in {}", template, name, path);
83 } else {
84 println!("Creating project '{}'...", name);
85 }
86
87 Ok(())
88 })
89 .build(),
90 CommandBuilder::new("build")
91 .short("Build the project")
92 .flags(vec![
93 Flag::bool("release")
94 .short('r')
95 .usage("Build in release mode"),
96 Flag::string_slice("features")
97 .short('f')
98 .usage("Enable features (can be specified multiple times)")
99 .completion(|_ctx, prefix| {
100 let features = vec!["async", "tls", "compression", "metrics"];
101 Ok(flag_rs::CompletionResult::new().extend(
102 features
103 .into_iter()
104 .filter(|f| f.starts_with(prefix))
105 .map(String::from),
106 ))
107 }),
108 Flag::range("jobs", 1, 32)
109 .short('j')
110 .usage("Number of parallel jobs")
111 .default_int(i64::try_from(num_cpus()).unwrap_or(4)),
112 ])
113 .run(|ctx| {
114 let release = ctx.flag_bool_or("release", false);
115 let jobs = ctx.flag_int_or("jobs", i64::try_from(num_cpus()).unwrap_or(4));
116 let quiet = ctx.flag_bool_or("quiet", false);
117
118 if !quiet {
119 println!(
120 "Building in {} mode with {} jobs",
121 if release { "release" } else { "debug" },
122 jobs
123 );
124
125 if let Some(features) = ctx.flag("features") {
126 println!("Features: {}", features);
127 }
128 }
129
130 Ok(())
131 })
132 .build(),
133 CommandBuilder::new("test")
134 .short("Run tests")
135 .flags(vec![
136 Flag::string("filter").usage("Only run tests matching this pattern"),
137 Flag::bool("nocapture").usage("Don't capture test output"),
138 Flag::int("test-threads")
139 .usage("Number of threads to use for running tests")
140 .default_int(1),
141 ])
142 .run(|ctx| {
143 let threads = ctx.flag_int_or("test-threads", 1);
144 let nocapture = ctx.flag_bool_or("nocapture", false);
145 let verbose = ctx.flag_bool_or("verbose", false);
146
147 if verbose {
148 println!("Running tests with {} thread(s)", threads);
149 if nocapture {
150 println!("Output capture disabled");
151 }
152
153 if let Some(filter) = ctx.flag("filter") {
154 println!("Filter: {}", filter);
155 }
156 }
157
158 println!("Running tests...");
159 Ok(())
160 })
161 .build(),
162 ])
163 .run(|_ctx| {
164 // Root command - show help by default
165 println!("Use --help for usage information");
166 Ok(())
167 })
168 .build();
169
170 let args: Vec<String> = std::env::args().skip(1).collect();
171 if let Err(e) = app.execute(args) {
172 eprintln!("Error: {}", e);
173 std::process::exit(1);
174 }
175}Sourcepub fn flag_bool_or(&self, name: &str, default: bool) -> bool
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 flagdefault- 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?
4fn main() {
5 // Demonstrates all the new builder API improvements
6 let app = CommandBuilder::new("app")
7 .short("Modern CLI app with improved API")
8 .long(
9 "This example showcases the builder API improvements including:\n\
10 - Type-specific flag constructors\n\
11 - Inline flag completions\n\
12 - Bulk flag/subcommand methods\n\
13 - Type-safe context access",
14 )
15 // Bulk flag addition
16 .flags(vec![
17 // Type-specific constructors
18 Flag::bool("verbose")
19 .short('v')
20 .usage("Enable verbose output")
21 .default_bool(false),
22 Flag::bool("quiet").short('q').usage("Suppress all output"),
23 Flag::int("threads")
24 .short('t')
25 .usage("Number of worker threads")
26 .default_int(4),
27 Flag::choice("log-level", &["debug", "info", "warn", "error"])
28 .usage("Set the logging level")
29 .default_str("info")
30 .completion(|_ctx, prefix| {
31 // Inline completion with descriptions
32 let levels = vec![
33 ("debug", "Show all messages including debug"),
34 ("info", "Show informational messages and above"),
35 ("warn", "Show warnings and errors only"),
36 ("error", "Show errors only"),
37 ];
38
39 let mut result = flag_rs::CompletionResult::new();
40 for (level, desc) in levels {
41 if level.starts_with(prefix) {
42 result =
43 result.add_with_description(level.to_string(), desc.to_string());
44 }
45 }
46 Ok(result)
47 }),
48 ])
49 // Bulk subcommand addition
50 .subcommands(vec![
51 CommandBuilder::new("init")
52 .short("Initialize a new project")
53 .flags(vec![
54 Flag::string("name")
55 .short('n')
56 .usage("Project name")
57 .required(),
58 Flag::choice("template", &["basic", "web", "api", "full"])
59 .usage("Project template to use")
60 .default_str("basic"),
61 Flag::directory("path")
62 .short('p')
63 .usage("Directory to create project in")
64 .completion(|_ctx, prefix| {
65 // In a real app, list actual directories
66 let dirs = vec!["./", "../", "/tmp/", "~/projects/"];
67 Ok(flag_rs::CompletionResult::new().extend(
68 dirs.into_iter()
69 .filter(|d| d.starts_with(prefix))
70 .map(String::from),
71 ))
72 }),
73 ])
74 .run(|ctx| {
75 // Type-safe flag access
76 let name = ctx.flag_str_or("name", "my-project");
77 let template = ctx.flag_str_or("template", "basic");
78 let path = ctx.flag_str_or("path", ".");
79 let verbose = ctx.flag_bool_or("verbose", false);
80
81 if verbose {
82 println!("Initializing {} project '{}' in {}", template, name, path);
83 } else {
84 println!("Creating project '{}'...", name);
85 }
86
87 Ok(())
88 })
89 .build(),
90 CommandBuilder::new("build")
91 .short("Build the project")
92 .flags(vec![
93 Flag::bool("release")
94 .short('r')
95 .usage("Build in release mode"),
96 Flag::string_slice("features")
97 .short('f')
98 .usage("Enable features (can be specified multiple times)")
99 .completion(|_ctx, prefix| {
100 let features = vec!["async", "tls", "compression", "metrics"];
101 Ok(flag_rs::CompletionResult::new().extend(
102 features
103 .into_iter()
104 .filter(|f| f.starts_with(prefix))
105 .map(String::from),
106 ))
107 }),
108 Flag::range("jobs", 1, 32)
109 .short('j')
110 .usage("Number of parallel jobs")
111 .default_int(i64::try_from(num_cpus()).unwrap_or(4)),
112 ])
113 .run(|ctx| {
114 let release = ctx.flag_bool_or("release", false);
115 let jobs = ctx.flag_int_or("jobs", i64::try_from(num_cpus()).unwrap_or(4));
116 let quiet = ctx.flag_bool_or("quiet", false);
117
118 if !quiet {
119 println!(
120 "Building in {} mode with {} jobs",
121 if release { "release" } else { "debug" },
122 jobs
123 );
124
125 if let Some(features) = ctx.flag("features") {
126 println!("Features: {}", features);
127 }
128 }
129
130 Ok(())
131 })
132 .build(),
133 CommandBuilder::new("test")
134 .short("Run tests")
135 .flags(vec![
136 Flag::string("filter").usage("Only run tests matching this pattern"),
137 Flag::bool("nocapture").usage("Don't capture test output"),
138 Flag::int("test-threads")
139 .usage("Number of threads to use for running tests")
140 .default_int(1),
141 ])
142 .run(|ctx| {
143 let threads = ctx.flag_int_or("test-threads", 1);
144 let nocapture = ctx.flag_bool_or("nocapture", false);
145 let verbose = ctx.flag_bool_or("verbose", false);
146
147 if verbose {
148 println!("Running tests with {} thread(s)", threads);
149 if nocapture {
150 println!("Output capture disabled");
151 }
152
153 if let Some(filter) = ctx.flag("filter") {
154 println!("Filter: {}", filter);
155 }
156 }
157
158 println!("Running tests...");
159 Ok(())
160 })
161 .build(),
162 ])
163 .run(|_ctx| {
164 // Root command - show help by default
165 println!("Use --help for usage information");
166 Ok(())
167 })
168 .build();
169
170 let args: Vec<String> = std::env::args().skip(1).collect();
171 if let Err(e) = app.execute(args) {
172 eprintln!("Error: {}", e);
173 std::process::exit(1);
174 }
175}Sourcepub fn flag_int_or(&self, name: &str, default: i64) -> i64
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 flagdefault- 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?
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
4fn main() {
5 // Demonstrates all the new builder API improvements
6 let app = CommandBuilder::new("app")
7 .short("Modern CLI app with improved API")
8 .long(
9 "This example showcases the builder API improvements including:\n\
10 - Type-specific flag constructors\n\
11 - Inline flag completions\n\
12 - Bulk flag/subcommand methods\n\
13 - Type-safe context access",
14 )
15 // Bulk flag addition
16 .flags(vec![
17 // Type-specific constructors
18 Flag::bool("verbose")
19 .short('v')
20 .usage("Enable verbose output")
21 .default_bool(false),
22 Flag::bool("quiet").short('q').usage("Suppress all output"),
23 Flag::int("threads")
24 .short('t')
25 .usage("Number of worker threads")
26 .default_int(4),
27 Flag::choice("log-level", &["debug", "info", "warn", "error"])
28 .usage("Set the logging level")
29 .default_str("info")
30 .completion(|_ctx, prefix| {
31 // Inline completion with descriptions
32 let levels = vec![
33 ("debug", "Show all messages including debug"),
34 ("info", "Show informational messages and above"),
35 ("warn", "Show warnings and errors only"),
36 ("error", "Show errors only"),
37 ];
38
39 let mut result = flag_rs::CompletionResult::new();
40 for (level, desc) in levels {
41 if level.starts_with(prefix) {
42 result =
43 result.add_with_description(level.to_string(), desc.to_string());
44 }
45 }
46 Ok(result)
47 }),
48 ])
49 // Bulk subcommand addition
50 .subcommands(vec![
51 CommandBuilder::new("init")
52 .short("Initialize a new project")
53 .flags(vec![
54 Flag::string("name")
55 .short('n')
56 .usage("Project name")
57 .required(),
58 Flag::choice("template", &["basic", "web", "api", "full"])
59 .usage("Project template to use")
60 .default_str("basic"),
61 Flag::directory("path")
62 .short('p')
63 .usage("Directory to create project in")
64 .completion(|_ctx, prefix| {
65 // In a real app, list actual directories
66 let dirs = vec!["./", "../", "/tmp/", "~/projects/"];
67 Ok(flag_rs::CompletionResult::new().extend(
68 dirs.into_iter()
69 .filter(|d| d.starts_with(prefix))
70 .map(String::from),
71 ))
72 }),
73 ])
74 .run(|ctx| {
75 // Type-safe flag access
76 let name = ctx.flag_str_or("name", "my-project");
77 let template = ctx.flag_str_or("template", "basic");
78 let path = ctx.flag_str_or("path", ".");
79 let verbose = ctx.flag_bool_or("verbose", false);
80
81 if verbose {
82 println!("Initializing {} project '{}' in {}", template, name, path);
83 } else {
84 println!("Creating project '{}'...", name);
85 }
86
87 Ok(())
88 })
89 .build(),
90 CommandBuilder::new("build")
91 .short("Build the project")
92 .flags(vec![
93 Flag::bool("release")
94 .short('r')
95 .usage("Build in release mode"),
96 Flag::string_slice("features")
97 .short('f')
98 .usage("Enable features (can be specified multiple times)")
99 .completion(|_ctx, prefix| {
100 let features = vec!["async", "tls", "compression", "metrics"];
101 Ok(flag_rs::CompletionResult::new().extend(
102 features
103 .into_iter()
104 .filter(|f| f.starts_with(prefix))
105 .map(String::from),
106 ))
107 }),
108 Flag::range("jobs", 1, 32)
109 .short('j')
110 .usage("Number of parallel jobs")
111 .default_int(i64::try_from(num_cpus()).unwrap_or(4)),
112 ])
113 .run(|ctx| {
114 let release = ctx.flag_bool_or("release", false);
115 let jobs = ctx.flag_int_or("jobs", i64::try_from(num_cpus()).unwrap_or(4));
116 let quiet = ctx.flag_bool_or("quiet", false);
117
118 if !quiet {
119 println!(
120 "Building in {} mode with {} jobs",
121 if release { "release" } else { "debug" },
122 jobs
123 );
124
125 if let Some(features) = ctx.flag("features") {
126 println!("Features: {}", features);
127 }
128 }
129
130 Ok(())
131 })
132 .build(),
133 CommandBuilder::new("test")
134 .short("Run tests")
135 .flags(vec![
136 Flag::string("filter").usage("Only run tests matching this pattern"),
137 Flag::bool("nocapture").usage("Don't capture test output"),
138 Flag::int("test-threads")
139 .usage("Number of threads to use for running tests")
140 .default_int(1),
141 ])
142 .run(|ctx| {
143 let threads = ctx.flag_int_or("test-threads", 1);
144 let nocapture = ctx.flag_bool_or("nocapture", false);
145 let verbose = ctx.flag_bool_or("verbose", false);
146
147 if verbose {
148 println!("Running tests with {} thread(s)", threads);
149 if nocapture {
150 println!("Output capture disabled");
151 }
152
153 if let Some(filter) = ctx.flag("filter") {
154 println!("Filter: {}", filter);
155 }
156 }
157
158 println!("Running tests...");
159 Ok(())
160 })
161 .build(),
162 ])
163 .run(|_ctx| {
164 // Root command - show help by default
165 println!("Use --help for usage information");
166 Ok(())
167 })
168 .build();
169
170 let args: Vec<String> = std::env::args().skip(1).collect();
171 if let Err(e) = app.execute(args) {
172 eprintln!("Error: {}", e);
173 std::process::exit(1);
174 }
175}Sourcepub fn flag_float_or(&self, name: &str, default: f64) -> f64
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 flagdefault- 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);Sourcepub fn set<T: Any + Send + Sync>(&mut self, value: T)
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 beSend + 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() });