mlb_api/endpoints/person/
types.rs

1use std::ops::{Deref, DerefMut};
2use chrono::NaiveDate;
3use derive_more::{Deref, Display, DerefMut};
4use serde::Deserialize;
5use crate::types::{Gender, Handedness, HeightMeasurement, Position};
6
7#[derive(Debug, Deref, DerefMut, Deserialize, PartialEq, Eq, Clone)]
8#[serde(rename_all = "camelCase")]
9pub struct Ballplayer {
10    #[deref]
11    #[deref_mut]
12    #[serde(flatten)]
13    inner: HydratedPerson,
14
15    #[serde(deserialize_with = "crate::types::from_str")]
16    pub primary_number: u8,
17    pub current_age: u8,
18    #[serde(flatten)]
19    pub birth_data: BirthData,
20    #[serde(flatten)]
21    pub body_measurements: BodyMeasurements,
22    pub gender: Gender,
23    pub draft_year: u16,
24    #[serde(rename = "mlbDebutDate")] pub mlb_debut: NaiveDate,
25    pub bat_side: Handedness,
26    pub pitch_hand: Handedness,
27    #[serde(flatten)]
28    pub strike_zone: StrikeZone,
29}
30
31#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
32#[serde(rename_all = "camelCase")]
33pub struct BirthData {
34    pub birth_date: NaiveDate,
35    pub birth_city: String,
36    #[serde(rename = "birthStateProvince")] pub birth_state_or_province: String,
37    pub birth_country: String,
38}
39
40#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
41#[serde(rename_all = "camelCase")]
42pub struct BodyMeasurements {
43    #[serde(deserialize_with = "crate::types::from_str")]
44    pub height: HeightMeasurement,
45    pub weight: u16,
46}
47
48#[derive(Debug, Deserialize, PartialEq, Clone)]
49#[serde(rename_all = "camelCase")]
50pub struct StrikeZone {
51    pub strike_zone_top: f64,
52    pub strike_zone_bottom: f64,
53}
54
55impl Eq for StrikeZone {}
56
57#[derive(Debug, Deref, DerefMut, Deserialize, PartialEq, Eq, Clone)]
58#[serde(rename_all = "camelCase")]
59pub struct HydratedPerson {
60    #[deref]
61    #[deref_mut]
62    #[serde(flatten)]
63    inner: UnhydratedPerson,
64    
65    pub primary_position: Position,
66    // '? ? Brown' in 1920 does not have a first name or a middle name, rather than dealing with Option and making everyone hate this API, the better approach is an empty String.
67    #[serde(default)]
68    pub first_name: String,
69    pub middle_name: Option<String>,
70    #[serde(default)]
71    pub last_name: String,
72    #[serde(default)]
73    #[serde(rename = "useName")] pub use_first_name: String,
74    #[serde(default)]
75    pub use_last_name: String,
76    #[serde(default)]
77    pub boxscore_name: String,
78    
79    pub is_player: bool,
80    pub is_verified: bool,
81    pub active: bool,
82}
83
84impl HydratedPerson {
85    #[must_use]
86    pub fn name_first_last(&self) -> String {
87        format!("{0} {1}", self.first_name, self.last_name)
88    }
89
90    #[must_use]
91    pub fn name_last_first(&self) -> String {
92        format!("{1}, {0}", self.first_name, self.last_name)
93    }
94
95    #[must_use]
96    pub fn name_last_first_initial(&self) -> String {
97        if let Some(char) = self.first_name.chars().next() {
98            format!("{1}, {0}", char, self.last_name)
99        } else {
100            self.last_name.clone()
101        }
102    }
103
104    #[must_use]
105    pub fn name_first_initial_last(&self) -> String {
106        if let Some(char) = self.first_name.chars().next() {
107            format!("{0} {1}", char, self.last_name)
108        } else {
109            self.last_name.clone()
110        }
111    }
112
113    #[must_use]
114    pub fn name_fml(&self) -> String {
115        if let Some(middle) = &self.middle_name {
116            format!("{0} {1} {2}", self.first_name, middle, self.last_name)
117        } else {
118            format!("{0} {1}", self.first_name, self.last_name)
119        }
120    }
121
122    #[must_use]
123    pub fn name_lfm(&self) -> String {
124        if let Some(middle) = &self.middle_name {
125            format!("{2}, {0} {1}", self.first_name, middle, self.last_name)
126        } else {
127            format!("{1}, {0}", self.first_name, self.last_name)
128        }
129    }
130}
131
132#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
133#[serde(rename_all = "camelCase")]
134pub struct UnhydratedPerson {
135    pub id: PersonId,
136    pub full_name: String,
137}
138
139#[repr(transparent)]
140#[derive(Debug, Deref, Display, Deserialize, PartialEq, Eq, Clone)]
141pub struct PersonId(u32);
142
143#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
144#[serde(untagged)]
145pub enum Person {
146    Ballplayer(Ballplayer),
147    Hydrated(HydratedPerson),
148    Unhydrated(UnhydratedPerson),
149}
150
151impl Deref for Person {
152    type Target = UnhydratedPerson;
153
154    fn deref(&self) -> &Self::Target {
155        match self {
156            Self::Ballplayer(inner) => inner,
157            Self::Hydrated(inner) => inner,
158            Self::Unhydrated(inner) => inner,
159        }
160    }
161}
162
163impl DerefMut for Person {
164    fn deref_mut(&mut self) -> &mut Self::Target {
165        match self {
166            Self::Ballplayer(inner) => inner,
167            Self::Hydrated(inner) => inner,
168            Self::Unhydrated(inner) => inner,
169        }
170    }
171}