cached_completion_demo/
cached_completion_demo.rs

1//! Demonstrates completion caching for performance
2//!
3//! This example shows how to use the completion cache to improve performance
4//! for expensive completion operations like API calls or file system scans.
5
6#![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
14/// Simulates an expensive operation (e.g., API call, file system scan)
15fn expensive_completion(prefix: &str) -> Vec<String> {
16    // Simulate network latency or expensive computation
17    thread::sleep(Duration::from_millis(500));
18
19    // In a real app, this might query a database or API
20    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    // 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}