Skip to main content

aperture_cli/
suggestions.rs

1//! Smart suggestions for error messages and command discovery
2
3use crate::cache::models::CachedSpec;
4use crate::search::CommandSearcher;
5use std::collections::BTreeMap;
6
7/// Generate suggestions for an operation that wasn't found
8#[must_use]
9pub fn suggest_similar_operations(spec: &CachedSpec, attempted_operation: &str) -> Vec<String> {
10    let searcher = CommandSearcher::new();
11
12    // Create a BTreeMap with just this spec
13    let mut specs = BTreeMap::new();
14    specs.insert(spec.name.clone(), spec.clone());
15
16    // Search for similar commands
17    let results = searcher.search(&specs, attempted_operation, None);
18
19    // Take top 3 most relevant suggestions
20    results.map_or_else(
21        |_| Vec::new(), // Return empty vec if search fails
22        |search_results| {
23            search_results
24                .into_iter()
25                .take(3)
26                .map(|result| {
27                    format!(
28                        "aperture api {} {}",
29                        result.api_context, result.command_path
30                    )
31                })
32                .collect()
33        },
34    )
35}
36
37/// Generate suggestions for missing parameters
38#[must_use]
39pub fn suggest_parameter_format(param_name: &str, param_type: Option<&str>) -> String {
40    let type_hint = param_type.unwrap_or("value");
41    format!("--{param_name} <{type_hint}>")
42}
43
44/// Generate suggestions for invalid parameter values
45#[must_use]
46pub fn suggest_valid_values(param_name: &str, valid_values: &[String]) -> String {
47    if valid_values.is_empty() {
48        return format!("Check the parameter documentation for valid values of '{param_name}'");
49    }
50
51    let values = valid_values
52        .iter()
53        .take(5)
54        .map(|v| format!("'{v}'"))
55        .collect::<Vec<_>>()
56        .join(", ");
57
58    let suffix = if valid_values.len() > 5 {
59        "include"
60    } else {
61        "are"
62    };
63    let ellipsis = if valid_values.len() > 5 { ", ..." } else { "" };
64
65    format!("Valid values for '{param_name}' {suffix}: {values}{ellipsis}")
66}
67
68/// Generate suggestions for authentication errors
69#[must_use]
70pub fn suggest_auth_fix(scheme_name: &str, env_var: Option<&str>) -> String {
71    env_var.map_or_else(
72        || format!("Configure authentication for '{scheme_name}' using 'aperture config secrets'"),
73        |var| format!("Set the {var} environment variable or run 'aperture config secrets' to configure authentication")
74    )
75}
76
77/// Generate suggestions for network errors
78#[must_use]
79pub fn suggest_network_fix(url: &str, error: &str) -> String {
80    match () {
81        () if error.contains("DNS") || error.contains("resolve") => {
82            format!("Check that the host '{url}' is reachable and the URL is correct")
83        }
84        () if error.contains("timeout") => {
85            "Try increasing the timeout with --timeout or check your network connection".to_string()
86        }
87        () if error.contains("refused") => {
88            format!("The server at '{url}' refused the connection. Check if the service is running")
89        }
90        () => "Check your network connection and try again".to_string(),
91    }
92}