1use crate::{Followers, Image, Page, PlayableItem, PlaylistId, PublicUser};
4use chrono::prelude::*;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
10pub struct PlaylistResult {
11 pub snapshot_id: String,
12}
13
14#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
16pub struct PlaylistTracksRef {
17 pub href: String,
18 pub total: u32,
19}
20
21fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
22where
23 T: Default + serde::Deserialize<'de>,
24 D: serde::Deserializer<'de>,
25{
26 Ok(Option::deserialize(deserializer)?.unwrap_or_default())
27}
28
29#[derive(Deserialize)]
30struct SimplifiedPlaylistShadow {
31 pub collaborative: bool,
32 pub external_urls: HashMap<String, String>,
33 pub href: String,
34 pub id: PlaylistId<'static>,
35 #[serde(deserialize_with = "deserialize_null_default")]
36 pub images: Vec<Image>,
37 pub name: String,
38 pub owner: PublicUser,
39 pub public: Option<bool>,
40 pub snapshot_id: String,
41 #[serde(default)]
42 pub items: Option<PlaylistTracksRef>,
43 #[serde(default)]
44 pub tracks: Option<PlaylistTracksRef>,
45}
46
47#[allow(deprecated)]
48impl From<SimplifiedPlaylistShadow> for SimplifiedPlaylist {
49 fn from(shadow: SimplifiedPlaylistShadow) -> Self {
50 let items = shadow
51 .items
52 .or(shadow.tracks)
53 .expect("missing items/tracks");
54 Self {
55 collaborative: shadow.collaborative,
56 external_urls: shadow.external_urls,
57 href: shadow.href,
58 id: shadow.id,
59 images: shadow.images,
60 name: shadow.name,
61 owner: shadow.owner,
62 public: shadow.public,
63 snapshot_id: shadow.snapshot_id,
64 tracks: items.clone(),
65 items,
66 }
67 }
68}
69
70#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
78#[serde(from = "SimplifiedPlaylistShadow")]
79pub struct SimplifiedPlaylist {
80 pub collaborative: bool,
81 pub external_urls: HashMap<String, String>,
82 pub href: String,
83 pub id: PlaylistId<'static>,
84 #[serde(deserialize_with = "deserialize_null_default")]
85 pub images: Vec<Image>,
86 pub name: String,
87 pub owner: PublicUser,
88 pub public: Option<bool>,
89 pub snapshot_id: String,
90 #[deprecated(
94 since = "0.16.0",
95 note = "Renamed to `items` by Spotify. Use `items` instead. See https://github.com/ramsayleung/rspotify/issues/550"
96 )]
97 pub tracks: PlaylistTracksRef,
98 pub items: PlaylistTracksRef,
99}
100
101#[derive(Deserialize)]
102struct FullPlaylistShadow {
103 pub collaborative: bool,
104 pub description: Option<String>,
105 pub external_urls: HashMap<String, String>,
106 pub followers: Followers,
107 pub href: String,
108 pub id: PlaylistId<'static>,
109 #[serde(deserialize_with = "deserialize_null_default")]
110 pub images: Vec<Image>,
111 pub name: String,
112 pub owner: PublicUser,
113 pub public: Option<bool>,
114 pub snapshot_id: String,
115 #[serde(default)]
116 pub items: Option<Page<PlaylistItem>>,
117 #[serde(default)]
118 pub tracks: Option<Page<PlaylistItem>>,
119}
120
121#[allow(deprecated)]
122impl From<FullPlaylistShadow> for FullPlaylist {
123 fn from(shadow: FullPlaylistShadow) -> Self {
124 let items = shadow
125 .items
126 .or(shadow.tracks)
127 .expect("missing items/tracks");
128 Self {
129 collaborative: shadow.collaborative,
130 description: shadow.description,
131 external_urls: shadow.external_urls,
132 followers: shadow.followers,
133 href: shadow.href,
134 id: shadow.id,
135 images: shadow.images,
136 name: shadow.name,
137 owner: shadow.owner,
138 public: shadow.public,
139 snapshot_id: shadow.snapshot_id,
140 tracks: items.clone(),
141 items,
142 }
143 }
144}
145
146#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
154#[serde(from = "FullPlaylistShadow")]
155pub struct FullPlaylist {
156 pub collaborative: bool,
157 pub description: Option<String>,
158 pub external_urls: HashMap<String, String>,
159 pub followers: Followers,
160 pub href: String,
161 pub id: PlaylistId<'static>,
162 #[serde(deserialize_with = "deserialize_null_default")]
163 pub images: Vec<Image>,
164 pub name: String,
165 pub owner: PublicUser,
166 pub public: Option<bool>,
167 pub snapshot_id: String,
168 #[deprecated(
172 since = "0.16.0",
173 note = "Renamed to `items` by Spotify. Use `items` instead. See https://github.com/ramsayleung/rspotify/issues/550"
174 )]
175 pub tracks: Page<PlaylistItem>,
176 pub items: Page<PlaylistItem>,
177}
178
179#[derive(Deserialize)]
180struct PlaylistItemShadow {
181 pub added_at: Option<DateTime<Utc>>,
182 pub added_by: Option<PublicUser>,
183 pub is_local: bool,
184 #[serde(default)]
185 pub item: Option<PlayableItem>,
186 #[serde(default)]
187 pub track: Option<PlayableItem>,
188}
189
190#[allow(deprecated)]
191impl From<PlaylistItemShadow> for PlaylistItem {
192 fn from(shadow: PlaylistItemShadow) -> Self {
193 let item = shadow.item.or(shadow.track);
194 Self {
195 added_at: shadow.added_at,
196 added_by: shadow.added_by,
197 is_local: shadow.is_local,
198 track: item.clone(),
199 item,
200 }
201 }
202}
203
204#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
212#[serde(from = "PlaylistItemShadow")]
213pub struct PlaylistItem {
214 pub added_at: Option<DateTime<Utc>>,
215 pub added_by: Option<PublicUser>,
216 pub is_local: bool,
217 #[deprecated(
221 since = "0.16.0",
222 note = "Renamed to `item` by Spotify. Use `item` instead. See https://github.com/ramsayleung/rspotify/issues/550"
223 )]
224 pub track: Option<PlayableItem>,
225 pub item: Option<PlayableItem>,
226}
227
228#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
230pub struct FeaturedPlaylists {
231 pub message: String,
232 pub playlists: Page<SimplifiedPlaylist>,
233}
234
235#[derive(Deserialize)]
237pub struct CategoryPlaylists {
238 pub playlists: Page<SimplifiedPlaylist>,
239}