use clap::{Args, Parser, Subcommand};
use clap_complete::Shell;
use crate::commands::adhoc_screen::AdhocScreenCommandArgs;
use crate::commands::industry::IndustryCommand;
use crate::commands::ownership::OwnershipCommand;
use crate::commands::screen::ScreenCommand;
use crate::commands::tree::TreeCommand;
use crate::commands::watchlist::WatchlistCommand;
#[derive(Debug, Parser)]
#[command(
name = "marketsurge-agent",
version,
about = "Query MarketSurge data as compact JSON",
long_about = "Query MarketSurge data as compact JSON. Auth reads browser cookies, so log in at https://marketsurge.investors.com first. Use --fields to limit top-level JSON fields in command output.",
after_help = "Examples:\n marketsurge-agent ratings AAPL MSFT\n marketsurge-agent --fields symbol,rs_rating ratings AAPL\n marketsurge-agent completions zsh > _marketsurge-agent",
arg_required_else_help = true
)]
pub struct Cli {
#[arg(long, global = true, value_delimiter = ',', value_name = "FIELD")]
pub fields: Vec<String>,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Debug, Subcommand)]
pub enum Commands {
#[command(
after_help = "Examples:\n marketsurge-agent adhoc-screen --symbols AAPL,MSFT --columns Symbol,CompanyName,EPSRating\n marketsurge-agent adhoc-screen --screen-id 12345 --limit 100"
)]
AdhocScreen(AdhocScreenCommandArgs),
#[command(
after_help = "Examples:\n marketsurge-agent chart AAPL MSFT\n marketsurge-agent chart --weekly AAPL"
)]
Chart(ChartArgs),
#[command(after_help = "Examples:\n marketsurge-agent fundamentals AAPL MSFT")]
Fundamentals(SymbolsArgs),
#[command(
after_help = "Examples:\n marketsurge-agent industry rs AAPL\n marketsurge-agent industry overview AAPL"
)]
Industry(IndustryArgs),
#[command(after_help = "Examples:\n marketsurge-agent market-data AAPL MSFT")]
MarketData(SymbolsArgs),
#[command(
after_help = "Examples:\n marketsurge-agent ownership summary AAPL\n marketsurge-agent ownership funds AAPL"
)]
Ownership(OwnershipArgs),
#[command(after_help = "Examples:\n marketsurge-agent ratings AAPL MSFT")]
Ratings(SymbolsArgs),
#[command(
after_help = "Examples:\n marketsurge-agent screen list --query ibd\n marketsurge-agent screen list --coach\n marketsurge-agent screen run 'IBD 50'"
)]
Screen(ScreenArgs),
#[command(
after_help = "Examples:\n marketsurge-agent tree coach\n marketsurge-agent tree nav"
)]
Tree(TreeArgs),
#[command(
after_help = "Examples:\n marketsurge-agent watchlist list --query ibd\n marketsurge-agent watchlist symbols 12345"
)]
Watchlist(WatchlistArgs),
#[command(
after_help = "Examples:\n marketsurge-agent completions zsh > _marketsurge-agent\n marketsurge-agent completions bash > marketsurge-agent.bash"
)]
Completions(CompletionsArgs),
}
#[derive(Debug, Args)]
pub struct ChartArgs {
#[arg(long)]
pub weekly: bool,
#[command(flatten)]
pub symbols: SymbolsArgs,
}
#[derive(Debug, Args)]
pub struct IndustryArgs {
#[command(subcommand)]
pub command: IndustryCommand,
}
#[derive(Debug, Args)]
pub struct OwnershipArgs {
#[command(subcommand)]
pub command: OwnershipCommand,
}
#[derive(Debug, Args)]
pub struct ScreenArgs {
#[command(subcommand)]
pub command: ScreenCommand,
}
#[derive(Debug, Args)]
pub struct TreeArgs {
#[command(subcommand)]
pub command: TreeCommand,
}
#[derive(Debug, Args)]
pub struct WatchlistArgs {
#[command(subcommand)]
pub command: WatchlistCommand,
}
#[derive(Debug, Args)]
pub struct CompletionsArgs {
pub shell: Shell,
}
#[derive(Debug, Args)]
pub struct SymbolsArgs {
#[arg(required = true)]
pub symbols: Vec<String>,
}
#[cfg(test)]
mod tests {
use clap::{CommandFactory, Parser};
use super::{Cli, Commands};
#[test]
fn command_tree_is_valid() {
Cli::command().debug_assert();
}
#[test]
fn fields_can_precede_subcommands() {
let cli = Cli::parse_from([
"marketsurge-agent",
"--fields",
"symbol,rs_rating",
"ratings",
"AAPL",
]);
assert_eq!(cli.fields, vec!["symbol", "rs_rating"]);
assert!(matches!(cli.command, Commands::Ratings(_)));
}
#[test]
fn fields_can_follow_nested_subcommands() {
let cli = Cli::parse_from([
"marketsurge-agent",
"ownership",
"summary",
"--fields",
"symbol,num_funds_held",
"AAPL",
]);
assert_eq!(cli.fields, vec!["symbol", "num_funds_held"]);
assert!(matches!(cli.command, Commands::Ownership(_)));
}
#[test]
fn json_objects_is_not_accepted() {
let result =
Cli::try_parse_from(["marketsurge-agent", "--json-objects", "ratings", "AAPL"]);
assert!(result.is_err());
}
}