mlb_api/endpoints/meta/kinds/
metrics.rs

1use 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					// PartialEq trait doesn't exist yet, so we have to use the other stuff implemented here
24					$((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}