pub struct CompletionResult {
pub values: Vec<String>,
pub descriptions: Vec<String>,
pub active_help: Vec<ActiveHelp>,
}Expand description
Result returned by completion functions
CompletionResult contains completion suggestions along with optional
descriptions for each suggestion. This is used by the shell completion
system to provide helpful hints to users.
§Examples
use flag_rs::completion::CompletionResult;
let completions = CompletionResult::new()
.add("create")
.add_with_description("delete", "Remove a resource")
.add_with_description("list", "Show all resources")
.extend(vec!["get".to_string(), "update".to_string()]);
assert_eq!(completions.values.len(), 5);
assert_eq!(completions.values[1], "delete");
assert_eq!(completions.descriptions[1], "Remove a resource");Fields§
§values: Vec<String>The completion values to suggest
descriptions: Vec<String>Optional descriptions for each value
active_help: Vec<ActiveHelp>ActiveHelp messages to display
Implementations§
Source§impl CompletionResult
impl CompletionResult
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new empty completion result
Examples found in repository?
examples/kubectl.rs (line 32)
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}
78
79fn build_get_pods() -> Command {
80 CommandBuilder::new("pods")
81 .aliases(vec!["po", "pod"])
82 .short("List pods")
83 .arg_completion(|ctx, prefix| {
84 // This is the key feature - dynamic completion based on runtime state!
85 let namespace = ctx
86 .flag("namespace")
87 .map(String::as_str)
88 .unwrap_or("default");
89
90 // In real kubectl, this would query the K8s API
91 let pods = get_pods_with_status(namespace);
92 let mut result = CompletionResult::new();
93
94 for (pod_name, status) in pods {
95 if pod_name.starts_with(prefix) {
96 result = result.add_with_description(pod_name, status);
97 }
98 }
99
100 Ok(result)
101 })
102 .run(|ctx| {
103 let namespace = ctx
104 .flag("namespace")
105 .map(String::as_str)
106 .unwrap_or("default");
107
108 println!("Listing pods in namespace: {namespace}");
109
110 if let Some(pod_name) = ctx.args().first() {
111 println!("Getting specific pod: {pod_name}");
112 } else {
113 for pod in get_pods_in_namespace(namespace) {
114 println!("pod/{}", pod);
115 }
116 }
117
118 Ok(())
119 })
120 .build()
121}
122
123fn build_get_services() -> Command {
124 CommandBuilder::new("services")
125 .aliases(vec!["svc", "service"])
126 .short("List services")
127 .arg_completion(|ctx, prefix| {
128 let namespace = ctx
129 .flag("namespace")
130 .map(String::as_str)
131 .unwrap_or("default");
132
133 let services = get_services_in_namespace(namespace);
134 Ok(CompletionResult::new()
135 .extend(services.into_iter().filter(|svc| svc.starts_with(prefix))))
136 })
137 .run(|ctx| {
138 let namespace = ctx
139 .flag("namespace")
140 .map(String::as_str)
141 .unwrap_or("default");
142
143 println!("Listing services in namespace: {namespace}");
144 Ok(())
145 })
146 .build()
147}
148
149fn build_get_deployments() -> Command {
150 CommandBuilder::new("deployments")
151 .aliases(vec!["deploy", "deployment"])
152 .short("List deployments")
153 .run(|ctx| {
154 let namespace = ctx
155 .flag("namespace")
156 .map(String::as_str)
157 .unwrap_or("default");
158
159 println!("Listing deployments in namespace: {namespace}");
160 Ok(())
161 })
162 .build()
163}
164
165fn build_describe_command() -> Command {
166 CommandBuilder::new("describe")
167 .short("Show details of a specific resource")
168 .flag(
169 Flag::new("output")
170 .short('o')
171 .usage("Output format")
172 .value_type(FlagType::String),
173 )
174 .run(|_ctx| {
175 println!("Describe command - add resource type subcommands");
176 Ok(())
177 })
178 .build()
179}
180
181fn build_delete_command() -> Command {
182 CommandBuilder::new("delete")
183 .short("Delete resources")
184 .run(|_ctx| {
185 println!("Delete command - add resource type subcommands");
186 Ok(())
187 })
188 .build()
189}
190
191// Mock functions that would normally query the Kubernetes API
192fn get_namespaces_with_descriptions() -> Vec<(String, String)> {
193 vec![
194 (
195 "default".to_string(),
196 "Default namespace for user workloads".to_string(),
197 ),
198 (
199 "kube-system".to_string(),
200 "Kubernetes system components".to_string(),
201 ),
202 (
203 "kube-public".to_string(),
204 "Public resources accessible to all users".to_string(),
205 ),
206 (
207 "development".to_string(),
208 "Development environment".to_string(),
209 ),
210 (
211 "staging".to_string(),
212 "Staging environment for pre-production testing".to_string(),
213 ),
214 (
215 "production".to_string(),
216 "Production environment (CAUTION)".to_string(),
217 ),
218 ]
219}
220
221fn get_pods_with_status(namespace: &str) -> Vec<(String, String)> {
222 use std::time::{SystemTime, UNIX_EPOCH};
223
224 // Generate random suffix based on current time
225 let timestamp = SystemTime::now()
226 .duration_since(UNIX_EPOCH)
227 .unwrap()
228 .as_secs();
229
230 // Simple pseudo-random generation
231 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
232 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
233 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
234
235 match namespace {
236 "default" => vec![
237 (
238 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
239 "Running (2/2 containers)".to_string(),
240 ),
241 (
242 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
243 "Running (2/2 containers)".to_string(),
244 ),
245 (
246 "redis-master-0".to_string(),
247 "Running (1/1 containers)".to_string(),
248 ),
249 (
250 "redis-slave-0".to_string(),
251 "Running (1/1 containers)".to_string(),
252 ),
253 ("redis-slave-1".to_string(), "Pending".to_string()),
254 ],
255 "kube-system" => vec![
256 (
257 format!("coredns-5d78c9869d-{:05x}", rand1),
258 "Running (1/1 containers)".to_string(),
259 ),
260 (
261 format!("coredns-5d78c9869d-{:05x}", rand2),
262 "Running (1/1 containers)".to_string(),
263 ),
264 ("etcd-minikube".to_string(), "Running".to_string()),
265 ("kube-apiserver-minikube".to_string(), "Running".to_string()),
266 (
267 "kube-controller-manager-minikube".to_string(),
268 "Running".to_string(),
269 ),
270 (format!("kube-proxy-{:05x}", rand3), "Running".to_string()),
271 ("kube-scheduler-minikube".to_string(), "Running".to_string()),
272 ],
273 _ => vec![],
274 }
275}
276
277fn get_pods_in_namespace(namespace: &str) -> Vec<String> {
278 use std::time::{SystemTime, UNIX_EPOCH};
279
280 // Generate random suffix based on current time
281 let timestamp = SystemTime::now()
282 .duration_since(UNIX_EPOCH)
283 .unwrap()
284 .as_secs();
285
286 // Simple pseudo-random generation
287 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
288 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
289 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
290
291 match namespace {
292 "default" => vec![
293 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
294 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
295 format!("redis-master-0"),
296 format!("redis-slave-0"),
297 format!("redis-slave-1"),
298 ],
299 "kube-system" => vec![
300 format!("coredns-5d78c9869d-{:05x}", rand1),
301 format!("coredns-5d78c9869d-{:05x}", rand2),
302 format!("etcd-minikube"),
303 format!("kube-apiserver-minikube"),
304 format!("kube-controller-manager-minikube"),
305 format!("kube-proxy-{:05x}", rand3),
306 format!("kube-scheduler-minikube"),
307 ],
308 _ => vec![],
309 }
310}
311
312fn get_services_in_namespace(namespace: &str) -> Vec<String> {
313 match namespace {
314 "default" => vec![
315 "kubernetes".to_string(),
316 "nginx-service".to_string(),
317 "redis-master".to_string(),
318 "redis-slave".to_string(),
319 ],
320 "kube-system" => vec!["kube-dns".to_string()],
321 _ => vec![],
322 }
323}
324
325fn build_completion_command() -> Command {
326 CommandBuilder::new("completion")
327 .short("Generate shell completion scripts")
328 .long("Generate shell completion scripts for kubectl")
329 .arg_completion(|_ctx, prefix| {
330 let shells = vec![
331 ("bash", "Bash shell completion"),
332 ("zsh", "Zsh shell completion"),
333 ("fish", "Fish shell completion"),
334 ];
335
336 let mut result = CompletionResult::new();
337 for (shell, description) in shells {
338 if shell.starts_with(prefix) {
339 result =
340 result.add_with_description(shell.to_string(), description.to_string());
341 }
342 }
343
344 Ok(result)
345 })
346 .run(|ctx| {
347 let shell_name = ctx.args().first().ok_or_else(|| {
348 flag_rs::Error::ArgumentParsing(
349 "shell name required (bash, zsh, or fish)".to_string(),
350 )
351 })?;
352
353 let shell = match shell_name.as_str() {
354 "bash" => Shell::Bash,
355 "zsh" => Shell::Zsh,
356 "fish" => Shell::Fish,
357 _ => {
358 return Err(flag_rs::Error::ArgumentParsing(format!(
359 "unsupported shell: {}",
360 shell_name
361 )));
362 }
363 };
364
365 // In a real app, you'd get the root command from a shared reference
366 // For this example, we'll recreate it
367 let root = build_kubectl();
368 println!("{}", root.generate_completion(shell));
369
370 Ok(())
371 })
372 .build()
373}More examples
examples/benchmark.rs (line 209)
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}examples/test_completion.rs (line 13)
3fn main() {
4 let cmd = CommandBuilder::new("test")
5 .short("Test completion with descriptions")
6 .flag(
7 Flag::new("environment")
8 .short('e')
9 .usage("Target environment")
10 .value_type(FlagType::String),
11 )
12 .flag_completion("environment", |_ctx, prefix| {
13 let mut result = CompletionResult::new();
14 let envs = vec![
15 ("dev", "Development environment - safe for testing"),
16 ("staging", "Staging environment - mirror of production"),
17 ("production", "Production environment - BE CAREFUL!"),
18 ];
19
20 for (env, desc) in envs {
21 if env.starts_with(prefix) {
22 result = result.add_with_description(env, desc);
23 }
24 }
25
26 Ok(result)
27 })
28 .subcommand(
29 CommandBuilder::new("deploy")
30 .short("Deploy the application")
31 .long("Deploy the application to the specified environment")
32 .build(),
33 )
34 .subcommand(
35 CommandBuilder::new("rollback")
36 .short("Rollback to previous version")
37 .long("Rollback the application to the previous deployed version")
38 .build(),
39 )
40 .build();
41
42 // Test completion output
43 if let Ok(shell_type) = std::env::var("TEST_COMPLETE") {
44 // For testing, override shell type to "display" to see formatted output
45 let display_mode = std::env::var("DISPLAY_MODE").is_ok();
46 let args: Vec<String> = std::env::args().skip(1).collect();
47
48 if display_mode {
49 // This is a hack for testing - normally shells would handle this
50 println!("Display mode - showing formatted completions:");
51 match cmd.handle_completion_request(&args) {
52 Ok(completions) => {
53 for completion in completions {
54 println!("{}", completion);
55 }
56 }
57 Err(e) => eprintln!("Completion error: {}", e),
58 }
59 } else {
60 match cmd.handle_completion_request(&args) {
61 Ok(completions) => {
62 println!("Shell type: {}", shell_type);
63 println!("Completions:");
64 for completion in completions {
65 println!("{}", completion);
66 }
67 }
68 Err(e) => eprintln!("Completion error: {}", e),
69 }
70 }
71 } else {
72 let args: Vec<String> = std::env::args().skip(1).collect();
73 if let Err(e) = cmd.execute(args) {
74 eprintln!("Error: {}", e);
75 std::process::exit(1);
76 }
77 }
78}examples/timeout_completion_demo.rs (line 57)
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 20)
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 79)
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}Additional examples can be found in:
Sourcepub fn add_with_description(
self,
value: impl Into<String>,
desc: impl Into<String>,
) -> Self
pub fn add_with_description( self, value: impl Into<String>, desc: impl Into<String>, ) -> Self
Adds a completion value with a description
§Arguments
value- The completion value to adddesc- A description of what this value represents
§Examples
use flag_rs::completion::CompletionResult;
let result = CompletionResult::new()
.add_with_description("--verbose", "Enable verbose output")
.add_with_description("--quiet", "Suppress output");Examples found in repository?
examples/kubectl.rs (line 36)
14fn build_kubectl() -> Command {
15 CommandBuilder::new("kubectl")
16 .short("Kubernetes command-line tool")
17 .long("kubectl controls the Kubernetes cluster manager")
18 .subcommand(build_get_command())
19 .subcommand(build_describe_command())
20 .subcommand(build_delete_command())
21 .subcommand(build_completion_command())
22 .flag(
23 Flag::new("namespace")
24 .short('n')
25 .usage("Kubernetes namespace")
26 .value_type(FlagType::String)
27 .default(FlagValue::String("default".to_string())),
28 )
29 .flag_completion("namespace", |_ctx, prefix| {
30 // In a real kubectl, this would query the API server
31 let namespaces = get_namespaces_with_descriptions();
32 let mut result = CompletionResult::new();
33
34 for (ns_name, description) in namespaces {
35 if ns_name.starts_with(prefix) {
36 result = result.add_with_description(ns_name, description);
37 }
38 }
39
40 Ok(result)
41 })
42 .build()
43}
44
45fn build_get_command() -> Command {
46 CommandBuilder::new("get")
47 .short("Display one or many resources")
48 .long("Display one or many resources. Prints a table of the most important information about the specified resources.")
49 .flag(
50 Flag::new("limit")
51 .short('l')
52 .usage("Maximum number of resources to display")
53 .value_type(FlagType::String)
54 )
55 .flag_completion("limit", |_ctx, prefix| {
56 let common_limits = vec![
57 ("10", "Display 10 items"),
58 ("20", "Display 20 items"),
59 ("50", "Display 50 items"),
60 ("100", "Display 100 items"),
61 ("all", "Display all items (no limit)"),
62 ];
63
64 let mut result = CompletionResult::new();
65 for (limit, description) in common_limits {
66 if limit.starts_with(prefix) {
67 result = result.add_with_description(limit.to_string(), description.to_string());
68 }
69 }
70
71 Ok(result)
72 })
73 .subcommand(build_get_pods())
74 .subcommand(build_get_services())
75 .subcommand(build_get_deployments())
76 .build()
77}
78
79fn build_get_pods() -> Command {
80 CommandBuilder::new("pods")
81 .aliases(vec!["po", "pod"])
82 .short("List pods")
83 .arg_completion(|ctx, prefix| {
84 // This is the key feature - dynamic completion based on runtime state!
85 let namespace = ctx
86 .flag("namespace")
87 .map(String::as_str)
88 .unwrap_or("default");
89
90 // In real kubectl, this would query the K8s API
91 let pods = get_pods_with_status(namespace);
92 let mut result = CompletionResult::new();
93
94 for (pod_name, status) in pods {
95 if pod_name.starts_with(prefix) {
96 result = result.add_with_description(pod_name, status);
97 }
98 }
99
100 Ok(result)
101 })
102 .run(|ctx| {
103 let namespace = ctx
104 .flag("namespace")
105 .map(String::as_str)
106 .unwrap_or("default");
107
108 println!("Listing pods in namespace: {namespace}");
109
110 if let Some(pod_name) = ctx.args().first() {
111 println!("Getting specific pod: {pod_name}");
112 } else {
113 for pod in get_pods_in_namespace(namespace) {
114 println!("pod/{}", pod);
115 }
116 }
117
118 Ok(())
119 })
120 .build()
121}
122
123fn build_get_services() -> Command {
124 CommandBuilder::new("services")
125 .aliases(vec!["svc", "service"])
126 .short("List services")
127 .arg_completion(|ctx, prefix| {
128 let namespace = ctx
129 .flag("namespace")
130 .map(String::as_str)
131 .unwrap_or("default");
132
133 let services = get_services_in_namespace(namespace);
134 Ok(CompletionResult::new()
135 .extend(services.into_iter().filter(|svc| svc.starts_with(prefix))))
136 })
137 .run(|ctx| {
138 let namespace = ctx
139 .flag("namespace")
140 .map(String::as_str)
141 .unwrap_or("default");
142
143 println!("Listing services in namespace: {namespace}");
144 Ok(())
145 })
146 .build()
147}
148
149fn build_get_deployments() -> Command {
150 CommandBuilder::new("deployments")
151 .aliases(vec!["deploy", "deployment"])
152 .short("List deployments")
153 .run(|ctx| {
154 let namespace = ctx
155 .flag("namespace")
156 .map(String::as_str)
157 .unwrap_or("default");
158
159 println!("Listing deployments in namespace: {namespace}");
160 Ok(())
161 })
162 .build()
163}
164
165fn build_describe_command() -> Command {
166 CommandBuilder::new("describe")
167 .short("Show details of a specific resource")
168 .flag(
169 Flag::new("output")
170 .short('o')
171 .usage("Output format")
172 .value_type(FlagType::String),
173 )
174 .run(|_ctx| {
175 println!("Describe command - add resource type subcommands");
176 Ok(())
177 })
178 .build()
179}
180
181fn build_delete_command() -> Command {
182 CommandBuilder::new("delete")
183 .short("Delete resources")
184 .run(|_ctx| {
185 println!("Delete command - add resource type subcommands");
186 Ok(())
187 })
188 .build()
189}
190
191// Mock functions that would normally query the Kubernetes API
192fn get_namespaces_with_descriptions() -> Vec<(String, String)> {
193 vec![
194 (
195 "default".to_string(),
196 "Default namespace for user workloads".to_string(),
197 ),
198 (
199 "kube-system".to_string(),
200 "Kubernetes system components".to_string(),
201 ),
202 (
203 "kube-public".to_string(),
204 "Public resources accessible to all users".to_string(),
205 ),
206 (
207 "development".to_string(),
208 "Development environment".to_string(),
209 ),
210 (
211 "staging".to_string(),
212 "Staging environment for pre-production testing".to_string(),
213 ),
214 (
215 "production".to_string(),
216 "Production environment (CAUTION)".to_string(),
217 ),
218 ]
219}
220
221fn get_pods_with_status(namespace: &str) -> Vec<(String, String)> {
222 use std::time::{SystemTime, UNIX_EPOCH};
223
224 // Generate random suffix based on current time
225 let timestamp = SystemTime::now()
226 .duration_since(UNIX_EPOCH)
227 .unwrap()
228 .as_secs();
229
230 // Simple pseudo-random generation
231 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
232 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
233 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
234
235 match namespace {
236 "default" => vec![
237 (
238 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
239 "Running (2/2 containers)".to_string(),
240 ),
241 (
242 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
243 "Running (2/2 containers)".to_string(),
244 ),
245 (
246 "redis-master-0".to_string(),
247 "Running (1/1 containers)".to_string(),
248 ),
249 (
250 "redis-slave-0".to_string(),
251 "Running (1/1 containers)".to_string(),
252 ),
253 ("redis-slave-1".to_string(), "Pending".to_string()),
254 ],
255 "kube-system" => vec![
256 (
257 format!("coredns-5d78c9869d-{:05x}", rand1),
258 "Running (1/1 containers)".to_string(),
259 ),
260 (
261 format!("coredns-5d78c9869d-{:05x}", rand2),
262 "Running (1/1 containers)".to_string(),
263 ),
264 ("etcd-minikube".to_string(), "Running".to_string()),
265 ("kube-apiserver-minikube".to_string(), "Running".to_string()),
266 (
267 "kube-controller-manager-minikube".to_string(),
268 "Running".to_string(),
269 ),
270 (format!("kube-proxy-{:05x}", rand3), "Running".to_string()),
271 ("kube-scheduler-minikube".to_string(), "Running".to_string()),
272 ],
273 _ => vec![],
274 }
275}
276
277fn get_pods_in_namespace(namespace: &str) -> Vec<String> {
278 use std::time::{SystemTime, UNIX_EPOCH};
279
280 // Generate random suffix based on current time
281 let timestamp = SystemTime::now()
282 .duration_since(UNIX_EPOCH)
283 .unwrap()
284 .as_secs();
285
286 // Simple pseudo-random generation
287 let rand1 = ((timestamp * 1_103_515_245 + 12345) / 65536) % 100_000;
288 let rand2 = ((rand1 * 1_103_515_245 + 12345) / 65536) % 100_000;
289 let rand3 = ((rand2 * 1_103_515_245 + 12345) / 65536) % 100_000;
290
291 match namespace {
292 "default" => vec![
293 format!("nginx-deployment-7fb96c846b-{:05x}", rand1),
294 format!("nginx-deployment-7fb96c846b-{:05x}", rand2),
295 format!("redis-master-0"),
296 format!("redis-slave-0"),
297 format!("redis-slave-1"),
298 ],
299 "kube-system" => vec![
300 format!("coredns-5d78c9869d-{:05x}", rand1),
301 format!("coredns-5d78c9869d-{:05x}", rand2),
302 format!("etcd-minikube"),
303 format!("kube-apiserver-minikube"),
304 format!("kube-controller-manager-minikube"),
305 format!("kube-proxy-{:05x}", rand3),
306 format!("kube-scheduler-minikube"),
307 ],
308 _ => vec![],
309 }
310}
311
312fn get_services_in_namespace(namespace: &str) -> Vec<String> {
313 match namespace {
314 "default" => vec![
315 "kubernetes".to_string(),
316 "nginx-service".to_string(),
317 "redis-master".to_string(),
318 "redis-slave".to_string(),
319 ],
320 "kube-system" => vec!["kube-dns".to_string()],
321 _ => vec![],
322 }
323}
324
325fn build_completion_command() -> Command {
326 CommandBuilder::new("completion")
327 .short("Generate shell completion scripts")
328 .long("Generate shell completion scripts for kubectl")
329 .arg_completion(|_ctx, prefix| {
330 let shells = vec![
331 ("bash", "Bash shell completion"),
332 ("zsh", "Zsh shell completion"),
333 ("fish", "Fish shell completion"),
334 ];
335
336 let mut result = CompletionResult::new();
337 for (shell, description) in shells {
338 if shell.starts_with(prefix) {
339 result =
340 result.add_with_description(shell.to_string(), description.to_string());
341 }
342 }
343
344 Ok(result)
345 })
346 .run(|ctx| {
347 let shell_name = ctx.args().first().ok_or_else(|| {
348 flag_rs::Error::ArgumentParsing(
349 "shell name required (bash, zsh, or fish)".to_string(),
350 )
351 })?;
352
353 let shell = match shell_name.as_str() {
354 "bash" => Shell::Bash,
355 "zsh" => Shell::Zsh,
356 "fish" => Shell::Fish,
357 _ => {
358 return Err(flag_rs::Error::ArgumentParsing(format!(
359 "unsupported shell: {}",
360 shell_name
361 )));
362 }
363 };
364
365 // In a real app, you'd get the root command from a shared reference
366 // For this example, we'll recreate it
367 let root = build_kubectl();
368 println!("{}", root.generate_completion(shell));
369
370 Ok(())
371 })
372 .build()
373}More examples
examples/benchmark.rs (line 235)
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}examples/test_completion.rs (line 22)
3fn main() {
4 let cmd = CommandBuilder::new("test")
5 .short("Test completion with descriptions")
6 .flag(
7 Flag::new("environment")
8 .short('e')
9 .usage("Target environment")
10 .value_type(FlagType::String),
11 )
12 .flag_completion("environment", |_ctx, prefix| {
13 let mut result = CompletionResult::new();
14 let envs = vec![
15 ("dev", "Development environment - safe for testing"),
16 ("staging", "Staging environment - mirror of production"),
17 ("production", "Production environment - BE CAREFUL!"),
18 ];
19
20 for (env, desc) in envs {
21 if env.starts_with(prefix) {
22 result = result.add_with_description(env, desc);
23 }
24 }
25
26 Ok(result)
27 })
28 .subcommand(
29 CommandBuilder::new("deploy")
30 .short("Deploy the application")
31 .long("Deploy the application to the specified environment")
32 .build(),
33 )
34 .subcommand(
35 CommandBuilder::new("rollback")
36 .short("Rollback to previous version")
37 .long("Rollback the application to the previous deployed version")
38 .build(),
39 )
40 .build();
41
42 // Test completion output
43 if let Ok(shell_type) = std::env::var("TEST_COMPLETE") {
44 // For testing, override shell type to "display" to see formatted output
45 let display_mode = std::env::var("DISPLAY_MODE").is_ok();
46 let args: Vec<String> = std::env::args().skip(1).collect();
47
48 if display_mode {
49 // This is a hack for testing - normally shells would handle this
50 println!("Display mode - showing formatted completions:");
51 match cmd.handle_completion_request(&args) {
52 Ok(completions) => {
53 for completion in completions {
54 println!("{}", completion);
55 }
56 }
57 Err(e) => eprintln!("Completion error: {}", e),
58 }
59 } else {
60 match cmd.handle_completion_request(&args) {
61 Ok(completions) => {
62 println!("Shell type: {}", shell_type);
63 println!("Completions:");
64 for completion in completions {
65 println!("{}", completion);
66 }
67 }
68 Err(e) => eprintln!("Completion error: {}", e),
69 }
70 }
71 } else {
72 let args: Vec<String> = std::env::args().skip(1).collect();
73 if let Err(e) = cmd.execute(args) {
74 eprintln!("Error: {}", e);
75 std::process::exit(1);
76 }
77 }
78}examples/timeout_completion_demo.rs (lines 81-88)
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 23)
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 (lines 82-88)
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}Additional examples can be found in:
Sourcepub fn extend<I: IntoIterator<Item = String>>(self, values: I) -> Self
pub fn extend<I: IntoIterator<Item = String>>(self, values: I) -> Self
Adds multiple completion values without descriptions
§Arguments
values- An iterator of completion values
§Examples
use flag_rs::completion::CompletionResult;
let options = vec!["opt1".to_string(), "opt2".to_string()];
let result = CompletionResult::new().extend(options);Examples found in repository?
examples/kubectl.rs (line 135)
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}More examples
examples/benchmark.rs (line 209)
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}examples/timeout_completion_demo.rs (lines 57-62)
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 (lines 50-55)
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 130)
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/builder_api_demo.rs (lines 67-71)
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 add_help(self, help: ActiveHelp) -> Self
pub fn add_help(self, help: ActiveHelp) -> Self
Sourcepub fn add_help_text<S: Into<String>>(self, message: S) -> Self
pub fn add_help_text<S: Into<String>>(self, message: S) -> Self
Adds an ActiveHelp message from a string
§Arguments
message- The help message text
§Examples
use flag_rs::completion::CompletionResult;
let result = CompletionResult::new()
.add_help_text("Use -n <namespace> to filter results");Examples found in repository?
examples/timeout_completion_demo.rs (lines 92-94)
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}More examples
examples/flag_completion_demo.rs (line 29)
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 94)
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/active_help_demo.rs (lines 35-37)
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 add_conditional_help<S, F>(self, message: S, condition: F) -> Self
pub fn add_conditional_help<S, F>(self, message: S, condition: F) -> Self
Adds a conditional ActiveHelp message
§Arguments
message- The help message textcondition- Function that determines if help should be shown
§Examples
use flag_rs::completion::CompletionResult;
let result = CompletionResult::new()
.add_conditional_help(
"Tip: Use --format json for machine-readable output",
|ctx| ctx.flag("format").is_none()
);Examples found in repository?
examples/active_help_demo.rs (lines 41-44)
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}Trait Implementations§
Source§impl Clone for CompletionResult
impl Clone for CompletionResult
Source§fn clone(&self) -> CompletionResult
fn clone(&self) -> CompletionResult
Returns a duplicate of the value. Read more
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Debug for CompletionResult
impl Debug for CompletionResult
Auto Trait Implementations§
impl Freeze for CompletionResult
impl !RefUnwindSafe for CompletionResult
impl Send for CompletionResult
impl Sync for CompletionResult
impl Unpin for CompletionResult
impl UnsafeUnpin for CompletionResult
impl !UnwindSafe for CompletionResult
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more