active_help_demo/
active_help_demo.rs

1//! Demonstrates `ActiveHelp` system for contextual hints during completion
2//!
3//! This example shows how to use `ActiveHelp` to provide contextual assistance
4//! to users as they type commands, similar to Cobra's `ActiveHelp` feature.
5
6use flag_rs::{CommandBuilder, CompletionResult, Flag, FlagType};
7
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}