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