lastfm/
recent_tracks_page.rs1use crate::{error_response::ErrorResponse, track::Track};
5use serde::{de::Error, Deserialize, Deserializer, Serialize};
6use serde_json::Value;
7
8#[derive(Serialize, Deserialize)]
10#[serde(untagged)]
11pub enum RecentTracksResponse {
12 Error(ErrorResponse),
13 RecentTracksPage(RecentTracksPage),
14}
15
16#[derive(Serialize, Debug, Clone)]
18pub struct RecentTracksPage {
19 pub total_tracks: u64,
20 pub tracks: Vec<Track>,
21}
22
23impl<'de> Deserialize<'de> for RecentTracksPage {
24 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
25 where
26 D: Deserializer<'de>,
27 {
28 let raw_data: Value = Deserialize::deserialize(deserializer)?;
29
30 let raw_recent_tracks = raw_data
32 .get("recenttracks")
33 .ok_or_else(|| D::Error::missing_field("recenttracks"))?
34 .as_object()
35 .ok_or_else(|| D::Error::custom("Field recenttracks is not an object"))?;
36
37 let total_tracks = raw_recent_tracks
39 .get("@attr")
40 .ok_or_else(|| D::Error::missing_field("@attr"))?
41 .as_object()
42 .ok_or_else(|| D::Error::custom("Field @attr is not an object"))?
43 .get("total")
44 .ok_or_else(|| D::Error::missing_field("total"))?
45 .as_str()
46 .ok_or_else(|| D::Error::custom("Field total is not a string"))?
47 .parse::<u64>()
48 .map_err(|e| D::Error::custom(format!("Failed to parse total: {e}")))?;
49
50 let tracks = raw_recent_tracks
52 .get("track")
53 .ok_or_else(|| D::Error::missing_field("track"))?
54 .as_array()
55 .ok_or_else(|| D::Error::custom("Field track is not an array"))?
56 .iter()
57 .map(|t| {
58 serde_json::from_value::<Track>(t.clone())
59 .map_err(|e| D::Error::custom(format!("Cannot deserialize track: {e}")))
60 })
61 .collect::<Result<Vec<Track>, D::Error>>()?;
62
63 Ok(RecentTracksPage {
64 total_tracks,
65 tracks,
66 })
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn it_deserializes_a_recent_tracks_page() {
76 let json_data = include_str!("fixtures/recent_tracks_page.json");
77
78 let recent_tracks_page: RecentTracksPage = serde_json::from_str(json_data).unwrap();
79 insta::assert_debug_snapshot!(recent_tracks_page);
80 }
81}