mlb_api/endpoints/meta/kinds/
metrics.rs1use crate::endpoints::meta::{MetaEndpointUrl, MetaKind};
2use crate::endpoints::stat_groups::StatGroup;
3use derive_more::{Deref, DerefMut, Display, From};
4use serde::Deserialize;
5use std::ops::{Deref, DerefMut};
6use strum::EnumTryAs;
7use crate::cache::{EndpointEntryCache, HydratedCacheTable};
8use crate::{rwlock_const_new, RwLock};
9use crate::endpoints::StatsAPIUrl;
10
11macro_rules! units {
12 ($($name:ident($func:path => $units:ty)),+ $(,)?) => {
13 #[derive(Debug, ::serde::Deserialize, Clone)]
14 #[serde(try_from = "__UnitStruct")]
15 pub enum Unit {
16 $($name($units),)+
17 Unknown(String),
18 }
19
20 impl PartialEq for Unit {
21 fn eq(&self, other: &Self) -> bool {
22 match (self, other) {
23 $((Self::$name(lhs), Self::$name(rhs)) => format!("{lhs:?}") == format!("{rhs:?}"),)+
25 (Self::Unknown(lhs), Self::Unknown(rhs)) => lhs == rhs,
26 _ => false,
27 }
28 }
29 }
30
31 impl Eq for Unit {}
32
33 #[derive(::serde::Deserialize)]
34 struct __UnitStruct(String);
35
36 impl TryFrom<__UnitStruct> for Unit {
37 type Error = ::uom::str::ParseQuantityError;
38
39 fn try_from(value: __UnitStruct) -> Result<Self, Self::Error> {
40 let __UnitStruct(inner) = value;
41
42 $(
43 for unit in $func() {
44 let abbreviation = unit.abbreviation();
45 if abbreviation.eq_ignore_ascii_case(&inner) {
46 return Ok(Self::$name(unit));
47 }
48 }
49 )+
50
51 Ok(Self::Unknown(inner))
52 }
53 }
54 };
55}
56
57units! {
58 AngularVelocity(uom::si::angular_velocity::units => uom::si::angular_velocity::Units),
59 Length(uom::si::length::units => uom::si::length::Units),
60 Velocity(uom::si::velocity::units => uom::si::velocity::Units),
61 Angle(uom::si::angle::units => uom::si::angle::Units),
62 Time(uom::si::time::units => uom::si::time::Units),
63}
64
65#[derive(Debug, Deserialize, Deref, DerefMut, PartialEq, Eq, Clone)]
66#[serde(rename_all = "camelCase")]
67pub struct HydratedMetric {
68 #[serde(deserialize_with = "crate::types::deserialize_comma_seperated_vec")]
69 pub group: Vec<StatGroup>,
70 pub unit: Unit,
71
72 #[deref]
73 #[deref_mut]
74 #[serde(flatten)]
75 inner: NamedMetric,
76}
77
78#[derive(Debug, Deserialize, Deref, DerefMut, PartialEq, Eq, Clone)]
79#[serde(rename_all = "camelCase")]
80pub struct NamedMetric {
81 pub name: String,
82
83 #[deref]
84 #[deref_mut]
85 #[serde(flatten)]
86 inner: IdentifiableMetric,
87}
88
89#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
90#[serde(rename_all = "camelCase")]
91pub struct IdentifiableMetric {
92 #[serde(rename = "metricId")]
93 pub id: MetricId,
94}
95
96#[repr(transparent)]
97#[derive(Debug, Deserialize, Deref, Display, PartialEq, Eq, Copy, Clone, Hash)]
98pub struct MetricId(u32);
99
100#[derive(Debug, Deserialize, Eq, Clone, From, EnumTryAs)]
101#[serde(untagged)]
102pub enum Metric {
103 Hydrated(HydratedMetric),
104 Named(NamedMetric),
105 Identifiable(IdentifiableMetric),
106}
107
108impl PartialEq for Metric {
109 fn eq(&self, other: &Self) -> bool {
110 self.id == other.id
111 }
112}
113
114impl Deref for Metric {
115 type Target = IdentifiableMetric;
116
117 fn deref(&self) -> &Self::Target {
118 match self {
119 Self::Hydrated(inner) => inner,
120 Self::Named(inner) => inner,
121 Self::Identifiable(inner) => inner,
122 }
123 }
124}
125
126impl DerefMut for Metric {
127 fn deref_mut(&mut self) -> &mut Self::Target {
128 match self {
129 Self::Hydrated(inner) => inner,
130 Self::Named(inner) => inner,
131 Self::Identifiable(inner) => inner,
132 }
133 }
134}
135
136impl MetaKind for Metric {
137 const ENDPOINT_NAME: &'static str = "metrics";
138}
139
140static CACHE: RwLock<HydratedCacheTable<Metric>> = rwlock_const_new(HydratedCacheTable::new());
141
142impl EndpointEntryCache for Metric {
143 type HydratedVariant = HydratedMetric;
144 type Identifier = MetricId;
145 type URL = MetaEndpointUrl<Self>;
146
147 fn into_hydrated_variant(self) -> Option<Self::HydratedVariant> {
148 self.try_as_hydrated()
149 }
150
151 fn id(&self) -> &Self::Identifier {
152 &self.id
153 }
154
155 fn url_for_id(_id: &Self::Identifier) -> Self::URL {
156 MetaEndpointUrl::new()
157 }
158
159 fn get_entries(response: <Self::URL as StatsAPIUrl>::Response) -> impl IntoIterator<Item=Self>
160 where
161 Self: Sized
162 {
163 response.entries
164 }
165
166 fn get_hydrated_cache_table() -> &'static RwLock<HydratedCacheTable<Self>>
167 where
168 Self: Sized
169 {
170 &CACHE
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use crate::endpoints::StatsAPIUrl;
177 use crate::endpoints::meta::MetaEndpointUrl;
178
179 #[tokio::test]
180 async fn parse_meta() {
181 let _response = MetaEndpointUrl::<super::Metric>::new().get().await.unwrap();
182 }
183}