mlb_api/requests/team/
uniforms.rs1use crate::cache::{Requestable, RequestableEntrypoint};
4use crate::season::SeasonId;
5use crate::team::TeamId;
6use crate::Copyright;
7use crate::request::RequestURL;
8use bon::Builder;
9use itertools::Itertools;
10use serde::Deserialize;
11use std::fmt::{Display, Formatter};
12
13#[cfg(feature = "cache")]
14use crate::{rwlock_const_new, RwLock, cache::CacheTable};
15
16#[derive(Debug, Deserialize, PartialEq, Clone)]
20pub struct UniformsResponse {
21 pub copyright: Copyright,
22 #[serde(rename = "uniforms")] pub teams: Vec<TeamUniformAssets>,
23}
24
25id!(UniformAssetId { uniformAssetId: u32 });
26id!(UniformAssetCategoryId { uniformAssetTypeId: u32 });
27
28#[derive(Debug, Deserialize, PartialEq, Clone)]
30#[serde(rename_all = "camelCase")]
31pub struct TeamUniformAssets {
32 pub team_id: TeamId,
33 pub uniform_assets: Vec<UniformAsset>,
34}
35
36#[derive(Debug, Deserialize, Clone)]
38pub struct UniformAsset {
39 #[serde(rename = "uniformAssetText")] pub name: String,
40 #[serde(rename = "uniformAssetType")] pub category: UniformAssetCategory,
41 #[serde(rename = "uniformAssetCode")] pub code: String,
42 #[serde(flatten)]
43 pub id: UniformAssetId,
44}
45
46#[derive(Debug, Deserialize, Clone)]
48pub struct UniformAssetCategory {
49 #[serde(rename = "uniformAssetTypeText")] pub name: String,
50 #[serde(rename = "uniformAssetTypeCode")] pub code: String,
51 #[serde(rename = "uniformAssetTypeDesc")] pub description: String,
52 #[serde(rename = "uniformAssetTypeId")] pub id: UniformAssetCategoryId,
53}
54
55id_only_eq_impl!(UniformAsset, id);
56id_only_eq_impl!(UniformAssetCategory, id);
57
58#[derive(Builder)]
60#[builder(derive(Into))]
61pub struct UniformsRequest {
62 teams: Vec<TeamId>,
63 #[builder(into)]
64 season: Option<SeasonId>,
65}
66
67impl<S: uniforms_request_builder::State + uniforms_request_builder::IsComplete> crate::request::RequestURLBuilderExt for UniformsRequestBuilder<S> {
68 type Built = UniformsRequest;
69}
70
71impl Display for UniformsRequest {
72 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
73 write!(f, "http://statsapi.mlb.com/api/v1/uniforms/team{}", gen_params! { "teamIds": self.teams.iter().copied().join(","), "season"?: self.season })
74 }
75}
76
77impl RequestURL for UniformsRequest {
78 type Response = UniformsResponse;
79}
80
81#[cfg(feature = "cache")]
82static CACHE: RwLock<CacheTable<UniformAsset>> = rwlock_const_new(CacheTable::new());
83
84impl Requestable for UniformAsset {
85 type Identifier = String;
86 type URL = UniformsRequest;
87
88 fn id(&self) -> &Self::Identifier {
89 &self.code
90 }
91
92 fn url_for_id(id: &Self::Identifier) -> Self::URL {
93 UniformsRequest::builder()
94 .teams(vec![TeamId::new(id.split_once('_').and_then(|(num, _)| num.parse().ok()).unwrap_or(0))])
95 .build()
96 }
97
98 fn get_entries(response: <Self::URL as RequestURL>::Response) -> impl IntoIterator<Item=Self>
99 where
100 Self: Sized
101 {
102 response.teams.into_iter().flat_map(|team| team.uniform_assets)
103 }
104
105 #[cfg(feature = "cache")]
106 fn get_cache_table() -> &'static RwLock<CacheTable<Self>>
107 where
108 Self: Sized
109 {
110 &CACHE
111 }
112}
113
114impl RequestableEntrypoint for UniformAsset {
115 type Complete = Self;
116
117 fn id(&self) -> &<<Self as RequestableEntrypoint>::Complete as Requestable>::Identifier {
118 &self.code
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use crate::team::uniforms::UniformsRequest;
125 use crate::team::TeamsRequest;
126 use crate::request::RequestURLBuilderExt;
127
128 #[tokio::test]
129 async fn parse_all_mlb_teams_this_season() {
130 let mlb_teams = TeamsRequest::mlb_teams().build_and_get().await.unwrap();
131 let team_ids = mlb_teams.teams.into_iter().map(|team| team.id).collect::<Vec<_>>();
132 let _ = UniformsRequest::builder().teams(team_ids).build_and_get().await.unwrap();
133 }
134}