use crate::cache::models::CachedSpec;
use crate::config::manager::{get_config_dir, ConfigManager};
use crate::constants;
use crate::docs::{DocumentationGenerator, HelpFormatter};
use crate::engine::loader;
use crate::error::Error;
use crate::fs::OsFileSystem;
use crate::output::Output;
use std::path::PathBuf;
pub fn list_commands(context: &str, output: &Output) -> Result<(), Error> {
let config_dir = if let Ok(dir) = std::env::var(constants::ENV_APERTURE_CONFIG_DIR) {
PathBuf::from(dir)
} else {
get_config_dir()?
};
let cache_dir = config_dir.join(constants::DIR_CACHE);
let spec = loader::load_cached_spec(&cache_dir, context).map_err(|e| match e {
Error::Io(_) => Error::spec_not_found(context),
_ => e,
})?;
let formatted_output = HelpFormatter::format_command_list(&spec);
println!("{formatted_output}");
output.tip(format!(
"Use 'aperture docs {context}' for detailed API documentation"
));
output.tip(format!(
"Use 'aperture search <term> --api {context}' to find specific operations"
));
output.tip("Use shortcuts: 'aperture exec <operation-id> --help'");
Ok(())
}
pub fn execute_help_command(
manager: &ConfigManager<OsFileSystem>,
api_name: Option<&str>,
tag: Option<&str>,
operation: Option<&str>,
enhanced: bool,
output: &Output,
) -> Result<(), Error> {
match (api_name, tag, operation) {
(None, None, None) => {
let specs = load_all_specs(manager)?;
let doc_gen = DocumentationGenerator::new(specs);
println!("{}", doc_gen.generate_interactive_menu());
}
(Some(api), tag_opt, operation_opt) => {
let specs = load_all_specs(manager)?;
let doc_gen = DocumentationGenerator::new(specs);
match (tag_opt, operation_opt) {
(None, None) => {
let overview = doc_gen.generate_api_overview(api)?;
println!("{overview}");
}
(Some(tag), Some(op)) => {
let help = doc_gen.generate_command_help(api, tag, op)?;
if enhanced {
println!("{help}");
} else {
println!("{}", help.lines().take(20).collect::<Vec<_>>().join("\n"));
output.tip("Use --enhanced for full documentation with examples");
}
}
_ => {
eprintln!("Invalid docs command. Usage:");
eprintln!(" aperture docs # Interactive menu");
eprintln!(" aperture docs <api> # API overview");
eprintln!(" aperture docs <api> <tag> <operation> # Command help");
std::process::exit(1);
}
}
}
_ => {
eprintln!("Invalid help command arguments");
std::process::exit(1);
}
}
Ok(())
}
#[allow(clippy::too_many_lines)]
pub fn execute_overview_command(
manager: &ConfigManager<OsFileSystem>,
api_name: Option<&str>,
all: bool,
output: &Output,
) -> Result<(), Error> {
if !all {
let Some(api) = api_name else {
eprintln!("Error: Must specify API name or use --all flag");
eprintln!("Usage:");
eprintln!(" aperture overview <api>");
eprintln!(" aperture overview --all");
std::process::exit(1);
};
let specs = load_all_specs(manager)?;
let doc_gen = DocumentationGenerator::new(specs);
let overview = doc_gen.generate_api_overview(api)?;
println!("{overview}");
return Ok(());
}
let specs = load_all_specs(manager)?;
if specs.is_empty() {
output.info("No API specifications configured.");
output.info("Use 'aperture config add <name> <spec-file>' to get started.");
return Ok(());
}
println!("All APIs Overview\n");
println!("{}", "=".repeat(60));
for (api_name, spec) in &specs {
println!("\n** {} ** (v{})", spec.name, spec.version);
if let Some(ref base_url) = spec.base_url {
println!(" Base URL: {base_url}");
}
let operation_count = spec.commands.len();
println!(" Operations: {operation_count}");
let mut method_counts = std::collections::BTreeMap::new();
for command in &spec.commands {
*method_counts.entry(command.method.clone()).or_insert(0) += 1;
}
let method_summary: Vec<String> = method_counts
.iter()
.map(|(method, count)| format!("{method}: {count}"))
.collect();
println!(" Methods: {}", method_summary.join(", "));
println!(" Quick start: aperture list-commands {api_name}");
}
println!("\n{}", "=".repeat(60));
output.tip("Use 'aperture overview <api>' for detailed information about a specific API");
Ok(())
}
pub fn load_all_specs(
manager: &ConfigManager<OsFileSystem>,
) -> Result<std::collections::BTreeMap<String, CachedSpec>, Error> {
let specs = manager.list_specs()?;
let cache_dir = manager.config_dir().join(constants::DIR_CACHE);
let mut all_specs = std::collections::BTreeMap::new();
for spec_name in &specs {
match loader::load_cached_spec(&cache_dir, spec_name) {
Ok(spec) => {
all_specs.insert(spec_name.clone(), spec);
}
Err(e) => tracing::warn!(spec = spec_name, error = %e, "could not load spec"),
}
}
Ok(all_specs)
}