Skip to main content

CompletionResult

Struct CompletionResult 

Source
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

Source

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
Hide additional 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}
Source

pub fn add(self, value: impl Into<String>) -> Self

Adds a completion value without a description

§Arguments
  • value - The completion value to add
§Examples
use flag_rs::completion::CompletionResult;

let result = CompletionResult::new()
    .add("option1")
    .add("option2");
Source

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 add
  • desc - 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
Hide additional 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}
Source

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
Hide additional 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}
Source

pub fn add_help(self, help: ActiveHelp) -> Self

Adds an ActiveHelp message

§Arguments
  • help - The ActiveHelp message to add
§Examples
use flag_rs::completion::CompletionResult;
use flag_rs::active_help::ActiveHelp;

let result = CompletionResult::new()
    .add_help(ActiveHelp::new("Press TAB to see available options"));
Source

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
Hide additional 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}
Source

pub fn add_conditional_help<S, F>(self, message: S, condition: F) -> Self
where S: Into<String>, F: Fn(&Context) -> bool + Send + Sync + 'static,

Adds a conditional ActiveHelp message

§Arguments
  • message - The help message text
  • condition - 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}
Source§

impl CompletionResult

Source

pub fn merge(self, other: Self) -> Self

Merges two completion results, combining their values and help messages

Trait Implementations§

Source§

impl Clone for CompletionResult

Source§

fn clone(&self) -> CompletionResult

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for CompletionResult

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for CompletionResult

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

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

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.