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