1use rmcp::schemars;
4use serde::Deserialize;
5
6use crate::tool_enums;
7
8use super::*;
9
10mod basic;
11mod f10;
12mod misc;
13mod price_reminder;
14mod reference;
15mod screen_unusual;
16mod shareholders;
17
18pub use basic::{
19 KLineReq, MarketStateReq, OptionExerciseProbabilityReq, OptionVolatilityReq, OrderBookReq,
20 PlateListReq, PlateStocksReq, ShortInfoReq, SymbolListReq, SymbolReq, TickerReq,
21 TopTenBuySellBrokersReq,
22};
23pub use f10::{
24 CompanyExecutiveBackgroundReq, CompanyOperationalEfficiencyReq, FinancialCalendarReq,
25 FinancialsEarningsPriceHistoryReq, FinancialsEarningsPriceMoveReq,
26 FinancialsRevenueBreakdownReq, FinancialsStatementsReq, ResearchAnalystConsensusReq,
27 ResearchMorningstarReportReq, ResearchRatingSummaryReq, TargetFinancialCalendarReq,
28 ValuationDetailReq, ValuationPlateStockListReq,
29};
30pub use misc::{
31 BizGroupReq, BondSymbolReq, CodeChangeReq, HistoryKlQuotaReq, HoldingChangeReq,
32 ModifyUserSecurityReq, QuoteRightsReq, StockFilterReq, SuspendReq, TickerStatisticDetailReq,
33 TickerStatisticReq, TradingDaysReq, UserSecurityGroupReq, UserSecurityReq,
34};
35pub use price_reminder::{GetPriceReminderReq, OptionExpirationDateReq, SetPriceReminderReq};
36pub use reference::{
37 FutureInfoReq, HistoryKLineReq, IpoCalendarReq, IpoListReq, OptionChainReq, ReferenceReq,
38 WarrantReq,
39};
40pub use screen_unusual::{
41 DerivativeUnusualReq, FinancialUnusualReq, OptionScreenReq, StockScreenReq,
42 TechnicalUnusualReq, WarrantScreenReq,
43};
44pub use shareholders::{
45 InsiderHolderListReq, InsiderTradeListReq, ShareholdersHolderDetailReq,
46 ShareholdersHoldingChangesReq, ShareholdersInstitutionalReq, ShareholdersOverviewReq,
47};
48
49fn validate_optional_i32_range(
50 tool: &str,
51 field: &str,
52 value: Option<i32>,
53 min: i32,
54 max: i32,
55) -> Result<(), String> {
56 if let Some(value) = value
57 && !(min..=max).contains(&value)
58 {
59 return Err(format!(
60 "{field} must be in {min}..={max} for {tool}, got {value}"
61 ));
62 }
63 Ok(())
64}
65
66fn validate_optional_f64_min_max(
67 tool: &str,
68 field: &str,
69 min: Option<f64>,
70 max: Option<f64>,
71) -> Result<(), String> {
72 if let (Some(min), Some(max)) = (min, max)
73 && min > max
74 {
75 return Err(format!("{field}_min must be <= {field}_max for {tool}"));
76 }
77 Ok(())
78}
79
80fn deser_hk_hkfuture_us_cn_market_as_i32<'de, D>(deserializer: D) -> Result<i32, D::Error>
81where
82 D: serde::Deserializer<'de>,
83{
84 #[derive(Deserialize)]
85 #[serde(untagged)]
86 enum IntOrStr {
87 Int(i32),
88 Str(String),
89 }
90
91 match IntOrStr::deserialize(deserializer)? {
92 IntOrStr::Int(value) => Ok(value),
93 IntOrStr::Str(value) => {
94 let trimmed = value.trim();
95 if let Ok(value) = trimmed.parse::<i32>() {
96 return Ok(value);
97 }
98 match trimmed.to_ascii_uppercase().as_str() {
99 "HK" => Ok(1),
100 "HK_FUTURE" | "HKFUTURE" => Ok(2),
101 "US" => Ok(11),
102 "SH" | "CN" => Ok(21),
103 "SZ" => Ok(22),
104 "SG" => Ok(31),
105 "JP" => Ok(41),
106 "MY" => Ok(61),
107 _ => Err(serde::de::Error::custom(format!(
108 "unknown market {value:?}: valid = HK/HK_FUTURE/US/SH/SZ/SG/JP/MY or integer 1/2/11/21/22/31/41/61"
109 ))),
110 }
111 }
112 }
113}