mlb_api/requests/
sport.rs1use crate::Copyright;
4use crate::request::RequestURL;
5use bon::Builder;
6use serde::Deserialize;
7use std::fmt::{Display, Formatter};
8use std::marker::PhantomData;
9use crate::cache::{Requestable};
10#[cfg(feature = "cache")]
11use crate::{rwlock_const_new, RwLock, cache::CacheTable};
12use crate::hydrations::Hydrations;
13
14#[derive(Debug, Deserialize, PartialEq, Clone)]
16#[serde(rename_all = "camelCase", bound = "H: SportsHydrations")]
17pub struct SportsResponse<H: SportsHydrations> {
18 pub copyright: Copyright,
19 pub sports: Vec<Sport<H>>,
20}
21
22id!(#[doc = "A [`u32`] representing the ID of the sport; `SportId::MLB = 1`"] SportId { id: u32 });
23
24impl SportId {
25 pub const MLB: Self = Self::new(1);
26}
27
28impl Default for SportId {
29 fn default() -> Self {
30 Self::MLB
31 }
32}
33
34#[derive(Builder)]
36#[builder(derive(Into))]
37pub struct SportsRequest<H: SportsHydrations> {
38 #[builder(into)]
39 id: Option<SportId>,
40 #[builder(skip)]
41 _marker: PhantomData<H>,
42}
43
44impl<H: SportsHydrations, S: sports_request_builder::State + sports_request_builder::IsComplete> crate::request::RequestURLBuilderExt for SportsRequestBuilder<H, S> {
45 type Built = SportsRequest<H>;
46}
47
48impl<H: SportsHydrations> Display for SportsRequest<H> {
49 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
50 let hydrations = Some(H::hydration_text(&())).filter(|s| !s.is_empty());
51 write!(f, "http://statsapi.mlb.com/api/v1/sports{}", gen_params! { "sportId"?: self.id, "hydrate"?: hydrations })
52 }
53}
54
55impl<H: SportsHydrations> RequestURL for SportsRequest<H> {
56 type Response = SportsResponse<H>;
57}
58
59#[derive(Debug, Deserialize, Clone)]
72#[serde(rename_all = "camelCase", bound = "H: SportsHydrations")]
73pub struct Sport<H: SportsHydrations = ()> {
74 pub code: String,
75 pub name: String,
76 pub abbreviation: String,
77 #[serde(rename = "activeStatus")]
78 pub active: bool,
79 #[serde(flatten)]
80 pub id: SportId,
81 #[serde(flatten)]
82 pub extras: H,
83}
84
85impl<H: SportsHydrations> PartialEq for Sport<H> {
86 fn eq(&self, other: &Self) -> bool {
87 self.id == other.id
88 }
89}
90
91pub trait SportsHydrations: Hydrations<RequestData=()> {}
92
93impl SportsHydrations for () {}
94
95#[macro_export]
118macro_rules! sports_hydrations {
119 (@ inline_structs [$field:ident $(: $value:ty)? $(, $($t:tt)*)?] $vis:vis struct $name:ident { $($field_tt:tt)* }) => {
120 $crate::sports_hydrations! { @ inline_structs [$($($t)*)?] $vis struct $name { $($field_tt)* $field $(: $value)?, } }
121 };
122 (@ inline_structs [$(,)?] $vis:vis struct $name:ident { $($t:tt)* }) => {
123 $crate::sports_hydrations! { @ actual $vis struct $name { $($t)* } }
124 };
125 (@ actual $vis:vis struct $name:ident {
126 $(season $season_comma:tt)?
127 }) => {
128 #[derive(::core::fmt::Debug, ::serde::Deserialize, ::core::cmp::PartialEq, ::core::clone::Clone)]
129 #[serde(rename_all = "camelCase")]
130 $vis struct $name {
131 $(pub season_date_info: $crate::season::Season $season_comma)?
132 }
133
134 impl $crate::sport::SportsHydrations for $name {}
135
136 impl $crate::hydrations::Hydrations for $name {
137 type RequestData = ();
138
139 fn hydration_text((): &Self::RequestData) -> ::std::borrow::Cow<'static, str> {
140 ::std::borrow::Cow::Borrowed(::std::concat!(
141 $("season," $season_comma)?
142 ))
143 }
144 }
145 };
146 ($vis:vis struct $name:ident { $($t:tt)* }) => {
147 $crate::sports_hydrations! { @ inline_structs [$($t)*] $vis struct $name {} }
148 };
149}
150
151#[cfg(feature = "cache")]
152static CACHE: RwLock<CacheTable<Sport>> = rwlock_const_new(CacheTable::new());
153
154impl Requestable for Sport {
155 type Identifier = SportId;
156 type URL = SportsRequest<()>;
157
158 fn id(&self) -> &Self::Identifier {
159 &self.id
160 }
161
162 #[cfg(feature = "aggressive_cache")]
163 fn url_for_id(_id: &Self::Identifier) -> Self::URL {
164 SportsRequest::builder().build()
165 }
166
167 #[cfg(not(feature = "aggressive_cache"))]
168 fn url_for_id(id: &Self::Identifier) -> Self::URL {
169 SportsRequest::builder().id(*id).build()
170 }
171
172 fn get_entries(response: <Self::URL as RequestURL>::Response) -> impl IntoIterator<Item=Self>
173 where
174 Self: Sized
175 {
176 response.sports
177 }
178
179 #[cfg(feature = "cache")]
180 fn get_cache_table() -> &'static RwLock<CacheTable<Self>>
181 where
182 Self: Sized
183 {
184 &CACHE
185 }
186}
187
188entrypoint!(SportId => Sport);
189entrypoint!(Sport.id => Sport);
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194 use crate::request::RequestURLBuilderExt;
195
196 #[tokio::test]
197 async fn parse_all_sports() {
198 let _result = SportsRequest::<()>::builder().build_and_get().await.unwrap();
199 }
200
201 #[tokio::test]
202 async fn parse_all_sports_with_hydrations() {
203 sports_hydrations! {
204 pub struct TestHydrations {
205 season
206 }
207 }
208
209 let _result = SportsRequest::<TestHydrations>::builder().build_and_get().await.unwrap();
210 }
211}