mlb_api/endpoints/divisions/
mod.rs

1use crate::endpoints::StatsAPIUrl;
2use crate::endpoints::league::{IdentifiableLeague, LeagueId};
3use crate::endpoints::sports::{IdentifiableSport, SportId};
4use crate::{gen_params, rwlock_const_new, RwLock};
5use crate::types::Copyright;
6use derive_more::{Deref, DerefMut, Display, From};
7use serde::Deserialize;
8use serde_with::DisplayFromStr;
9use serde_with::serde_as;
10use std::fmt::{Display, Formatter};
11use std::ops::{Deref, DerefMut};
12use strum::EnumTryAs;
13use crate::cache::{EndpointEntryCache, HydratedCacheTable};
14
15#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
16#[serde(rename_all = "camelCase")]
17pub struct DivisionsResponse {
18	pub copyright: Copyright,
19	pub divisions: Vec<Division>,
20}
21#[derive(Debug, Deserialize, PartialEq, Eq, Copy, Clone)]
22#[serde(rename_all = "camelCase")]
23pub struct IdentifiableDivision {
24	pub id: DivisionId,
25}
26
27#[derive(Debug, Deserialize, Deref, DerefMut, PartialEq, Eq, Clone)]
28#[serde(rename_all = "camelCase")]
29pub struct NamedDivision {
30	pub name: String,
31
32	#[deref]
33	#[deref_mut]
34	#[serde(flatten)]
35	inner: IdentifiableDivision,
36}
37
38#[serde_as]
39#[derive(Debug, Deserialize, Deref, DerefMut, PartialEq, Eq, Clone)]
40#[serde(rename_all = "camelCase")]
41pub struct HydratedDivision {
42	#[serde(rename = "nameShort")]
43	pub short_name: String,
44	#[serde_as(as = "DisplayFromStr")]
45	pub season: u16,
46	pub abbreviation: String,
47	pub league: IdentifiableLeague,
48	pub sport: IdentifiableSport,
49	pub has_wildcard: bool,
50	pub num_playoff_teams: Option<u8>,
51	pub active: bool,
52
53	#[deref]
54	#[deref_mut]
55	#[serde(flatten)]
56	inner: NamedDivision,
57}
58
59#[derive(Debug, Deserialize, Eq, Clone, From, EnumTryAs)]
60#[serde(untagged)]
61pub enum Division {
62	Hydrated(HydratedDivision),
63	Named(NamedDivision),
64	Identifiable(IdentifiableDivision),
65}
66
67impl PartialEq for Division {
68	fn eq(&self, other: &Self) -> bool {
69		self.id == other.id
70	}
71}
72
73impl Deref for Division {
74	type Target = IdentifiableDivision;
75
76	fn deref(&self) -> &Self::Target {
77		match self {
78			Self::Hydrated(inner) => inner,
79			Self::Named(inner) => inner,
80			Self::Identifiable(inner) => inner,
81		}
82	}
83}
84
85impl DerefMut for Division {
86	fn deref_mut(&mut self) -> &mut Self::Target {
87		match self {
88			Self::Hydrated(inner) => inner,
89			Self::Named(inner) => inner,
90			Self::Identifiable(inner) => inner,
91		}
92	}
93}
94
95#[repr(transparent)]
96#[derive(Debug, Deserialize, Deref, Display, PartialEq, Eq, Copy, Clone, Hash)]
97pub struct DivisionId(u32);
98
99#[derive(Default)]
100pub struct DivisionsEndpointUrl {
101	pub division_id: Option<DivisionId>,
102	pub league_id: Option<LeagueId>,
103	pub sport_id: Option<SportId>,
104	pub season: Option<u16>,
105}
106
107impl Display for DivisionsEndpointUrl {
108	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
109		write!(
110			f,
111			"http://statsapi.mlb.com/api/v1/divisions{}",
112			gen_params! { "divisionId"?: self.division_id, "leagueId"?: self.league_id, "sportId"?: self.sport_id, "season"?: self.season }
113		)
114	}
115}
116
117impl StatsAPIUrl for DivisionsEndpointUrl {
118	type Response = DivisionsResponse;
119}
120
121static CACHE: RwLock<HydratedCacheTable<Division>> = rwlock_const_new(HydratedCacheTable::new());
122
123impl EndpointEntryCache for Division {
124	type HydratedVariant = HydratedDivision;
125	type Identifier = DivisionId;
126	type URL = DivisionsEndpointUrl;
127
128	fn into_hydrated_variant(self) -> Option<Self::HydratedVariant> {
129		self.try_as_hydrated()
130	}
131
132	fn id(&self) -> &Self::Identifier {
133		&self.id
134	}
135
136	fn url_for_id(id: &Self::Identifier) -> Self::URL {
137		DivisionsEndpointUrl {
138			division_id: Some(id.clone()),
139			league_id: None,
140			sport_id: None,
141			season: None,
142		}
143	}
144
145	fn get_entries(response: <Self::URL as StatsAPIUrl>::Response) -> impl IntoIterator<Item=Self>
146	where
147		Self: Sized
148	{
149		response.divisions
150	}
151
152	fn get_hydrated_cache_table() -> &'static RwLock<HydratedCacheTable<Self>>
153	where
154		Self: Sized
155	{
156		&CACHE
157	}
158}
159
160#[cfg(test)]
161mod tests {
162	use crate::endpoints::StatsAPIUrl;
163	use crate::endpoints::divisions::DivisionsEndpointUrl;
164
165	#[tokio::test]
166	async fn all_divisions_this_season() {
167		let _response = DivisionsEndpointUrl::default().get().await.unwrap();
168	}
169}