Skip to main content

mlb_api/requests/stats/
parse.rs

1use serde::Deserialize;
2use serde_json::Value;
3use smallvec::SmallVec;
4use thiserror::Error;
5use crate::stat_groups::StatGroup;
6use crate::stat_types::StatType;
7use crate::stats::Stat;
8
9#[derive(Deserialize)]
10#[doc(hidden)]
11struct __RawStats {
12	#[serde(alias = "stat")]
13	stats: Vec<__RawStatEntry>,
14}
15
16#[derive(Deserialize)]
17#[serde(untagged)]
18#[doc(hidden)]
19enum __RawStatEntry {
20	Depth0(__Depth0StatEntry),
21	Depth1(__Depth1StatEntry),
22}
23
24pub type __Depth0StatEntry = __ParsedStatEntry;
25
26#[derive(Deserialize)]
27#[doc(hidden)]
28struct __Depth1StatEntry {
29	splits: Vec<__InlineStatEntry>
30}
31
32#[derive(Deserialize)]
33#[doc(hidden)]
34struct __InlineStatEntry {
35	#[serde(rename = "type")]
36	stat_type: StatType,
37	#[serde(rename = "group")]
38	stat_group: StatGroup,
39	stat: Value,
40}
41
42impl From<__InlineStatEntry> for __ParsedStatEntry {
43	fn from(value: __InlineStatEntry) -> Self {
44		Self {
45			stat_type: value.stat_type,
46			stat_group: value.stat_group,
47			splits: SmallVec::from_buf::<1>([value.stat]),
48		}
49	}
50}
51
52impl From<__Depth1StatEntry> for Vec<__Depth0StatEntry> {
53	fn from(value: __Depth1StatEntry) -> Self {
54		value.splits.into_iter().map(Into::into).collect()
55	}
56}
57
58impl From<__RawStatEntry> for Vec<__ParsedStatEntry> {
59	fn from(value: __RawStatEntry) -> Self {
60		match value {
61			__RawStatEntry::Depth0(x) => vec![x],
62			__RawStatEntry::Depth1(x) => x.into(),
63		}
64	}
65}
66
67impl From<__RawStats> for __ParsedStats {
68	fn from(value: __RawStats) -> Self {
69		let mut entries = Vec::with_capacity(value.stats.len());
70		for entry in value.stats {
71			match entry {
72				__RawStatEntry::Depth0(entry) => entries.push(entry),
73				__RawStatEntry::Depth1(entry) => {
74					entries.reserve(entry.splits.len());
75					for entry in entry.splits {
76						entries.push(__ParsedStatEntry::from(entry));
77					}
78				},
79			}
80		}
81		Self {
82			entries
83		}
84	}
85}
86
87#[doc(hidden)]
88#[derive(Deserialize, Debug)]
89#[serde(from = "__RawStats")]
90pub struct __ParsedStats {
91	entries: Vec<__ParsedStatEntry>
92}
93
94#[doc(hidden)]
95#[derive(Deserialize, Debug)]
96#[serde(rename_all = "camelCase")]
97pub struct __ParsedStatEntry {
98	#[serde(rename = "type")]
99	stat_type: StatType,
100	#[serde(rename = "group")]
101	stat_group: StatGroup,
102	splits: SmallVec<Value, 1>,
103}
104
105#[doc(hidden)]
106#[derive(Debug, Error)]
107pub enum MakeStatSplitsError<S: Stat> {
108	#[error("Failed to deserialize json into split type ({name}): {0}", name = core::any::type_name::<S>())]
109	FailedPartialDeserialize(serde_json::Error),
110	// FailedPartialDeserialize(serde_path_to_error::Error<serde_json::Error>),
111	#[error("Failed to deserialize splits into greater split type ({name}): {0}", name = core::any::type_name::<S>())]
112	FailedFullDeserialize(S::TryFromSplitError),
113}
114
115#[doc(hidden)]
116pub fn make_stat_split<S: Stat>(stats: &mut __ParsedStats, target_stat_type_str: &'static str, target_stat_group: StatGroup) -> Result<S, MakeStatSplitsError<S>> {
117	if let Some(idx) = stats.entries.iter().position(|entry| entry.stat_type.as_str().eq_ignore_ascii_case(target_stat_type_str) && entry.stat_group == target_stat_group) {
118		let entry = stats.entries.remove(idx);
119		let partially_deserialized = entry.splits
120			.into_iter()
121			.map(|split| {
122				<<S as Stat>::Split as Deserialize>::deserialize(split)
123				// serde_path_to_error::deserialize::<_, <S as Stat>::Split>(split)
124			})
125			.collect::<Result<Vec<S::Split>, _>>()
126			.map_err(MakeStatSplitsError::FailedPartialDeserialize)?;
127		let partially_deserialized_is_empty = partially_deserialized.is_empty();
128		match <S as Stat>::from_splits(partially_deserialized.into_iter()) {
129			Ok(s) => Ok(s),
130			Err(_) if partially_deserialized_is_empty => Ok(S::default()),
131			Err(e) => Err(MakeStatSplitsError::FailedFullDeserialize(e)),
132		}
133	} else {
134		Ok(S::default())
135	}
136}