1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4use super::enums::{Handedness, Position};
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
8pub struct LocalizedString {
9 pub default: String,
10}
11
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14pub struct Conference {
15 pub abbr: String,
16 pub name: String,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
21pub struct Division {
22 pub abbr: String,
23 pub name: String,
24}
25
26#[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#[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#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct FranchisesResponse {
66 pub data: Vec<Franchise>,
67}
68
69#[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#[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}