mlb_api/requests/
league.rs1use crate::sport::SportId;
2use crate::request::{RequestURL, RequestURLBuilderExt};
3use bon::Builder;
4use derive_more::{Deref, DerefMut};
5use itertools::Itertools;
6use serde::Deserialize;
7use std::fmt::{Display, Formatter};
8use crate::cache::Requestable;
9use crate::season::{Season, SeasonState};
10
11#[cfg(feature = "cache")]
12use crate::{rwlock_const_new, RwLock, cache::CacheTable};
13
14#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
15#[serde(rename_all = "camelCase")]
16pub struct LeagueResponse {
17 pub copyright: String,
18 pub leagues: Vec<League>,
19}
20
21#[derive(Debug, Deserialize, Clone, Hash)]
22#[serde(rename_all = "camelCase")]
23pub struct NamedLeague {
24 pub name: String,
25 #[serde(flatten)]
26 pub id: LeagueId,
27}
28
29#[allow(clippy::struct_excessive_bools, reason = "false positive")]
30#[derive(Debug, Deserialize, Deref, DerefMut, Clone)]
31#[serde(rename_all = "camelCase")]
32pub struct League {
33 pub abbreviation: String,
34 #[serde(rename = "nameShort")]
35 pub short_name: Option<String>,
36 #[serde(rename = "orgCode")]
37 pub code: String,
38 pub season_state: SeasonState,
39 #[serde(flatten, deserialize_with = "bad_league_season_schema_deserializer")]
40 #[serde(rename = "seasonDateInfo")]
41 pub season: Season,
42 #[serde(default)]
43 pub has_split_season: bool,
44 pub num_games: u8,
45 pub has_playoff_points: Option<bool>,
46 pub num_teams: u8,
47 pub num_wildcard_teams: Option<u8>,
48 #[serde(rename = "conferencesInUse")]
49 pub has_conferences: bool,
50 #[serde(rename = "divisionsInUse")]
51 pub has_divisions: bool,
52 pub sport: Option<SportId>,
53 pub active: bool,
54
55 #[deref]
56 #[deref_mut]
57 #[serde(flatten)]
58 inner: NamedLeague,
59}
60
61#[derive(Deserialize)]
63struct BadLeagueSeasonSchema {
64 #[serde(rename = "hasWildCard")]
65 has_wildcard: bool,
66 #[serde(rename = "seasonDateInfo")]
67 rest: Season,
68}
69
70fn bad_league_season_schema_deserializer<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Season, D::Error> {
71 let BadLeagueSeasonSchema { has_wildcard, mut rest } = BadLeagueSeasonSchema::deserialize(deserializer)?;
72 rest.has_wildcard = has_wildcard;
73 Ok(rest)
74}
75
76id!(LeagueId { id: u32 });
77id_only_eq_impl!(League, id);
78id_only_eq_impl!(NamedLeague, id);
79
80impl NamedLeague {
81 #[must_use]
82 pub(crate) fn unknown_league() -> Self {
83 Self {
84 name: "null".to_owned(),
85 id: LeagueId::new(0),
86 }
87 }
88
89 #[must_use]
90 pub fn is_unknown(&self) -> bool {
91 *self.id == 0
92 }
93}
94
95#[derive(Builder)]
96#[builder(derive(Into))]
97pub struct LeaguesRequest {
98 #[builder(into)]
99 sport_id: Option<SportId>,
100 #[builder(setters(vis = "", name = league_ids_internal))]
101 league_ids: Option<Vec<LeagueId>>,
102}
103
104impl<S: leagues_request_builder::State + leagues_request_builder::IsComplete> RequestURLBuilderExt for LeaguesRequestBuilder<S> {
105 type Built = LeaguesRequest;
106}
107
108impl<S: leagues_request_builder::State> LeaguesRequestBuilder<S> {
109 #[allow(dead_code)]
110 pub fn league_ids<T: Into<LeagueId>>(self, league_ids: Vec<T>) -> LeaguesRequestBuilder<leagues_request_builder::SetLeagueIds<S>> where S::LeagueIds: leagues_request_builder::IsUnset {
111 self.league_ids_internal(league_ids.into_iter().map(T::into).collect::<Vec<_>>())
112 }
113}
114
115impl Display for LeaguesRequest {
116 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
117 write!(f, "http://statsapi.mlb.com/api/v1/leagues{}", gen_params! {
118 "sportId"?: self.sport_id,
119 "leagueIds"?: self.league_ids.as_ref().map(|ids| ids.iter().copied().join(",")),
120 })
121 }
122}
123
124impl RequestURL for LeaguesRequest {
125 type Response = LeagueResponse;
126}
127
128#[cfg(feature = "cache")]
129static CACHE: RwLock<CacheTable<League>> = rwlock_const_new(CacheTable::new());
130
131impl Requestable for League {
132 type Identifier = LeagueId;
133 type URL = LeaguesRequest;
134
135 fn id(&self) -> &Self::Identifier {
136 &self.id
137 }
138
139 #[cfg(feature = "aggressive_cache")]
140 fn url_for_id(_id: &Self::Identifier) -> Self::URL {
141 LeaguesRequest::builder().build()
142 }
143
144 #[cfg(not(feature = "aggressive_cache"))]
145 fn url_for_id(id: &Self::Identifier) -> Self::URL {
146 LeaguesRequest::builder().league_ids_internal(vec![*id]).build()
147 }
148
149 fn get_entries(response: <Self::URL as RequestURL>::Response) -> impl IntoIterator<Item=Self>
150 where
151 Self: Sized
152 {
153 response.leagues
154 }
155
156 #[cfg(feature = "cache")]
157 fn get_cache_table() -> &'static RwLock<CacheTable<Self>>
158 where
159 Self: Sized
160 {
161 &CACHE
162 }
163}
164
165entrypoint!(LeagueId => League);
166entrypoint!(NamedLeague.id => League);
167entrypoint!(League.id => League);