use crate::cli::{groups, help};
use clap::{Args, Parser, Subcommand};
fn parse_date(s: &str) -> Result<String, clap::Error> {
jiff::civil::Date::strptime("%Y-%m-%d", s)
.map(|_| s.to_string())
.map_err(|_| {
clap::Error::raw(
clap::error::ErrorKind::ValueValidation,
format!("invalid date '{s}': expected YYYY-MM-DD"),
)
})
}
const DEFAULT_BASE_URL: &str = "https://financialmodelingprep.com/stable/";
#[derive(Debug, Parser)]
#[command(
name = "fmp-agent",
version,
about = help::CLI_ABOUT,
after_help = help::EXIT_CODES,
disable_help_subcommand = true
)]
pub struct Cli {
#[arg(long, global = true, env = "FMP_API_KEY", hide_env_values = true, help = help::API_KEY)]
pub api_key: Option<String>,
#[arg(long, global = true, env = "FMP_BASE_URL", default_value = DEFAULT_BASE_URL, help = help::BASE_URL)]
pub base_url: String,
#[arg(short = 'v', long = "verbose", action = clap::ArgAction::Count)]
pub verbose: u8,
#[arg(long, global = true, help = help::STRICT_EMPTY)]
pub strict_empty: bool,
#[command(subcommand)]
pub(crate) command: Command,
}
#[derive(Debug, Subcommand)]
pub(crate) enum Command {
#[command(
about = help::HELP_GROUP_ABOUT,
long_about = help::HELP_GROUP_LONG,
disable_help_subcommand = true
)]
Help {
#[command(subcommand)]
command: Option<HelpTopic>,
},
#[command(about = help::SEARCH_ABOUT, long_about = help::SEARCH_LONG)]
Search {
#[arg(help = help::SEARCH_QUERY)]
query: String,
},
#[command(about = help::SCHEMA_ABOUT, long_about = help::SCHEMA_LONG)]
Schema,
#[command(about = help::COMMANDS_ABOUT, long_about = help::COMMANDS_LONG)]
Commands {
#[arg(long, help = help::COMMANDS_GROUPED)]
grouped: bool,
},
#[command(about = help::COMPLETIONS_ABOUT, long_about = help::COMPLETIONS_LONG)]
Completions {
#[arg(value_enum)]
shell: clap_complete::Shell,
},
#[command(about = help::DOCTOR_ABOUT, long_about = help::DOCTOR_LONG)]
Doctor,
#[command(about = help::QUOTE_ALIAS_ABOUT, long_about = help::QUOTE_ALIAS_LONG)]
Quote(SymbolArgs),
#[command(
about = help::HISTORICAL_ALIAS_ABOUT,
long_about = help::HISTORICAL_ALIAS_LONG
)]
Historical(SymbolDateRangeArgs),
#[command(about = help::PROFILE_ALIAS_ABOUT, long_about = help::PROFILE_ALIAS_LONG)]
Profile(SymbolArgs),
#[command(about = help::EARNINGS_ALIAS_ABOUT, long_about = help::EARNINGS_ALIAS_LONG)]
Earnings(DateRangeArgs),
#[command(about = help::COMPANY_GROUP_ABOUT, long_about = help::COMPANY_GROUP_LONG)]
Company {
#[command(subcommand)]
command: Option<groups::company::Cmd>,
},
#[command(about = help::MARKET_GROUP_ABOUT, long_about = help::MARKET_GROUP_LONG)]
Market {
#[command(subcommand)]
command: Option<groups::market::Cmd>,
},
#[command(
about = help::FUNDAMENTALS_GROUP_ABOUT,
long_about = help::FUNDAMENTALS_GROUP_LONG
)]
Fundamentals {
#[command(subcommand)]
command: Option<groups::fundamentals::Cmd>,
},
#[command(about = help::ANALYST_GROUP_ABOUT, long_about = help::ANALYST_GROUP_LONG)]
Analyst {
#[command(subcommand)]
command: Option<groups::analyst::Cmd>,
},
#[command(about = help::INSIDER_GROUP_ABOUT, long_about = help::INSIDER_GROUP_LONG)]
Insider {
#[command(subcommand)]
command: Option<groups::insider::Cmd>,
},
#[command(about = help::CALENDAR_GROUP_ABOUT, long_about = help::CALENDAR_GROUP_LONG)]
Calendar {
#[command(subcommand)]
command: Option<groups::calendar::Cmd>,
},
#[command(
name = "macro",
about = help::MACRO_GROUP_ABOUT,
long_about = help::MACRO_GROUP_LONG
)]
MacroEcon {
#[command(subcommand)]
command: Option<groups::macro_econ::Cmd>,
},
#[command(about = help::TECHNICAL_GROUP_ABOUT, long_about = help::TECHNICAL_GROUP_LONG)]
Technical {
#[command(subcommand)]
command: Option<groups::technical::Cmd>,
},
#[command(about = help::SEC_GROUP_ABOUT, long_about = help::SEC_GROUP_LONG)]
Sec {
#[command(subcommand)]
command: Option<groups::sec::Cmd>,
},
#[command(about = help::ETF_GROUP_ABOUT, long_about = help::ETF_GROUP_LONG)]
Etf {
#[command(subcommand)]
command: Option<groups::etf::Cmd>,
},
#[command(about = help::CRYPTO_GROUP_ABOUT, long_about = help::CRYPTO_GROUP_LONG)]
Crypto {
#[command(subcommand)]
command: Option<groups::crypto::Cmd>,
},
#[command(about = help::FOREX_GROUP_ABOUT, long_about = help::FOREX_GROUP_LONG)]
Forex {
#[command(subcommand)]
command: Option<groups::forex::Cmd>,
},
#[command(about = help::NEWS_GROUP_ABOUT, long_about = help::NEWS_GROUP_LONG)]
News {
#[command(subcommand)]
command: Option<groups::news::Cmd>,
},
}
#[derive(Debug, Subcommand)]
pub(crate) enum HelpTopic {
#[command(about = help::HELP_ENVIRONMENT_ABOUT, long_about = help::HELP_ENVIRONMENT_LONG)]
Environment,
#[command(about = help::HELP_EXIT_CODES_ABOUT, long_about = help::HELP_EXIT_CODES_LONG)]
ExitCodes,
#[command(about = help::HELP_SCHEMA_ABOUT, long_about = help::HELP_SCHEMA_LONG)]
Schema,
#[command(about = help::HELP_EXAMPLES_ABOUT, long_about = help::HELP_EXAMPLES_LONG)]
Examples,
}
#[derive(Debug, Args)]
pub struct SymbolArgs {
#[arg(help = help::SYMBOL)]
pub symbol: String,
}
#[derive(Debug, Args)]
pub struct SymbolsArgs {
#[arg(required = true, num_args = 1.., value_name = "SYMBOLS", help = help::SYMBOLS)]
pub symbols: Vec<String>,
}
#[derive(Debug, Args)]
pub struct SymbolLimitArgs {
#[arg(help = help::STOCK_SYMBOL)]
pub symbol: String,
#[arg(long, default_value_t = 10, help = help::LIMIT_ROWS)]
pub limit: u16,
}
#[derive(Debug, Args)]
pub struct AnnualReportFormArgs {
#[arg(help = help::STOCK_SYMBOL)]
pub symbol: String,
#[arg(long, help = help::YEAR)]
pub year: u16,
#[arg(long, default_value = "FY", help = help::PERIOD)]
pub period: String,
}
#[derive(Debug, Args)]
pub struct SymbolDateRangeArgs {
#[arg(help = help::SYMBOL)]
pub symbol: String,
#[arg(long, help = help::FROM, value_parser = parse_date)]
pub from: Option<String>,
#[arg(long, help = help::TO, value_parser = parse_date)]
pub to: Option<String>,
}
#[derive(Debug, Args)]
pub struct DateRangeArgs {
#[arg(long, help = help::FROM, value_parser = parse_date)]
pub from: Option<String>,
#[arg(long, help = help::TO, value_parser = parse_date)]
pub to: Option<String>,
}
#[derive(Debug, Args)]
pub struct NameDateRangeArgs {
#[arg(help = help::ECONOMIC_INDICATOR_NAME)]
pub name: String,
#[arg(long, help = help::FROM, value_parser = parse_date)]
pub from: Option<String>,
#[arg(long, help = help::TO, value_parser = parse_date)]
pub to: Option<String>,
}
#[derive(Debug, Args)]
pub struct AnnualArgs {
#[arg(help = help::STOCK_SYMBOL)]
pub symbol: String,
#[arg(long, default_value_t = 5, help = help::ANNUAL_LIMIT)]
pub limit: u16,
}
#[derive(Debug, Args)]
pub struct TechnicalSmaArgs {
#[arg(help = help::STOCK_SYMBOL)]
pub symbol: String,
#[arg(long, default_value_t = 10, help = help::PERIOD_LENGTH)]
pub period_length: u16,
#[arg(long, default_value = "1day", help = help::TIMEFRAME)]
pub timeframe: String,
}
#[derive(Debug, Args)]
pub struct StockNewsArgs {
#[arg(help = help::STOCK_SYMBOL)]
pub symbol: String,
#[arg(long, default_value_t = 10, help = help::LIMIT_ROWS)]
pub limit: u16,
}
#[derive(Debug, Args)]
pub struct PagedArgs {
#[arg(long, default_value_t = 0, help = help::PAGE)]
pub page: u16,
#[arg(long, default_value_t = 10, help = help::PAGE_LIMIT)]
pub limit: u16,
}