1use serde::{Deserialize, Serialize};
4
5use super::album::AlbumSimplified;
6use super::artist::ArtistSimplified;
7use super::common::{ExternalIds, ExternalUrls, LinkedFrom, Restrictions};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct TrackSimplified {
12 pub artists: Option<Vec<ArtistSimplified>>,
14 pub available_markets: Option<Vec<String>>,
16 pub disc_number: Option<u32>,
18 pub duration_ms: u64,
20 pub explicit: Option<bool>,
22 pub external_urls: Option<ExternalUrls>,
24 pub href: Option<String>,
26 pub id: String,
28 pub is_playable: Option<bool>,
30 pub linked_from: Option<LinkedFrom>,
32 pub restrictions: Option<Restrictions>,
34 pub name: String,
36 pub preview_url: Option<String>,
38 pub track_number: Option<u32>,
40 #[serde(rename = "type")]
42 pub item_type: String,
43 pub uri: String,
45 pub is_local: Option<bool>,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct Track {
52 pub album: Option<AlbumSimplified>,
54 pub artists: Option<Vec<ArtistSimplified>>,
56 pub available_markets: Option<Vec<String>>,
58 pub disc_number: Option<u32>,
60 pub duration_ms: u64,
62 pub explicit: Option<bool>,
64 pub external_ids: Option<ExternalIds>,
66 pub external_urls: Option<ExternalUrls>,
68 pub href: Option<String>,
70 pub id: String,
72 pub is_playable: Option<bool>,
74 pub linked_from: Option<LinkedFrom>,
76 pub restrictions: Option<Restrictions>,
78 pub name: String,
80 pub popularity: Option<u32>,
82 pub preview_url: Option<String>,
84 pub track_number: Option<u32>,
86 #[serde(rename = "type")]
88 pub item_type: String,
89 pub uri: String,
91 pub is_local: Option<bool>,
93}
94
95impl Track {
96 pub fn artist_name(&self) -> Option<&str> {
98 self.artists
99 .as_ref()
100 .and_then(|artists| artists.first())
101 .map(|a| a.name.as_str())
102 }
103
104 pub fn artist_names(&self) -> String {
106 self.artists
107 .as_ref()
108 .map(|artists| {
109 artists
110 .iter()
111 .map(|a| a.name.as_str())
112 .collect::<Vec<_>>()
113 .join(", ")
114 })
115 .unwrap_or_default()
116 }
117
118 pub fn album_name(&self) -> Option<&str> {
120 self.album.as_ref().map(|a| a.name.as_str())
121 }
122
123 pub fn duration_str(&self) -> String {
125 let total_secs = self.duration_ms / 1000;
126 let mins = total_secs / 60;
127 let secs = total_secs % 60;
128 format!("{}:{:02}", mins, secs)
129 }
130
131 pub fn image_url(&self) -> Option<&str> {
133 self.album.as_ref().and_then(|a| a.image_url())
134 }
135}
136
137impl TrackSimplified {
138 pub fn artist_name(&self) -> Option<&str> {
140 self.artists
141 .as_ref()
142 .and_then(|artists| artists.first())
143 .map(|a| a.name.as_str())
144 }
145
146 pub fn duration_str(&self) -> String {
148 let total_secs = self.duration_ms / 1000;
149 let mins = total_secs / 60;
150 let secs = total_secs % 60;
151 format!("{}:{:02}", mins, secs)
152 }
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct SavedTrack {
158 pub added_at: String,
160 pub track: Track,
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use serde_json::json;
168
169 #[test]
170 fn track_simplified_deserializes() {
171 let json = json!({
172 "id": "track123",
173 "name": "Test Song",
174 "type": "track",
175 "uri": "spotify:track:track123",
176 "duration_ms": 210000,
177 "track_number": 5,
178 "disc_number": 1
179 });
180 let track: TrackSimplified = serde_json::from_value(json).unwrap();
181 assert_eq!(track.id, "track123");
182 assert_eq!(track.name, "Test Song");
183 assert_eq!(track.duration_ms, 210000);
184 }
185
186 #[test]
187 fn track_simplified_artist_name() {
188 let json = json!({
189 "id": "track123",
190 "name": "Test Song",
191 "type": "track",
192 "uri": "spotify:track:track123",
193 "duration_ms": 210000,
194 "artists": [{"id": "artist1", "name": "Test Artist", "type": "artist", "uri": "spotify:artist:artist1"}]
195 });
196 let track: TrackSimplified = serde_json::from_value(json).unwrap();
197 assert_eq!(track.artist_name(), Some("Test Artist"));
198 }
199
200 #[test]
201 fn track_simplified_artist_name_none() {
202 let json = json!({
203 "id": "track123",
204 "name": "Test Song",
205 "type": "track",
206 "uri": "spotify:track:track123",
207 "duration_ms": 210000
208 });
209 let track: TrackSimplified = serde_json::from_value(json).unwrap();
210 assert!(track.artist_name().is_none());
211 }
212
213 #[test]
214 fn track_simplified_duration_str() {
215 let json = json!({
216 "id": "track123",
217 "name": "Test Song",
218 "type": "track",
219 "uri": "spotify:track:track123",
220 "duration_ms": 210000 });
222 let track: TrackSimplified = serde_json::from_value(json).unwrap();
223 assert_eq!(track.duration_str(), "3:30");
224 }
225
226 #[test]
227 fn track_full_deserializes() {
228 let json = json!({
229 "id": "track123",
230 "name": "Test Song",
231 "type": "track",
232 "uri": "spotify:track:track123",
233 "duration_ms": 210000,
234 "popularity": 75,
235 "explicit": true
236 });
237 let track: Track = serde_json::from_value(json).unwrap();
238 assert_eq!(track.id, "track123");
239 assert_eq!(track.popularity, Some(75));
240 assert_eq!(track.explicit, Some(true));
241 }
242
243 #[test]
244 fn track_artist_name() {
245 let json = json!({
246 "id": "track123",
247 "name": "Test Song",
248 "type": "track",
249 "uri": "spotify:track:track123",
250 "duration_ms": 210000,
251 "artists": [{"id": "artist1", "name": "Primary Artist", "type": "artist", "uri": "spotify:artist:artist1"}]
252 });
253 let track: Track = serde_json::from_value(json).unwrap();
254 assert_eq!(track.artist_name(), Some("Primary Artist"));
255 }
256
257 #[test]
258 fn track_artist_names_multiple() {
259 let json = json!({
260 "id": "track123",
261 "name": "Test Song",
262 "type": "track",
263 "uri": "spotify:track:track123",
264 "duration_ms": 210000,
265 "artists": [
266 {"id": "artist1", "name": "Artist One", "type": "artist", "uri": "spotify:artist:artist1"},
267 {"id": "artist2", "name": "Artist Two", "type": "artist", "uri": "spotify:artist:artist2"}
268 ]
269 });
270 let track: Track = serde_json::from_value(json).unwrap();
271 assert_eq!(track.artist_names(), "Artist One, Artist Two");
272 }
273
274 #[test]
275 fn track_artist_names_empty() {
276 let json = json!({
277 "id": "track123",
278 "name": "Test Song",
279 "type": "track",
280 "uri": "spotify:track:track123",
281 "duration_ms": 210000
282 });
283 let track: Track = serde_json::from_value(json).unwrap();
284 assert_eq!(track.artist_names(), "");
285 }
286
287 #[test]
288 fn track_album_name() {
289 let json = json!({
290 "id": "track123",
291 "name": "Test Song",
292 "type": "track",
293 "uri": "spotify:track:track123",
294 "duration_ms": 210000,
295 "album": {"id": "album1", "name": "Test Album", "type": "album", "uri": "spotify:album:album1"}
296 });
297 let track: Track = serde_json::from_value(json).unwrap();
298 assert_eq!(track.album_name(), Some("Test Album"));
299 }
300
301 #[test]
302 fn track_album_name_none() {
303 let json = json!({
304 "id": "track123",
305 "name": "Test Song",
306 "type": "track",
307 "uri": "spotify:track:track123",
308 "duration_ms": 210000
309 });
310 let track: Track = serde_json::from_value(json).unwrap();
311 assert!(track.album_name().is_none());
312 }
313
314 #[test]
315 fn track_duration_str() {
316 let json = json!({
317 "id": "track123",
318 "name": "Test Song",
319 "type": "track",
320 "uri": "spotify:track:track123",
321 "duration_ms": 185000 });
323 let track: Track = serde_json::from_value(json).unwrap();
324 assert_eq!(track.duration_str(), "3:05");
325 }
326
327 #[test]
328 fn track_image_url() {
329 let json = json!({
330 "id": "track123",
331 "name": "Test Song",
332 "type": "track",
333 "uri": "spotify:track:track123",
334 "duration_ms": 210000,
335 "album": {
336 "id": "album1",
337 "name": "Test Album",
338 "type": "album",
339 "uri": "spotify:album:album1",
340 "images": [{"url": "https://cover.jpg", "height": 640, "width": 640}]
341 }
342 });
343 let track: Track = serde_json::from_value(json).unwrap();
344 assert_eq!(track.image_url(), Some("https://cover.jpg"));
345 }
346
347 #[test]
348 fn saved_track_deserializes() {
349 let json = json!({
350 "added_at": "2024-01-15T10:30:00Z",
351 "track": {
352 "id": "track123",
353 "name": "Test Song",
354 "type": "track",
355 "uri": "spotify:track:track123",
356 "duration_ms": 210000
357 }
358 });
359 let saved: SavedTrack = serde_json::from_value(json).unwrap();
360 assert_eq!(saved.added_at, "2024-01-15T10:30:00Z");
361 assert_eq!(saved.track.id, "track123");
362 }
363}