cached_completion_demo/
cached_completion_demo.rs1#![allow(clippy::or_fun_call)]
7
8use flag_rs::completion_cache::CompletionCache;
9use flag_rs::{CommandBuilder, CompletionResult, Flag, FlagType};
10use std::sync::Arc;
11use std::thread;
12use std::time::{Duration, Instant};
13
14fn expensive_completion(prefix: &str) -> Vec<String> {
16 thread::sleep(Duration::from_millis(500));
18
19 let items = vec![
21 "nginx-deployment-abc123",
22 "nginx-deployment-def456",
23 "redis-cache-ghi789",
24 "redis-cache-jkl012",
25 "postgres-db-mno345",
26 "postgres-db-pqr678",
27 "mongodb-cluster-stu901",
28 "mongodb-cluster-vwx234",
29 ];
30
31 items
32 .into_iter()
33 .filter(|item| item.starts_with(prefix))
34 .map(String::from)
35 .collect()
36}
37
38fn main() {
39 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 let cache_key = CompletionCache::make_key(
64 &["kubectl".to_string(), "get".to_string(), "pods".to_string()],
65 prefix,
66 ctx.flags(),
67 );
68
69 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 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 if prefix.is_empty() {
93 result =
94 result.add_help_text("Tip: Start typing to filter pod names");
95 }
96
97 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 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 if std::env::var("KUBECTL_COMPLETE").is_ok() {
179 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}