Skip to main content

aperture_cli/cli/commands/
docs.rs

1//! Handlers for `aperture docs`, `aperture overview`, and `aperture list-commands`.
2
3use crate::cache::models::CachedSpec;
4use crate::config::manager::{get_config_dir, ConfigManager};
5use crate::constants;
6use crate::docs::{DocumentationGenerator, HelpFormatter};
7use crate::engine::loader;
8use crate::error::Error;
9use crate::fs::OsFileSystem;
10use crate::output::Output;
11use std::path::PathBuf;
12
13pub fn list_commands(context: &str, output: &Output) -> Result<(), Error> {
14    let config_dir = if let Ok(dir) = std::env::var(constants::ENV_APERTURE_CONFIG_DIR) {
15        PathBuf::from(dir)
16    } else {
17        get_config_dir()?
18    };
19    let cache_dir = config_dir.join(constants::DIR_CACHE);
20    let spec = loader::load_cached_spec(&cache_dir, context).map_err(|e| match e {
21        Error::Io(_) => Error::spec_not_found(context),
22        _ => e,
23    })?;
24    let formatted_output = HelpFormatter::format_command_list(&spec);
25    // ast-grep-ignore: no-println
26    println!("{formatted_output}");
27    output.tip(format!(
28        "Use 'aperture docs {context}' for detailed API documentation"
29    ));
30    output.tip(format!(
31        "Use 'aperture search <term> --api {context}' to find specific operations"
32    ));
33    output.tip("Use shortcuts: 'aperture exec <operation-id> --help'");
34    Ok(())
35}
36
37/// Execute help command with enhanced documentation
38pub fn execute_help_command(
39    manager: &ConfigManager<OsFileSystem>,
40    api_name: Option<&str>,
41    tag: Option<&str>,
42    operation: Option<&str>,
43    enhanced: bool,
44    output: &Output,
45) -> Result<(), Error> {
46    match (api_name, tag, operation) {
47        (None, None, None) => {
48            let specs = load_all_specs(manager)?;
49            let doc_gen = DocumentationGenerator::new(specs);
50            // ast-grep-ignore: no-println
51            println!("{}", doc_gen.generate_interactive_menu());
52        }
53        (Some(api), tag_opt, operation_opt) => {
54            let specs = load_all_specs(manager)?;
55            let doc_gen = DocumentationGenerator::new(specs);
56            match (tag_opt, operation_opt) {
57                (None, None) => {
58                    let overview = doc_gen.generate_api_overview(api)?;
59                    // ast-grep-ignore: no-println
60                    println!("{overview}");
61                }
62                (Some(tag), Some(op)) => {
63                    let help = doc_gen.generate_command_help(api, tag, op)?;
64                    if enhanced {
65                        // ast-grep-ignore: no-println
66                        println!("{help}");
67                    } else {
68                        // ast-grep-ignore: no-println
69                        println!("{}", help.lines().take(20).collect::<Vec<_>>().join("\n"));
70                        output.tip("Use --enhanced for full documentation with examples");
71                    }
72                }
73                _ => {
74                    eprintln!("Invalid docs command. Usage:");
75                    eprintln!("  aperture docs                        # Interactive menu");
76                    eprintln!("  aperture docs <api>                  # API overview");
77                    eprintln!("  aperture docs <api> <tag> <operation> # Command help");
78                    std::process::exit(1);
79                }
80            }
81        }
82        _ => {
83            eprintln!("Invalid help command arguments");
84            std::process::exit(1);
85        }
86    }
87    Ok(())
88}
89
90/// Execute overview command
91#[allow(clippy::too_many_lines)]
92pub fn execute_overview_command(
93    manager: &ConfigManager<OsFileSystem>,
94    api_name: Option<&str>,
95    all: bool,
96    output: &Output,
97) -> Result<(), Error> {
98    if !all {
99        let Some(api) = api_name else {
100            eprintln!("Error: Must specify API name or use --all flag");
101            eprintln!("Usage:");
102            eprintln!("  aperture overview <api>");
103            eprintln!("  aperture overview --all");
104            std::process::exit(1);
105        };
106        let specs = load_all_specs(manager)?;
107        let doc_gen = DocumentationGenerator::new(specs);
108        let overview = doc_gen.generate_api_overview(api)?;
109        // ast-grep-ignore: no-println
110        println!("{overview}");
111        return Ok(());
112    }
113
114    let specs = load_all_specs(manager)?;
115    if specs.is_empty() {
116        output.info("No API specifications configured.");
117        output.info("Use 'aperture config add <name> <spec-file>' to get started.");
118        return Ok(());
119    }
120
121    // ast-grep-ignore: no-println
122    println!("All APIs Overview\n");
123    // ast-grep-ignore: no-println
124    println!("{}", "=".repeat(60));
125    for (api_name, spec) in &specs {
126        // ast-grep-ignore: no-println
127        println!("\n** {} ** (v{})", spec.name, spec.version);
128        if let Some(ref base_url) = spec.base_url {
129            // ast-grep-ignore: no-println
130            println!("   Base URL: {base_url}");
131        }
132        let operation_count = spec.commands.len();
133        // ast-grep-ignore: no-println
134        println!("   Operations: {operation_count}");
135        let mut method_counts = std::collections::BTreeMap::new();
136        for command in &spec.commands {
137            *method_counts.entry(command.method.clone()).or_insert(0) += 1;
138        }
139        let method_summary: Vec<String> = method_counts
140            .iter()
141            .map(|(method, count)| format!("{method}: {count}"))
142            .collect();
143        // ast-grep-ignore: no-println
144        println!("   Methods: {}", method_summary.join(", "));
145        // ast-grep-ignore: no-println
146        println!("   Quick start: aperture list-commands {api_name}");
147    }
148    // ast-grep-ignore: no-println
149    println!("\n{}", "=".repeat(60));
150    output.tip("Use 'aperture overview <api>' for detailed information about a specific API");
151    Ok(())
152}
153
154/// Load all cached specs from the manager
155pub fn load_all_specs(
156    manager: &ConfigManager<OsFileSystem>,
157) -> Result<std::collections::BTreeMap<String, CachedSpec>, Error> {
158    let specs = manager.list_specs()?;
159    let cache_dir = manager.config_dir().join(constants::DIR_CACHE);
160    let mut all_specs = std::collections::BTreeMap::new();
161    for spec_name in &specs {
162        match loader::load_cached_spec(&cache_dir, spec_name) {
163            Ok(spec) => {
164                all_specs.insert(spec_name.clone(), spec);
165            }
166            Err(e) => eprintln!("Warning: Could not load spec '{spec_name}': {e}"),
167        }
168    }
169    Ok(all_specs)
170}