mlb_api/requests/stats/
parse.rs1use 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 #[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 })
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}