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