mlb_api/requests/stats/
leaders.rs1use crate::league::NamedLeague;
4use crate::sport::SportId;
5use crate::{Copyright, IntegerOrFloatStat, PlayerPool, MLB_API_DATE_FORMAT};
6use bon::Builder;
7use chrono::NaiveDate;
8use itertools::Itertools;
9use serde::Deserialize;
10use std::fmt::{Display, Formatter};
11use std::str::FromStr;
12use crate::meta::BaseballStatId;
13use crate::meta::GameType;
14use crate::person::NamedPerson;
15use crate::request::RequestURL;
16use crate::season::SeasonId;
17use crate::meta::StatGroup;
18use crate::meta::StatType;
19use crate::team::NamedTeam;
20
21#[derive(Debug, Deserialize, PartialEq, Clone)]
23#[serde(rename_all = "camelCase")]
24pub struct StatLeadersResponse {
25 pub copyright: Copyright,
26 pub league_leaders: Vec<StatLeaders>,
27}
28
29#[derive(Debug, Deserialize, PartialEq, Clone)]
31#[serde(try_from = "__StatLeadersStruct")]
32pub struct StatLeaders {
33 pub category: BaseballStatId,
34 pub game_type: GameType,
35 pub leaders: Vec<StatLeader>,
36 pub stat_group: StatGroup,
37 pub total_splits: u32,
38}
39
40#[derive(Deserialize)]
41#[serde(rename_all = "camelCase")]
42#[doc(hidden)]
43struct __StatLeadersStruct {
44 leader_category: String,
45 game_type: GameType,
46 #[serde(default)]
47 leaders: Vec<StatLeader>,
48 stat_group: String,
49 total_splits: u32,
50}
51
52impl TryFrom<__StatLeadersStruct> for StatLeaders {
53 type Error = <StatGroup as FromStr>::Err;
54
55 fn try_from(value: __StatLeadersStruct) -> Result<Self, Self::Error> {
56 Ok(Self {
57 category: value.leader_category.into(),
58 game_type: value.game_type,
59 leaders: value.leaders,
60 stat_group: StatGroup::from_str(&value.stat_group)?,
61 total_splits: value.total_splits,
62 })
63 }
64}
65
66#[derive(Debug, Deserialize, PartialEq, Clone)]
67#[serde(rename_all = "camelCase")]
68pub struct StatLeader {
69 pub rank: u32,
70 pub value: IntegerOrFloatStat,
71 #[serde(default = "NamedTeam::unknown_team")]
72 pub team: NamedTeam,
73 #[serde(default = "NamedLeague::unknown_league")]
74 pub league: NamedLeague,
75 pub person: NamedPerson,
76 pub sport: SportId,
77 pub season: SeasonId,
78}
79
80#[derive(Builder)]
82#[builder(derive(Into))]
83pub struct StatLeadersRequest {
84 stats: Vec<BaseballStatId>,
85 stat_group: Option<StatGroup>,
86 #[builder(into)]
87 season: Option<SeasonId>,
88 #[builder(into, default)]
89 sport_id: SportId,
90 stat_types: Vec<StatType>,
91 start_date: Option<NaiveDate>,
92 end_date: Option<NaiveDate>,
93 pool: PlayerPool,
94
95 days_back: Option<u32>,
97
98 limit: Option<u16>,
101 offset: Option<u16>,
103
104 game_types: Option<Vec<GameType>>,
106}
107
108impl<S: stat_leaders_request_builder::State + stat_leaders_request_builder::IsComplete> crate::request::RequestURLBuilderExt for StatLeadersRequestBuilder<S> {
109 type Built = StatLeadersRequest;
110}
111
112impl Display for StatLeadersRequest {
113 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
114 write!(
115 f,
116 "http://statsapi.mlb.com/api/v1/stats/leaders{params}",
117 params = gen_params! {
118 "leaderCategories": self.stats.iter().join(","),
119 "statGroup"?: self.stat_group,
120 "season"?: self.season,
121 "sportId": self.sport_id,
122 "stats": self.stat_types.iter().join(","),
123 "startDate"?: self.start_date.map(|x| x.format(MLB_API_DATE_FORMAT)),
124 "endDate"?: self.end_date.map(|x| x.format(MLB_API_DATE_FORMAT)),
125 "playerPool": self.pool,
126 "daysBack"?: self.days_back,
127 "limit"?: self.limit,
128 "offset"?: self.offset,
129 "gameTypes"?: self.game_types.as_ref().map(|x| x.iter().join(",")),
130 }
131 )
132 }
133}
134
135impl RequestURL for StatLeadersRequest {
136 type Response = StatLeadersResponse;
137}
138
139#[cfg(test)]
140mod tests {
141 use crate::meta::MetaRequest;
142 use crate::stats::leaders::StatLeadersRequest;
143 use crate::PlayerPool;
144 use crate::meta::BaseballStat;
145 use crate::meta::GameType;
146 use crate::request::{RequestURL, RequestURLBuilderExt};
147
148 #[tokio::test]
149 async fn test_stat_leaders() {
150 let all_stats = MetaRequest::<BaseballStat>::new().get().await.unwrap().entries.into_iter().map(|x| x.id.clone()).collect::<Vec<_>>();
151 let all_game_types = MetaRequest::<GameType>::new().get().await.unwrap().entries;
152
153 let _ = StatLeadersRequest::builder().stats(all_stats).pool(PlayerPool::All).limit(100).game_types(all_game_types).stat_types(vec![]).build_and_get().await.unwrap();
154 }
155}