nhl_api/types/
common.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4use super::enums::{Handedness, Position};
5
6/// Localized string (NHL API returns {default: "value"})
7#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
8pub struct LocalizedString {
9    pub default: String,
10}
11
12/// Conference information for a team
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14pub struct Conference {
15    pub abbr: String,
16    pub name: String,
17}
18
19/// Division information for a team
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
21pub struct Division {
22    pub abbr: String,
23    pub name: String,
24}
25
26/// NHL Team information
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
28pub struct Team {
29    pub name: String,
30    pub common_name: String,
31    pub abbr: String,
32    pub logo: String,
33    pub conference: Conference,
34    pub division: Division,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub franchise_id: Option<i64>,
37}
38
39impl fmt::Display for Team {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        write!(f, "{} ({})", self.name, self.abbr)
42    }
43}
44
45/// Franchise information
46#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
47pub struct Franchise {
48    pub id: i32,
49    #[serde(rename = "fullName")]
50    pub full_name: String,
51    #[serde(rename = "teamCommonName")]
52    pub team_common_name: String,
53    #[serde(rename = "teamPlaceName")]
54    pub team_place_name: String,
55}
56
57impl fmt::Display for Franchise {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        write!(f, "{} (ID: {})", self.full_name, self.id)
60    }
61}
62
63/// Response from the franchises endpoint
64#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct FranchisesResponse {
66    pub data: Vec<Franchise>,
67}
68
69/// Team roster information
70/// Team roster with players by position
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct Roster {
73    #[serde(default)]
74    pub forwards: Vec<RosterPlayer>,
75    #[serde(default)]
76    pub defensemen: Vec<RosterPlayer>,
77    #[serde(default)]
78    pub goalies: Vec<RosterPlayer>,
79}
80
81/// Individual player in a team roster
82#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
83pub struct RosterPlayer {
84    pub id: i64,
85    pub headshot: String,
86    #[serde(rename = "firstName")]
87    pub first_name: LocalizedString,
88    #[serde(rename = "lastName")]
89    pub last_name: LocalizedString,
90    #[serde(rename = "sweaterNumber")]
91    pub sweater_number: i32,
92    #[serde(rename = "positionCode")]
93    pub position: Position,
94    #[serde(rename = "shootsCatches")]
95    pub shoots_catches: Handedness,
96    #[serde(rename = "heightInInches")]
97    pub height_in_inches: i32,
98    #[serde(rename = "weightInPounds")]
99    pub weight_in_pounds: i32,
100    #[serde(rename = "heightInCentimeters")]
101    pub height_in_centimeters: i32,
102    #[serde(rename = "weightInKilograms")]
103    pub weight_in_kilograms: i32,
104    #[serde(rename = "birthDate")]
105    pub birth_date: String,
106    #[serde(rename = "birthCity")]
107    pub birth_city: LocalizedString,
108    #[serde(rename = "birthCountry")]
109    pub birth_country: String,
110    #[serde(skip_serializing_if = "Option::is_none")]
111    #[serde(rename = "birthStateProvince")]
112    pub birth_state_province: Option<LocalizedString>,
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_team_serialization() {
121        let team = Team {
122            name: "Buffalo Sabres".to_string(),
123            common_name: "Sabres".to_string(),
124            abbr: "BUF".to_string(),
125            logo: "https://assets.nhle.com/logos/nhl/svg/BUF_light.svg".to_string(),
126            conference: Conference {
127                abbr: "E".to_string(),
128                name: "Eastern".to_string(),
129            },
130            division: Division {
131                abbr: "ATL".to_string(),
132                name: "Atlantic".to_string(),
133            },
134            franchise_id: Some(19),
135        };
136
137        let json = serde_json::to_string(&team).unwrap();
138        let deserialized: Team = serde_json::from_str(&json).unwrap();
139
140        assert_eq!(deserialized.name, "Buffalo Sabres");
141        assert_eq!(deserialized.abbr, "BUF");
142        assert_eq!(deserialized.franchise_id, Some(19));
143    }
144
145    #[test]
146    fn test_franchise_deserialization() {
147        let json = r#"{
148            "id": 32,
149            "fullName": "Anaheim Ducks",
150            "teamCommonName": "Ducks",
151            "teamPlaceName": "Anaheim"
152        }"#;
153
154        let franchise: Franchise = serde_json::from_str(json).unwrap();
155        assert_eq!(franchise.id, 32);
156        assert_eq!(franchise.full_name, "Anaheim Ducks");
157        assert_eq!(franchise.team_common_name, "Ducks");
158        assert_eq!(franchise.team_place_name, "Anaheim");
159    }
160
161    #[test]
162    fn test_franchise_display() {
163        let franchise = Franchise {
164            id: 16,
165            full_name: "Philadelphia Flyers".to_string(),
166            team_common_name: "Flyers".to_string(),
167            team_place_name: "Philadelphia".to_string(),
168        };
169
170        assert_eq!(format!("{}", franchise), "Philadelphia Flyers (ID: 16)");
171    }
172
173    #[test]
174    fn test_franchise_response_deserialization() {
175        let json = r#"{
176            "data": [
177                {
178                    "id": 32,
179                    "fullName": "Anaheim Ducks",
180                    "teamCommonName": "Ducks",
181                    "teamPlaceName": "Anaheim"
182                },
183                {
184                    "id": 8,
185                    "fullName": "Brooklyn Americans",
186                    "teamCommonName": "Americans",
187                    "teamPlaceName": "Brooklyn"
188                }
189            ]
190        }"#;
191
192        let response: FranchisesResponse = serde_json::from_str(json).unwrap();
193        assert_eq!(response.data.len(), 2);
194        assert_eq!(response.data[0].id, 32);
195        assert_eq!(response.data[0].full_name, "Anaheim Ducks");
196        assert_eq!(response.data[1].id, 8);
197        assert_eq!(response.data[1].full_name, "Brooklyn Americans");
198    }
199
200    #[test]
201    fn test_franchise_clone() {
202        let franchise = Franchise {
203            id: 1,
204            full_name: "Montreal Canadiens".to_string(),
205            team_common_name: "Canadiens".to_string(),
206            team_place_name: "Montreal".to_string(),
207        };
208
209        let cloned = franchise.clone();
210        assert_eq!(franchise, cloned);
211    }
212
213    #[test]
214    fn test_franchise_equality() {
215        let franchise1 = Franchise {
216            id: 1,
217            full_name: "Montreal Canadiens".to_string(),
218            team_common_name: "Canadiens".to_string(),
219            team_place_name: "Montreal".to_string(),
220        };
221
222        let franchise2 = Franchise {
223            id: 1,
224            full_name: "Montreal Canadiens".to_string(),
225            team_common_name: "Canadiens".to_string(),
226            team_place_name: "Montreal".to_string(),
227        };
228
229        assert_eq!(franchise1, franchise2);
230    }
231
232    #[test]
233    fn test_franchise_inequality() {
234        let franchise1 = Franchise {
235            id: 1,
236            full_name: "Montreal Canadiens".to_string(),
237            team_common_name: "Canadiens".to_string(),
238            team_place_name: "Montreal".to_string(),
239        };
240
241        let franchise2 = Franchise {
242            id: 6,
243            full_name: "Boston Bruins".to_string(),
244            team_common_name: "Bruins".to_string(),
245            team_place_name: "Boston".to_string(),
246        };
247
248        assert_ne!(franchise1, franchise2);
249    }
250
251    #[test]
252    fn test_franchise_empty_response() {
253        let json = r#"{"data": []}"#;
254        let response: FranchisesResponse = serde_json::from_str(json).unwrap();
255        assert_eq!(response.data.len(), 0);
256    }
257
258    #[test]
259    fn test_team_display() {
260        let team = Team {
261            name: "Buffalo Sabres".to_string(),
262            common_name: "Sabres".to_string(),
263            abbr: "BUF".to_string(),
264            logo: "https://assets.nhle.com/logos/nhl/svg/BUF_light.svg".to_string(),
265            conference: Conference {
266                abbr: "E".to_string(),
267                name: "Eastern".to_string(),
268            },
269            division: Division {
270                abbr: "ATL".to_string(),
271                name: "Atlantic".to_string(),
272            },
273            franchise_id: Some(19),
274        };
275
276        assert_eq!(team.to_string(), "Buffalo Sabres (BUF)");
277    }
278}