mlb_api/endpoints/teams/team/roster/
mod.rs

1
2use crate::endpoints::{Position, RosterTypeId, StatsAPIUrl};
3use crate::endpoints::teams::team::TeamId;
4use crate::gen_params;
5use std::fmt::{Display, Formatter};
6use chrono::NaiveDate;
7use serde::Deserialize;
8use crate::endpoints::person::Person;
9use crate::types::{Copyright, MLB_API_DATE_FORMAT};
10
11#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
12#[serde(rename_all = "camelCase")]
13pub struct RosterResponse {
14    pub copyright: Copyright,
15    #[serde(default)]
16    pub roster: Vec<RosterPlayer>,
17    pub team_id: TeamId,
18    pub roster_type: RosterTypeId,
19}
20
21#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
22#[serde(rename_all = "camelCase")]
23pub struct RosterPlayer {
24    pub person: Person,
25    #[serde(deserialize_with = "crate::types::try_from_str")]
26    pub jersey_number: Option<u8>,
27    pub position: Position,
28    pub status: RosterStatus,
29    pub parent_team_id: Option<TeamId>,
30}
31
32#[derive(Debug, Deserialize, PartialEq, Eq, Copy, Clone)]
33#[serde(try_from = "__RosterStatusStruct")]
34pub enum RosterStatus {
35    Active,
36    Claimed,
37    ReassignedToMinors,
38    Released,
39    MinorLeagueContract,
40    InjuryLeave7Day,
41    InjuryLeave10Day,
42    InjuryLeave15Day,
43    InjuryLeave60Day,
44    Traded,
45    DesignatedForAssignment,
46    FreeAgent,
47    RestrictedList,
48}
49
50#[derive(Deserialize)]
51struct __RosterStatusStruct {
52    code: String,
53    description: String,
54}
55
56impl TryFrom<__RosterStatusStruct> for RosterStatus {
57    type Error = String;
58
59    fn try_from(value: __RosterStatusStruct) -> Result<Self, Self::Error> {
60        Ok(match &*value.code {
61            "A" => RosterStatus::Active,
62            "CL" => RosterStatus::Claimed,
63            "RM" => RosterStatus::ReassignedToMinors,
64            "RL" => RosterStatus::Released,
65            "MIN" => RosterStatus::MinorLeagueContract,
66            "D7" => RosterStatus::InjuryLeave10Day,
67            "D10" => RosterStatus::InjuryLeave10Day,
68            "D15" => RosterStatus::InjuryLeave15Day,
69            "D60" => RosterStatus::InjuryLeave60Day,
70            "TR" => RosterStatus::Traded,
71            "DES" => RosterStatus::DesignatedForAssignment,
72            "FA" => RosterStatus::FreeAgent,
73            "RST" => RosterStatus::RestrictedList,
74            code => return Err(format!("Invalid code '{code}' (desc: {})", value.description)),
75        })
76    }
77}
78
79pub struct RosterEndpointUrl {
80    team_id: TeamId,
81    season: Option<u16>,
82    date: Option<NaiveDate>,
83    roster_type: RosterTypeId,
84}
85
86impl Display for RosterEndpointUrl {
87    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
88        write!(f, "http://statsapi.mlb.com/api/v1/teams/{}/roster{}", self.team_id, gen_params! { "season"?: self.season, "date"?: self.date.as_ref().map(|date| date.format(MLB_API_DATE_FORMAT)), "rosterType": &self.roster_type })
89    }
90}
91
92impl StatsAPIUrl for RosterEndpointUrl {
93    type Response = RosterResponse;
94}
95
96#[cfg(test)]
97mod tests {
98    use crate::endpoints::{RosterType, StatsAPIUrl};
99    use crate::endpoints::teams::TeamsEndpointUrl;
100    use crate::endpoints::teams::team::roster::RosterEndpointUrl;
101    use chrono::{Datelike, Local};
102    use crate::endpoints::meta::MetaEndpointUrl;
103    use crate::endpoints::sports::SportId;
104
105    #[tokio::test]
106    #[cfg_attr(not(feature = "_heavy_tests"), ignore)]
107    async fn test_this_year_all_mlb_teams_all_roster_types() {
108        let season = Local::now().year() as _;
109        let teams = TeamsEndpointUrl { sport_id: Some(SportId::MLB), season: Some(season) }.get().await.unwrap().teams;
110        let roster_types = MetaEndpointUrl::<RosterType>::new().get().await.unwrap().entries;
111        for team in teams {
112            for roster_type in &roster_types {
113                let json = reqwest::get(RosterEndpointUrl { team_id: team.id, season: Some(season), date: None, roster_type: roster_type.id.clone() }.to_string()).await.unwrap().bytes().await.unwrap();
114                let mut de = serde_json::Deserializer::from_slice(&json);
115                let result: Result<<RosterEndpointUrl as StatsAPIUrl>::Response, serde_path_to_error::Error<serde_json::Error>> = serde_path_to_error::deserialize(&mut de);
116                match result {
117                    Ok(_) => {}
118                    Err(e) if format!("{:?}", e.inner()).contains("missing field `copyright`") => {}
119                    Err(e) => panic!("Err: {:?}", e),
120                }
121            }
122        }
123    }
124}
125