Skip to main content

mlb_api/requests/stats/
macros.rs

1#[macro_export]
2#[doc(hidden)]
3macro_rules! __stats__stat_type_definition {
4    ($parent_name:ident => $vis:vis struct $name:ident { $stat_type:ident => [$($stat_group:ident),+] }) => {
5		::pastey::paste! {
6			#[derive(Debug, PartialEq, Clone)]
7			$vis struct $name {
8				$($vis [< $stat_group:snake >]: ::std::boxed::Box<<$crate::stats::stat_types::[<__ $stat_type StatTypeStats>] as $crate::stats::StatTypeStats>::$stat_group>),+
9			}
10
11			impl [<__ $parent_name Split Parser>] for $name {
12				fn parse(parsed_stats: &mut $crate::stats::parse::__ParsedStats) -> ::core::result::Result<Self, ::std::string::String> {
13					Ok(Self {
14						$([<$stat_group:snake>]: ::std::boxed::Box::new(
15							$crate::stats::parse::make_stat_split::<<$crate::stats::stat_types::[< __ $stat_type StatTypeStats >] as $crate::stats::StatTypeStats>::$stat_group>(
16								parsed_stats, ::core::stringify!([<$stat_type:lower_camel>]), $crate::meta::StatGroup::$stat_group
17							).map_err(|e| ::std::string::ToString::to_string(&e))?
18						)),+
19					})
20				}
21			}
22    	}
23	};
24}
25
26#[macro_export]
27#[doc(hidden)]
28macro_rules! __stats__base_hydration_text {
29    ([$first_stat_type:ident $(, $stat_type:ident)* $(,)?] [$first_stat_group:ident $(, $stat_group:ident)* $(,)?]) => {
30		::pastey::paste! {
31			::core::concat!(
32				"type=[",
33				::core::stringify!([<$first_stat_type:lower_camel>]),
34				$(",", ::core::stringify!([<$stat_type:lower_camel>]), )*
35				"],group=[",
36				::core::stringify!([<$first_stat_group:lower_camel>]),
37				$(",", ::core::stringify!([<$stat_group:lower_camel>]), )*
38				"]"
39			)
40		}
41	};
42}
43
44// macro_rules! __stats__generate_needle_haystack_predicate {
45//     ($name:ident ! ( $($needle:ident),+ ) $d:tt) => {
46// 		#[macro_export]
47// 		#[doc(hidden)]
48// 		macro_rules! $name {
49// 			$(
50// 			([$needle $d (, $d haystack:ident)* $d (,)?] => { $d ($d t1:tt)* } else { $d ($d t2:tt)* }) => {
51// 				$d ($d t1)*
52// 			};
53// 			)+
54// 			([$d first:ident $d(, $d haystack:ident)* $d (,)?] => { $d ($d t1:tt)* } else { $d ($d t2:tt)* }) => {
55// 				$name!([$d($d haystack),*] => { $d ($d t1)* } else { $d ($d t2)* });
56// 			};
57//     		([$d(,)?] => { $d ($d t1:tt)* } else { $d ($d t2:tt)* }) => { $d ($d t2)* };
58// 		}
59//
60// 		#[doc(hidden)]
61// 		pub use $name;
62// 	};
63// }
64
65/// Generated by [`__stats__generate_needle_haystack_predicate`], cannot inline due to <https://github.com/rust-lang/rust/pull/52234>
66#[macro_export]
67#[doc(hidden)]
68macro_rules! __stats__needle_haystack_metrics {
69	([$($haystack:ident),+ $(,)?] => { $($t:tt)* }) => {
70		$crate::__stats__needle_haystack_metrics!([$($haystack),+] => { $($t)* } else {});
71	};
72	([MetricLog $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
73		$($t1)*
74	};
75	([MetricAverages $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
76		$($t1)*
77	};
78	([$first:ident $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
79		$crate::__stats__needle_haystack_metrics!([$($haystack),*] => { $($t1)* } else { $($t2)* });
80	};
81	([$(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => { $($t2)* };
82}
83
84/// Generated by [`__stats__generate_needle_haystack_predicate`], cannot inline due to <https://github.com/rust-lang/rust/pull/52234>
85#[macro_export]
86#[doc(hidden)]
87macro_rules! __stats__needle_haystack_date_range {
88	([$($haystack:ident),+ $(,)?] => { $($t:tt)* }) => {
89		$crate::__stats__needle_haystack_date_range!([$($haystack),+] => { $($t)* } else {});
90	};
91	([ByDateRange $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
92		$($t1)*
93	};
94	([$first:ident $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
95		$crate::__stats__needle_haystack_date_range!([$($haystack),*] => { $($t1)* } else { $($t2)* });
96	};
97	([$(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => { $($t2)* };
98}
99
100/// Generated by [`__stats__generate_needle_haystack_predicate`], cannot inline due to <https://github.com/rust-lang/rust/pull/52234>
101#[macro_export]
102#[doc(hidden)]
103macro_rules! __stats__needle_haystack_situations {
104	([$($haystack:ident),+ $(,)?] => { $($t:tt)* }) => {
105		$crate::__stats__needle_haystack_situations!([$($haystack),+] => { $($t)* } else {});
106	};
107	([StatSplits $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
108		$($t1)*
109	};
110	([StatSplitsAdvanced $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
111		$($t1)*
112	};
113	([$first:ident $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
114		$crate::__stats__needle_haystack_situations!([$($haystack),*] => { $($t1)* } else { $($t2)* });
115	};
116	([$(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => { $($t2)* };
117}
118
119/// Generated by [`__stats__generate_needle_haystack_predicate`], cannot inline due to <https://github.com/rust-lang/rust/pull/52234>
120#[macro_export]
121#[doc(hidden)]
122macro_rules! __stats__needle_haystack_games_back {
123	([$($haystack:ident),+ $(,)?] => { $($t:tt)* }) => {
124		$crate::__stats__needle_haystack_games_back!([$($haystack),+] => { $($t)* } else {});
125	};
126	([LastXGames $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
127		$($t1)*
128	};
129	([$first:ident $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
130		$crate::__stats__needle_haystack_games_back!([$($haystack),*] => { $($t1)* } else { $($t2)* });
131	};
132	([$(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => { $($t2)* };
133}
134
135/// Generated by [`__stats__generate_needle_haystack_predicate`], cannot inline due to <https://github.com/rust-lang/rust/pull/52234>
136#[macro_export]
137#[doc(hidden)]
138macro_rules! __stats__needle_haystack_opponent_player {
139	([$($haystack:ident),+ $(,)?] => { $($t:tt)* }) => {
140		$crate::__stats__needle_haystack_opponent_player!([$($haystack),+] => { $($t)* } else {});
141	};
142	([VsPlayer5Y $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
143		$($t1)*
144	};
145	([VsPlayerTotal $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
146		$($t1)*
147	};
148	([VsPlayer $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
149		$($t1)*
150	};
151	([$first:ident $(, $haystack:ident)* $(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => {
152		$crate::__stats__needle_haystack_opponent_player!([$($haystack),*] => { $($t1)* } else { $($t2)* });
153	};
154	([$(,)?] => { $($t1:tt)* } else { $($t2:tt)* }) => { $($t2)* };
155}
156
157#[macro_export]
158#[doc(hidden)]
159macro_rules! __stats__request_data {
160	(@ metrics [$($stat_type:ident),+] $(#[$m:meta])* $vis:vis struct $name:ident { $($field_tt:tt)* } $($impl_tt:tt)*) => {
161		::pastey::paste! {
162			$crate::__stats__needle_haystack_metrics! { [$($stat_type),+] => {
163				$crate::__stats__request_data! { @ date_range [$($stat_type),+]
164					$(#[$m])*
165					$vis struct $name {
166						$($field_tt)*
167						metrics: ::std::vec::Vec<$crate::meta::MetricId>,
168					}
169					$($impl_tt)*
170					impl<S: [<$name:snake _builder>]::State> [<$name Builder>]<S> {
171						pub fn metric(self, metric: impl ::core::convert::Into<$crate::meta::MetricId>) -> [<$name Builder>]<[<$name:snake _builder>]::SetMetrics<S>>
172						where
173							S::Metrics: [<$name:snake _builder>]::IsUnset
174						{
175							self.metrics(::std::vec![::core::convert::Into::into(metric)])
176						}
177					}
178				}
179			} else {
180				$crate::__stats__request_data! { @ date_range [$($stat_type),+]
181					$(#[$m])*
182					$vis struct $name {
183						$($field_tt)*
184					}
185					$($impl_tt)*
186				}
187			}}
188		}
189	};
190	(@ date_range [$($stat_type:ident),+] $(#[$m:meta])* $vis:vis struct $name:ident { $($field_tt:tt)* } $($impl_tt:tt)*) => {
191		::pastey::paste! {
192			$crate::__stats__needle_haystack_date_range!{[$($stat_type),+] => {
193				$crate::__stats__request_data! { @ situations [$($stat_type),+]
194					$(#[$m])*
195					$vis struct $name {
196						$($field_tt)*
197						date_range: $crate::NaiveDateRange,
198					}
199					$($impl_tt)*
200				}
201			} else {
202				$crate::__stats__request_data! { @ situations [$($stat_type),+]
203					$(#[$m])*
204					$vis struct $name {
205						$($field_tt)*
206					}
207					$($impl_tt)*
208				}
209			}}
210		}
211	};
212	(@ situations [$($stat_type:ident),+] $(#[$m:meta])* $vis:vis struct $name:ident { $($field_tt:tt)* } $($impl_tt:tt)*) => {
213		::pastey::paste! {
214			$crate::__stats__needle_haystack_situations!{[$($stat_type),+] => {
215				$crate::__stats__request_data! { @ games_back [$($stat_type),+]
216					$(#[$m])*
217					$vis struct $name {
218						$($field_tt)*
219						situations: ::std::vec::Vec<$crate::meta::SituationCodeId>,
220						#[builder(default)]
221						situation_filter: $crate::meta::SituationCodeFilter,
222					}
223					$($impl_tt)*
224					impl<S: [<$name:snake _builder>]::State> [<$name Builder>]<S> {
225						#[allow(dead_code, reason = "could be used by the end user")]
226						pub fn situation(self, situation: impl ::core::convert::Into<$crate::meta::SituationCodeId>) -> [<$name Builder>]<[<$name:snake _builder>]::SetSituations<S>>
227						where
228							S::Situations: [<$name:snake _builder>]::IsUnset
229						{
230							self.situations(::std::vec![::core::convert::Into::into(situation)])
231						}
232					}
233				}
234			} else {
235				$crate::__stats__request_data! { @ games_back [$($stat_type),+]
236					$(#[$m])*
237					$vis struct $name {
238						$($field_tt)*
239					}
240					$($impl_tt)*
241				}
242			}}
243		}
244	};
245	(@ games_back [$($stat_type:ident),+] $(#[$m:meta])* $vis:vis struct $name:ident { $($field_tt:tt)* } $($impl_tt:tt)*) => {
246		::pastey::paste! {
247			$crate::__stats__needle_haystack_games_back!{[$($stat_type),+] => {
248				$crate::__stats__request_data! { @ opponent_player [$($stat_type),+]
249					$(#[$m])*
250					$vis struct $name {
251						$($field_tt)*
252						games_back: usize,
253					}
254					$($impl_tt)*
255				}
256			} else {
257				$crate::__stats__request_data! { @ opponent_player [$($stat_type),+]
258					$(#[$m])*
259					$vis struct $name {
260						$($field_tt)*
261					}
262					$($impl_tt)*
263				}
264			}}
265		}
266	};
267	(@ opponent_player [$($stat_type:ident),+] $(#[$m:meta])* $vis:vis struct $name:ident { $($field_tt:tt)* } $($impl_tt:tt)*) => {
268		::pastey::paste! {
269			$crate::__stats__needle_haystack_opponent_player!{[$($stat_type),+] => {
270				// $crate::__stats__request_data! { @ ? [$($stat_type),+]
271					$(#[$m])*
272					$vis struct $name {
273						$($field_tt)*
274						#[builder(into)]
275						opponent_player: $crate::person::PersonId,
276					}
277					$($impl_tt)*
278				// }
279			} else {
280				// $crate::__stats__request_data! { @ ? [$($stat_type),+]
281					$(#[$m])*
282					$vis struct $name {
283						$($field_tt)*
284					}
285					$($impl_tt)*
286				// }
287			}}
288		}
289	};
290    ($vis:vis $name:ident [$($stat_type:ident),+]) => {
291		::pastey::paste! {
292			$crate::__stats__request_data! { @ metrics [$($stat_type),+]
293				#[derive(::bon::Builder)]
294				#[builder(derive(Into))]
295				#[allow(unused, reason = "could be unused by the end user")]
296				$vis struct [<$name RequestData>] {
297					#[builder(default)]
298					game_type: $crate::meta::GameType,
299					#[builder(into)]
300					season: ::core::option::Option<$crate::season::SeasonId>,
301					team_ids: ::core::option::Option<::std::vec::Vec<$crate::team::TeamId>>,
302					sport_ids: ::core::option::Option<::std::vec::Vec<$crate::sport::SportId>>,
303					league_ids: ::core::option::Option<::std::vec::Vec<$crate::league::LeagueId>>,
304					limit: Option<usize>,
305					#[builder(default)]
306					player_pool: $crate::PlayerPool,
307
308
309					/*batter_team: TeamId,
310					pitcher_team: TeamId,
311					batter: PersonId,
312					pitcher: PersonId,
313					days_back: usize*/
314				}
315
316				#[allow(unused, reason = "may be used by end user")]
317				impl<S: [<$name:snake _request_data_builder>]::State> [<$name RequestDataBuilder>]<S> {
318					pub fn team_id(self, team_id: impl ::core::convert::Into<$crate::team::TeamId>) -> [<$name RequestDataBuilder>]<[<$name:snake _request_data_builder>]::SetTeamIds<S>>
319					where
320						S::TeamIds: [<$name:snake _request_data_builder>]::IsUnset
321					{
322						self.team_ids(::std::vec![::core::convert::Into::into(team_id)])
323					}
324
325					pub fn sport_id(self, sport_id: impl ::core::convert::Into<$crate::sport::SportId>) -> [<$name RequestDataBuilder>]<[<$name:snake _request_data_builder>]::SetSportIds<S>>
326					where
327						S::SportIds: [<$name:snake _request_data_builder>]::IsUnset
328					{
329						self.sport_ids(::std::vec![::core::convert::Into::into(sport_id)])
330					}
331
332					pub fn league_id(self, league_id: impl ::core::convert::Into<$crate::league::LeagueId>) -> [<$name RequestDataBuilder>]<[<$name:snake _request_data_builder>]::SetLeagueIds<S>>
333					where
334						S::LeagueIds: [<$name:snake _request_data_builder>]::IsUnset
335					{
336						self.league_ids(::std::vec![::core::convert::Into::into(league_id)])
337					}
338				}
339			}
340
341			impl ::core::fmt::Display for [<$name RequestData>] {
342				fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
343					use ::itertools::Itertools;
344
345					::core::fmt::Write::write_fmt(f, ::core::format_args!("gameType={:?},", self.game_type))?;
346					if let ::core::option::Option::Some(season)	= self.season {
347						::core::fmt::Write::write_fmt(f, ::core::format_args!("season={},", season))?;
348					}
349					if let ::core::option::Option::Some(ids) = ::core::option::Option::as_ref(&self.team_ids) {
350						::core::fmt::Write::write_fmt(f, ::core::format_args!("teamIds=[{}],", ids.iter().join(",")))?;
351					}
352					if let ::core::option::Option::Some(ids) = ::core::option::Option::as_ref(&self.sport_ids) {
353						::core::fmt::Write::write_fmt(f, ::core::format_args!("sportIds=[{}],", ids.iter().join(",")))?;
354					}
355					if let ::core::option::Option::Some(ids) = ::core::option::Option::as_ref(&self.league_ids) {
356						::core::fmt::Write::write_fmt(f, ::core::format_args!("leagueIds=[{}],", ids.iter().join(",")))?;
357					}
358					::core::fmt::Write::write_fmt(f, ::core::format_args!("playerPool={},", self.player_pool))?;
359					if let ::core::option::Option::Some(limit) = ::core::option::Option::as_ref(&self.limit) {
360						::core::fmt::Write::write_fmt(f, ::core::format_args!("limit={}", limit))?;
361					}
362
363					$crate::__stats__needle_haystack_metrics! { [$($stat_type),+] => {
364						::core::fmt::Write::write_fmt(f, ::core::format_args!("metrics={},", self.metrics.iter().join(",")))?;
365					} else {}}
366					$crate::__stats__needle_haystack_date_range! { [$($stat_type),+] => {
367						::core::fmt::Write::write_fmt(f, ::core::format_args!("startDate={},endDate={},", self.date_range.start().format($crate::MLB_API_DATE_FORMAT), self.date_range.end().format($crate::MLB_API_DATE_FORMAT)))?;
368					} else {}}
369					$crate::__stats__needle_haystack_situations! { [$($stat_type),+] => {
370						::core::fmt::Write::write_fmt(f, ::core::format_args!("sitCodes=[{}],combineSits={},", self.situations.iter().join(","), self.situation_filter == $crate::meta::SituationCodeFilter::All))?;
371					} else {}}
372					$crate::__stats__needle_haystack_opponent_player! { [$($stat_type),+] => {
373						::core::fmt::Write::write_fmt(f, ::core::format_args!("opposingPlayerId={},", self.opponent_player))?;
374					} else {}}
375
376					Ok(())
377				}
378			}
379
380			$crate::__stats__needle_haystack_metrics! { [$($stat_type),+] => {} else {
381				$crate::__stats__needle_haystack_date_range! { [$($stat_type),+] => {} else {
382					$crate::__stats__needle_haystack_situations! { [$($stat_type),+] => {} else {
383						$crate::__stats__needle_haystack_opponent_player! { [$($stat_type),+] => {} else {
384							impl ::core::default::Default for [<$name RequestData>] {
385								fn default() -> Self {
386									[<$name RequestDataBuilder>]::<[<$name:snake _request_data_builder>]::Empty>::build(Self::builder())
387								}
388							}
389						}}
390					}}
391				}}
392			}}
393
394			impl $name {
395				#[allow(unused, reason = "might use RequestData::builder() instead")]
396				pub fn builder() -> [<$name RequestDataBuilder>] {
397					[<$name RequestData>]::builder()
398				}
399			}
400		}
401	};
402}
403
404#[doc(hidden)]
405#[macro_export]
406macro_rules! __stats0 {
407    ($vis:vis struct $name:ident {
408		[$($stat_type:ident),+ $(,)?] + $stat_groups:tt
409	}) => {
410		::pastey::paste! {
411			#[derive(Debug, PartialEq, Clone)]
412        	$vis struct $name {
413				$($vis [<$stat_type:snake>]: [<$name $stat_type Split>],)*
414			}
415
416			#[doc(hidden)]
417			trait [<__ $name Split Parser>] {
418				fn parse(parsed_stats: &mut $crate::stats::parse::__ParsedStats) -> ::core::result::Result<Self, ::std::string::String>
419				where
420					Self: Sized;
421			}
422
423			$($crate::__stats__stat_type_definition!($name => $vis struct [<$name $stat_type Split>] { $stat_type => $stat_groups });)+
424
425			impl<'de> ::serde::Deserialize<'de> for $name {
426				fn deserialize<D: ::serde::de::Deserializer<'de>>(deserializer: D) -> ::core::result::Result<Self, D::Error>
427				where
428					Self: Sized
429				{
430					let mut parsed_stats: $crate::stats::parse::__ParsedStats = <$crate::stats::parse::__ParsedStats as ::serde::Deserialize>::deserialize(deserializer)?;
431
432					Ok(Self {
433						$([<$stat_type:snake>]: <[<$name $stat_type Split>] as [<__ $name Split Parser>]>::parse(&mut parsed_stats).map_err(<D::Error as ::serde::de::Error>::custom)?),+
434					})
435				}
436			}
437
438			$crate::__stats__request_data!($vis $name [$($stat_type),+]);
439
440			impl $crate::hydrations::Hydrations for $name {
441				type RequestData = [<$name RequestData>];
442
443                fn hydration_text(data: &<Self as $crate::hydrations::Hydrations>::RequestData) -> ::std::borrow::Cow<'static, str> {
444					let base: &'static str = $crate::__stats__base_hydration_text!([$($stat_type),+] $stat_groups);
445					let data: ::std::string::String = ::std::string::ToString::to_string(data);
446					if str::is_empty(&*data) {
447						::std::borrow::Cow::Borrowed(base)
448					} else {
449						::std::borrow::Cow::Owned(::std::format!("{base},{data}"))
450					}
451				}
452            }
453		}
454    };
455}
456
457
458// todo: see why we need a clone
459
460/// Shorthand function for getting a single [`StatType`] and [`StatGroup`] for a specific player.
461///
462/// ## Examples
463/// ```no_run
464/// let stat = single_stat!(Season + Hitting for 660_271).await.unwrap();
465/// 
466/// // or supply builder params with
467/// let stat = single_stat!(Season + Hitting for 660_271; with |builder| builder.season(2024)).await.unwrap();
468/// ```
469#[macro_export]
470macro_rules! single_stat {
471	($stat_type:ident + $stat_group:ident for $person_id:expr) => {
472		$crate::single_stat! { $stat_type + $stat_group for $person_id; with |builder| builder }
473	};
474	($stat_type:ident + $stat_group:ident for $person_id:expr; with |$builder:ident| $builder_expr:expr) => {{
475		::pastey::paste! {
476			$crate::person_hydrations! {
477				struct [<$stat_type $stat_group SingleStatHydrations >] {
478				    stats: { [$stat_type] + [$stat_group] },
479				}
480			}
481			
482			async {
483				let request = $crate::person::PersonRequest::<[<$stat_type $stat_group SingleStatHydrations >]>::builder().hydrations([<$stat_type $stat_group SingleStatHydrations>]::builder().stats({
484					let $builder = [<$stat_type $stat_group SingleStatHydrationsInlineStats>]::builder();
485					$builder_expr
486				})).id($person_id).build();
487				let response = $crate::request::RequestURL::get(&request).await;
488				response.and_then::<[$crate::person::Person<[<$stat_type $stat_group SingleStatHydrations>]>; 1], _>(|response|
489					response.people.try_into().map_err(|_| $crate::request::Error::MLB($crate::MLBError { message: "Expected one person in a single stat response".to_owned() }))
490				)
491				.map(|[person]| person.extras.stats.[<$stat_type:snake>].[<$stat_group:snake>].clone())
492			}
493		}
494	}};
495}
496
497/// Generates stat data types to be used in requests.
498///
499/// These are commonly associated with [`person_hydrations`](crate::person_hydrations) to create a [`PersonRequest`](crate::person::PersonRequest).
500///
501/// # Stat Types & Stat Groups for [`stats_hydrations!`](crate::stats_hydrations!)
502///
503/// | Name                  | Stat Type                            | Stat Group | Notes                        |
504/// |-----------------------|--------------------------------------|------------|------------------------------|
505/// | `Projected`           | [`WithPlayer<_>`]                    | **`HP--`** | likely ZIPS projections      |
506/// | `YearByYear`          | [`HashMap<SeasonId, WithSeason<_>>`] | **`HPCF`** | 1.                           |
507/// | `YearByYearAdvanced`  | [`HashMap<SeasonId, WithSeason<_>>`] | **`HP--`** | 1.                           |
508/// | `Season`              | [`WithSeason<_>`]                    | **`HPCF`** |                              |
509/// | `Career`              | [`Career<_>`]                        | **`HPCF`** |                              |
510/// | `SeasonAdvanced`      | [`WithSeason<_>`]                    | **`HP--`** |                              |
511/// | `CareerAdvanced`      | [`Career<_>`]                        | **`HP--`** |                              |
512/// | `GameLog`             | [`Vec<WithGame<_>>`]                 | **`HPCF`** |                              |
513/// | `PlayLog`             | [`Vec<SingleMatchup<Play<_>>>`]      | **`HPCF`** | same format as in games      |
514/// | `PitchLog`            | [`Vec<SingleMatchup<PitchStat>>`]    | **`HPCF`** | same format as in games      |
515/// | `ExpectedStatistics`  | *no wrapper*                         | **`HP--`** | `xAVG`, `xwOBA`, etc.        |
516/// | `Sabermetrics`        | *no wrapper*                         | **`HP--`** | `xFIP`, `fWAR`, etc.         |
517/// | `VsPlayer5Y`          | [`AccumulatedVsPlayerMatchup<_>`]    | **`HP--`** | `opposing_player` in builder |
518/// | `LastXGames`          | [`WithTeam<_>`]                      | **`HPCF`** | `games_back` in builder      |
519/// | `ByDateRange`         | [`WithTeam<_>`]                      | **`HPCF`** | `date_range` in builder      |
520/// | `ByDateRangeAdvanced` | [`WithTeam<_>`]                      | **`HPCF`** | `date_range` in builder      |
521/// | `ByMonth`             | [`HashMap<Month, WithMonth<_>>`]     | **`HPCF`** |                              |
522/// | `ByDayOfWeek`         | [`HashMap<Weekday, WithWeekday<_>>`] | **`HPCF`** |                              |
523/// | `HomeAndAway`         | [`WithHomeAndAway<_>`]               | **`HPCF`** |                              |
524/// | `WinLoss`             | [`WithWinLoss<_>`]                   | **`HPCF`** |                              |
525/// | `OpponentsFaced`      | [`FieldedMatchup`]                   | **`HPCF`** |                              |
526/// | `StatSplits`          | [`WithSeason<_>`]                    | **`HP--`** | `situations` in builder      |
527/// | `StatSplitsAdvanced`  | [`WithSeason<_>`]                    | **`HP--`** | `situations` in builder      |
528///
529/// Note: `HPCF` stands for Hitting, Pitching, Catching, and Fielding. [`StatType`]s will be marked as for what [`StatGroup`]s they support in requests.
530///
531/// Table Footnotes
532/// 1. Seasons will display the last entry sent via the API, which is typically a full season with multiple teams, as opposed to the split with one team (ex. if a player is traded at the deadline).
533///
534/// # Examples
535///```
536/// mlb_api::stats_hydrations! {
537///     pub struct MyStats {
538///         [Season, Career] + [Hitting, Pitching]
539///     }
540/// }
541///
542/// mlb_api::person_hydrations! {
543///     pub struct MyStatsHydrations {
544///         stats: MyStats,
545///     }
546/// }
547///
548/// let response = mlb_api::person::PersonRequest::<MyStatsHydrations>::builder()
549///     .id(id)
550///     .hydrations(MyStatsHydrations::builder()
551///         .stats(MyStats::builder()))
552///     .build_and_get()
553///     .await?;
554///
555/// // for simple requests which don't involve values supplied in the builder (see table above), this also works:
556/// let response = mlb_api::person::PersonRequest::<MyStatsHydrations>::for_id(id)
557///     .build_and_get()
558///     .await?;
559///
560/// let stats: &MyStats = &response.people[0].extras.stats;
561///```
562///
563/// [`HashMap<SeasonId, WithSeason<_>>`]: crate::stats::wrappers::WithSeason
564/// [`WithSeason<_>`]: crate::stats::wrappers::WithSeason
565/// [`Career<_>`]: crate::stats::wrappers::Career
566/// [`Vec<WithGame<_>>`]: crate::stats::wrappers::WithGame
567/// [`Vec<SingleMatchup<Play<_>>>`]: crate::stats::wrappers::SingleMatchup
568/// [`Vec<SingleMatchup<PitchStat>>`]: crate::stats::wrappers::SingleMatchup
569/// [`Vec<PitchUsage>`]: crate::stats::raw::PitchUsage
570/// [`AccumulatedVsPlayerMatchup<_>`]: crate::stats::wrappers::AccumulatedVsPlayerMatchup
571/// [`WithTeam<_>`]: crate::stats::wrappers::WithTeam
572/// [`HashMap<Month, WithMonth<_>>`]: crate::stats::wrappers::WithMonth
573/// [`HashMap<Weekday, WithWeekday<_>>`]: crate::stats::wrappers::WithWeekday
574/// [`WithHomeAndAway<_>`]: crate::stats::wrappers::WithHomeAndAway
575/// [`WithWinLoss<_>`]: crate::stats::wrappers::WithWinLoss
576/// [`HittingHotColdZones`]: crate::stats::raw::HittingHotColdZones
577/// [`PitchingHotColdZones`]: crate::stats::raw::PitchingHotColdZones
578/// [`FieldedMatchup`]: crate::stats::raw::FieldedMatchup
579/// [`StatType`]: crate::meta::StatType
580/// [`StatGroup`]: crate::meta::StatGroup
581/// [`WithPlayer<_>`]: crate::stats::wrappers::WithPlayer
582#[macro_export]
583macro_rules! stats_hydrations {
584    ($($t:tt)*) => {
585		$crate::__stats0! { $($t)* }
586	};
587}