mlb_api/endpoints/teams/team/roster/
mod.rs1
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