use clap::CommandFactory;
use serde_json::{Value, json};
use super::args::Cli;
const ALIAS_PREFERRED: &[(&str, &str)] = &[
("quote", "market quote"),
("historical", "market historical"),
("profile", "company profile"),
("earnings", "calendar earnings"),
];
pub(super) fn schema_payload() -> Value {
let cmd = Cli::command();
let mut groups: Vec<String> = Vec::new();
let mut commands: Vec<Value> = Vec::new();
for sub in cmd.get_subcommands() {
let name = sub.get_name();
let children: Vec<_> = sub.get_subcommands().collect();
if children.is_empty() {
let path = vec![name.to_owned()];
let preferred = preferred_path_for(name);
let api_key_required = name != "schema" && name != "commands" && name != "completions";
commands.push(leaf_to_json(sub, path, preferred, &[], api_key_required));
} else {
groups.push(name.to_owned());
for child in &children {
let child_name = child.get_name();
let path = vec![name.to_owned(), child_name.to_owned()];
let preferred = format!("{name} {child_name}");
let aliases = aliases_for(name, child_name);
commands.push(leaf_to_json(child, path, preferred, &aliases, true));
}
}
}
json!({
"schema_version": 2,
"binary": "fmp-agent",
"version": env!("CARGO_PKG_VERSION"),
"groups": groups,
"commands": commands,
})
}
fn leaf_to_json(
sub: &clap::Command,
path: Vec<String>,
preferred_path: String,
aliases: &[String],
api_key_required: bool,
) -> Value {
let name = sub.get_name().to_owned();
let about = sub.get_about().map(|s| s.to_string());
let long_about = sub.get_long_about().map(|s| s.to_string());
let args: Vec<Value> = sub
.get_arguments()
.filter(|a| !a.is_hide_set())
.map(|a| {
let kind = if a.is_positional() {
"positional"
} else {
"option"
};
let default_val = a
.get_default_values()
.first()
.and_then(|v| v.to_str())
.map(|s| json!(s));
let help_text = a.get_help().map(|s| s.to_string());
json!({
"name": a.get_id().as_str(),
"kind": kind,
"required": a.is_required_set(),
"default": default_val,
"help": help_text,
})
})
.collect();
json!({
"name": name,
"path": path,
"about": about,
"long_about": long_about,
"args": args,
"aliases": aliases,
"preferred_path": preferred_path,
"api_key_required": api_key_required,
})
}
fn preferred_path_for(name: &str) -> String {
for &(alias, canonical) in ALIAS_PREFERRED {
if alias == name {
return canonical.to_owned();
}
}
name.to_owned()
}
fn aliases_for(group: &str, child: &str) -> Vec<String> {
let canonical = format!("{group} {child}");
for &(alias, preferred) in ALIAS_PREFERRED {
if preferred == canonical {
return vec![alias.to_owned()];
}
}
Vec::new()
}