1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
//! The Spotify [Object
//! Model](https://developer.spotify.com/documentation/web-api/reference/object-model/), in
//! deserializable Rust structures.
use serde::{Deserialize, Serialize};
pub use album::*;
pub use analysis::*;
pub use artist::*;
pub use consts::*;
pub use device::*;
pub use errors::*;
pub use playlist::*;
pub use show::*;
pub use track::*;
pub use user::*;
macro_rules! to_struct {
($(#[$attr:meta])* $name:ident { $($(#[$f_attr:meta])* $f_name:ident : $f_ty:ty,)* }) => {
$(#[$attr])*
#[derive(Debug, Clone, PartialEq, Eq, ::serde::Serialize, ::serde::Deserialize)]
pub struct $name {
$(
$(#[$f_attr])*
pub $f_name: $f_ty,
)*
}
}
}
mod album;
mod analysis;
mod artist;
mod device;
mod errors;
mod playlist;
mod show;
mod track;
mod user;
/// Serialization and deserialization constants.
pub mod consts {
use std::fmt::{self, Formatter};
use serde::de::{self, Deserialize, Deserializer, Visitor};
use serde::ser::{Serialize, Serializer};
macro_rules! serde_string_consts {
($($name:ident = $value:literal $svalue:expr,)*) => {
$(
#[doc = "The string `"]
#[doc = $svalue]
#[doc = "`."]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct $name;
impl<'de> Deserialize<'de> for $name {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct ValueVisitor;
impl<'de> Visitor<'de> for ValueVisitor {
type Value = $name;
fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(concat!("the string ", $svalue))
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
if v != $value {
return Err(E::invalid_value(de::Unexpected::Str(v), &self));
}
Ok($name)
}
}
deserializer.deserialize_str(ValueVisitor)
}
}
impl Serialize for $name {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str($value)
}
}
)*
};
($($name:ident = $value:literal,)*) => {
serde_string_consts!($($name = $value stringify!($value),)*);
};
}
serde_string_consts! {
TypeAlbum = "album",
TypeArtist = "artist",
TypeAudioFeatures = "audio_features",
TypeEpisode = "episode",
TypePlaylist = "playlist",
TypeShow = "show",
TypeTrack = "track",
TypeUser = "user",
}
}
/// A category of music, for example "Mood", "Top Lists", "Workout", et cetera.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Category {
/// The category icon, in various sizes, probably with widest first (although this is not
/// specified by the Web API documentation).
pub icons: Vec<Image>,
/// The [Spotify ID](https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids)
/// for the category.
pub id: String,
/// The name of the category.
pub name: String,
}
/// The copyright information for a resource.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Copyright {
/// The copyright text.
pub text: String,
/// Whether the copyright is for the performance of the piece, not the piece.
#[serde(rename = "type", with = "serde_is_p")]
pub performance_copyright: bool,
}
mod serde_is_p {
use serde::{
de::{self, Visitor},
Deserializer, Serializer,
};
use std::fmt::{self, Formatter};
pub fn deserialize<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
struct CopyrightType;
impl<'de> Visitor<'de> for CopyrightType {
type Value = bool;
fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("P or C")
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
match s {
"P" => Ok(true),
"C" => Ok(false),
_ => Err(de::Error::invalid_value(de::Unexpected::Str(s), &self)),
}
}
}
deserializer.deserialize_str(CopyrightType)
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn serialize<S: Serializer>(v: &bool, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(if *v { "P" } else { "C" })
}
}
/// Information about the followers of an item. Currently only contains the number of followers.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Followers {
/// The total number of followers.
pub total: usize,
}
/// An image with a URL and an optional width and height.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Image {
/// The source URL of the image.
pub url: String,
/// The height of the image in pixels, if known.
pub height: Option<usize>,
/// The width of the image in pixels, if known.
pub width: Option<usize>,
}
/// A page of items.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Page<T> {
/// The items in the page.
pub items: Vec<T>,
/// The maximum number of items in the page, as set by the request or a default value.
pub limit: usize,
/// The offset of the page in the items.
pub offset: usize,
/// The total number of items.
pub total: usize,
}
/// A page of items, using a cursor to find the next page.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CursorPage<T> {
/// The items in the page.
pub items: Vec<T>,
/// The maximum number of items in the page, as set by the request or a default value.
pub limit: usize,
/// The cursor used to find the next set of items.
pub cursors: Cursor,
/// The total number of items.
pub total: usize,
}
/// Object that contains the next `CursorPage`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Cursor {
/// The cursor page after this one.
pub after: Option<String>,
}
/// A page of items, using a cursor to move backwards and forwards.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TwoWayCursorPage<T> {
/// The items in the page.
pub items: Vec<T>,
/// The maximum number of items in the page, as set by the request or a default value.
pub limit: usize,
/// The cursor used to find the next set of items.
pub cursors: TwoWayCursor,
}
/// Object that contains the next and previous [`CursorPage`].
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TwoWayCursor {
/// The cursor page after this one.
pub after: Option<String>,
/// The cursor page before this one.
pub before: Option<String>,
}
/// Recommended tracks for the user.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Recommendations {
/// An array of recommendation seeds.
pub seeds: Vec<RecommendationSeed>,
/// An array of simplified track objects.
pub tracks: Vec<TrackSimplified>,
}
/// How the recommendation was chosen.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RecommendationSeed {
/// The number of tracks available after min_* and max_* filters have been applied.
pub after_filtering_size: usize,
/// The number of tracks available after relinking for regional availability.
pub after_relinking_size: usize,
/// The id used to select this seed, given by the user.
pub id: String,
/// The number of recommended tracks available for this seed.
pub initial_pool_size: usize,
/// The entity type of this seed; [Artist](SeedType::Artist), [Track](SeedType::Track) or
/// [Genre](SeedType::Genre).
#[serde(rename = "type")]
pub entity_type: SeedType,
}
/// The context from which the recommendation was chosen; artist, track or genre.
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[allow(missing_docs)]
pub enum SeedType {
Artist,
Track,
Genre,
}
/// How precise a date measurement is.
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DatePrecision {
/// The measurement is precise to the nearest year.
Year,
/// The measurement is precise to the nearest month.
Month,
/// The measurement is precise to the nearest day.
Day,
}
/// Restrictions applied to a track due to markets.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Restrictions {
/// Why the restriction was applied.
pub reason: String,
}
/// A type of item in the Spotify model.
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[allow(missing_docs)]
pub enum ItemType {
Album,
Artist,
Playlist,
Track,
Show,
Episode,
}
impl ItemType {
/// The type of item as a lowercase string.
///
/// ```
/// assert_eq!(aspotify::ItemType::Episode.as_str(), "episode");
/// ```
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Album => "album",
Self::Artist => "artist",
Self::Playlist => "playlist",
Self::Track => "track",
Self::Show => "show",
Self::Episode => "episode",
}
}
}
/// The results of a search.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SearchResults {
/// The resulting artists of the search.
pub artists: Option<Page<Artist>>,
/// The resulting albums of the search.
pub albums: Option<Page<AlbumSimplified>>,
/// The resulting tracks of the search.
pub tracks: Option<Page<Track>>,
/// The resulting playlists of the search.
pub playlists: Option<Page<PlaylistSimplified>>,
/// The resulting shows of the search.
pub shows: Option<Page<ShowSimplified>>,
/// The resulting episodes of the search.
pub episodes: Option<Page<EpisodeSimplified>>,
}