mlb_api/endpoints/teams/team/leaders/
mod.rs

1use crate::endpoints::league::League;
2use crate::endpoints::person::Person;
3use crate::endpoints::sports::Sport;
4use crate::endpoints::teams::team::{Team, TeamId};
5use crate::endpoints::{BaseballStat, BaseballStatId, GameType, IdentifiableBaseballStat, StatGroup, StatsAPIUrl};
6use crate::gen_params;
7use crate::types::{Copyright, IntegerOrFloat, PlayerPool};
8use itertools::Itertools;
9use serde::Deserialize;
10use serde_with::DisplayFromStr;
11use serde_with::serde_as;
12use std::fmt::{Display, Formatter};
13use std::str::FromStr;
14
15#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
16#[serde(rename_all = "camelCase")]
17pub struct TeamStatLeadersResponse {
18	pub copyright: Copyright,
19	pub team_leaders: Vec<TeamStatLeaders>,
20}
21
22#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
23#[serde(try_from = "__TeamStatLeadersStruct")]
24pub struct TeamStatLeaders {
25	pub category: BaseballStat,
26	pub season: u16,
27	pub game_type: GameType,
28	pub leaders: Vec<TeamStatLeader>,
29	pub stat_group: StatGroup,
30	pub total_splits: u32,
31}
32
33#[serde_as]
34#[derive(Deserialize)]
35#[serde(rename_all = "camelCase")]
36struct __TeamStatLeadersStruct {
37	leader_category: String,
38	#[serde_as(as = "DisplayFromStr")]
39	season: u16,
40	game_type: GameType,
41	leaders: Vec<TeamStatLeader>,
42	stat_group: String,
43	total_splits: u32,
44}
45
46impl TryFrom<__TeamStatLeadersStruct> for TeamStatLeaders {
47	type Error = <StatGroup as FromStr>::Err;
48
49	fn try_from(value: __TeamStatLeadersStruct) -> Result<Self, Self::Error> {
50		Ok(TeamStatLeaders {
51			category: BaseballStat::Identifiable(IdentifiableBaseballStat {
52				id: BaseballStatId::new(value.leader_category),
53			}),
54			season: value.season,
55			game_type: value.game_type,
56			leaders: value.leaders,
57			stat_group: StatGroup::from_str(&value.stat_group)?,
58			total_splits: value.total_splits,
59		})
60	}
61}
62
63#[serde_as]
64#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
65#[serde(rename_all = "camelCase")]
66pub struct TeamStatLeader {
67	pub rank: u32,
68	pub value: IntegerOrFloat,
69	pub team: Team,
70	pub league: League,
71	pub person: Person,
72	pub sport: Sport,
73	#[serde_as(as = "DisplayFromStr")]
74	pub season: u16,
75}
76
77/// Represents the stat leaders per team
78pub struct TeamStatLeadersEndpointUrl {
79	pub team_id: TeamId,
80	pub stats: Vec<BaseballStat>,
81	pub season: Option<u16>,
82	pub pool: PlayerPool,
83
84	/// [`None`] represents matching for all [`GameType`]s.
85	pub game_types: Option<Vec<GameType>>,
86}
87
88impl Display for TeamStatLeadersEndpointUrl {
89	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
90		write!(
91			f,
92			"http://statsapi.mlb.com/api/v1/teams/{}/leaders{params}",
93			self.team_id,
94			params = gen_params! {
95				"leaderCategories": self.stats.iter().map(|stat| &stat.id).join(","),
96				"season"?: self.season,
97				"pool": self.pool,
98				"game_types"?: self.game_types.as_ref().map(|x| x.iter().join(",")),
99			}
100		)
101	}
102}
103
104impl StatsAPIUrl for TeamStatLeadersEndpointUrl {
105	type Response = TeamStatLeadersResponse;
106}
107
108#[cfg(test)]
109mod tests {
110	use crate::endpoints::{BaseballStat, StatsAPIUrl};
111	use crate::endpoints::meta::MetaEndpointUrl;
112	use crate::endpoints::sports::SportId;
113	use crate::endpoints::teams::TeamsEndpointUrl;
114	use crate::endpoints::teams::team::leaders::TeamStatLeadersEndpointUrl;
115
116	#[tokio::test]
117	async fn test_all_mlb_teams_all_stats() {
118		let all_categories = MetaEndpointUrl::<BaseballStat>::new().get().await.unwrap().entries;
119
120		for team in (TeamsEndpointUrl { sport_id: Some(SportId::MLB), season: None }).get().await.unwrap().teams {
121			let _all_stats = TeamStatLeadersEndpointUrl {
122				team_id: team.id,
123				stats: all_categories.clone(),
124				season: None,
125				pool: Default::default(),
126				game_types: None,
127			}
128			.get()
129			.await
130			.unwrap();
131		}
132	}
133}