mlb_api/endpoints/divisions/
mod.rs1use 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}