mlb_api/endpoints/meta/kinds/
positions.rs1use crate::endpoints::meta::{MetaEndpointUrl, MetaKind};
2use derive_more::{Deref, DerefMut, Display, From};
3use serde::Deserialize;
4use std::ops::{Deref, DerefMut};
5use strum::EnumTryAs;
6use crate::cache::{EndpointEntryCache, HydratedCacheTable};
7use crate::{rwlock_const_new, RwLock};
8use crate::endpoints::StatsAPIUrl;
9
10#[derive(Debug, Deserialize, Deref, DerefMut, PartialEq, Eq, Clone)]
11#[serde(rename_all = "camelCase")]
12pub struct HydratedPosition {
13 pub short_name: String,
14 pub full_name: String,
15 pub formal_name: String,
16 #[serde(rename = "pitcher")]
17 pub is_pitcher: bool,
18 #[serde(rename = "gamePosition")]
19 pub is_game_position: bool,
20 #[serde(rename = "fielder")]
21 pub is_fielder: bool,
22 #[serde(rename = "outfield")]
23 pub is_outfield: bool,
24
25 #[deref]
26 #[deref_mut]
27 #[serde(flatten)]
28 inner: NamedPosition,
29}
30
31#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
32#[serde(rename_all = "camelCase")]
33pub struct NamedPosition {
34 pub code: PositionCode,
35 #[serde(alias = "displayName")]
36 pub name: String,
37 #[serde(rename = "type")]
38 pub r#type: String,
39 #[serde(alias = "abbrev")]
40 pub abbreviation: String,
41}
42
43#[repr(transparent)]
44#[derive(Debug, Deserialize, Deref, Display, PartialEq, Eq, Clone, Hash)]
45pub struct PositionCode(String);
46
47#[derive(Debug, Deserialize, Eq, Clone, From, EnumTryAs)]
48#[serde(untagged)]
49pub enum Position {
50 Hydrated(HydratedPosition),
51 Named(NamedPosition),
52}
53
54impl PartialEq for Position {
55 fn eq(&self, other: &Self) -> bool {
56 self.code == other.code
57 }
58}
59
60impl Deref for Position {
61 type Target = NamedPosition;
62
63 fn deref(&self) -> &Self::Target {
64 match self {
65 Self::Hydrated(inner) => inner,
66 Self::Named(inner) => inner,
67 }
68 }
69}
70
71impl DerefMut for Position {
72 fn deref_mut(&mut self) -> &mut Self::Target {
73 match self {
74 Self::Hydrated(inner) => inner,
75 Self::Named(inner) => inner,
76 }
77 }
78}
79
80impl MetaKind for Position {
81 const ENDPOINT_NAME: &'static str = "positions";
82}
83
84static CACHE: RwLock<HydratedCacheTable<Position>> = rwlock_const_new(HydratedCacheTable::new());
85
86impl EndpointEntryCache for Position {
87 type HydratedVariant = HydratedPosition;
88 type Identifier = PositionCode;
89 type URL = MetaEndpointUrl<Self>;
90
91 fn into_hydrated_variant(self) -> Option<Self::HydratedVariant> {
92 self.try_as_hydrated()
93 }
94
95 fn id(&self) -> &Self::Identifier {
96 &self.code
97 }
98
99 fn url_for_id(_id: &Self::Identifier) -> Self::URL {
100 MetaEndpointUrl::new()
101 }
102
103 fn get_entries(response: <Self::URL as StatsAPIUrl>::Response) -> impl IntoIterator<Item=Self>
104 where
105 Self: Sized
106 {
107 response.entries
108 }
109
110 fn get_hydrated_cache_table() -> &'static RwLock<HydratedCacheTable<Self>>
111 where
112 Self: Sized
113 {
114 &CACHE
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use crate::endpoints::StatsAPIUrl;
121 use crate::endpoints::meta::MetaEndpointUrl;
122
123 #[tokio::test]
124 async fn parse_meta() {
125 let _response = MetaEndpointUrl::<super::Position>::new().get().await.unwrap();
126 }
127}