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
//! Various error types exposed by the crate.
use std::borrow::Cow;
#[cfg(any(feature = "async", feature = "sync"))]
use std::convert::Infallible;
use thiserror::Error;
use crate::model::ItemType;
#[cfg(any(feature = "async", feature = "sync"))]
use crate::model::{
error::AuthenticationErrorKind,
id::{AlbumId, ArtistId, EpisodeId, Id, PlaylistId, ShowId, TrackId},
};
/// The result type the library returns in the public-facing interface.
#[cfg(any(feature = "async", feature = "sync"))]
pub type Result<T> = std::result::Result<T, Error>;
/// Covers all errors the library may return.
#[cfg(any(feature = "async", feature = "sync"))]
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Error {
/// When attempting to finalize an
/// [AuthorizationCodeUserClient](crate::client::authorization_code::AuthorizationCodeUserClient), the
/// given state string does not match the original one in the client.
#[error("The given state does not match the original state")]
AuthorizationCodeStateMismatch,
/// When attempting to finalize an
/// [AuthorizationCodeUserClient](crate::client::authorization_code::AuthorizationCodeUserClient), the
/// given authorization code is invalid.
#[error("The authorization code is invalid")]
InvalidAuthorizationCode,
/// The access token expired and was not automatically refreshed, due to automatic token refreshind being disabled
/// or it being impossible ([ImplicitGrantUserClient](crate::client::implicit_grant::ImplicitGrantUserClient)
/// does not support refreshing its access token).
#[error("The access token expired")]
AccessTokenExpired,
/// The refresh token is invalid; it cannot be used to retrieve an access token. This is likely due to the user
/// removing the application's access to their account. The error message from Spotify is included. The user should
/// be reauthorized.
#[error("The refresh token is invalid: {0}")]
InvalidRefreshToken(String),
/// The client credentails (ID and possible secret) are invalid.
#[error("The client ID and/or secret is invalid")]
InvalidClient(String),
/// Request rate limit was hit. The required wait time is included.
#[error("Request rate limit hit; retry after {0} seconds")]
RateLimit(u64),
/// The required scope for the endpoint being called has not been granted by the user.
#[error("The required scope for the endpoint has not been granted by the user")]
MissingScope,
/// The endpoint is forbidden and its possible error message body couldn't be mapped to a more specific error.
///
/// This could be due to the user removing the application's access to their account. The user should be
/// reauthorized.
#[error("The endpoint is forbidden")]
Forbidden,
/// The player control is restricted.
///
/// [CurrentlyPlayingItem](crate::model::playback::CurrentlyPlayingItem) has an
/// [`actions`-function](crate::model::playback::CurrentlyPlayingItem::actions) that has a
/// [`disallows`-field](crate::model::playback::Actions::disallows) that contains information about which player
/// controls are disallowed for the current playback.
#[error("The player control is restricted")]
Restricted,
/// A player control failed because the target user does not have a Spotify Premium account.
#[error("A Spotify Premium account is required")]
PremiumRequired,
/// No device is currently active in the user's account, the active device didn't respond to the playback in a \
/// timely manner or the given device could not be activated for playback.
///
/// It is still possible the playback control starts after this error if, for example, the active device is slow to
/// respond to playback requests (e.g. smartphones).
#[error(
"No device is currently active in the user's account, the active device didn't respond to the playback in a \
timely manner or the given device could not be activated for playback"
)]
NoActiveDevice,
/// The given track ID doesn't refer to any Spotify track.
#[error("Nonexistent track ID: {0}")]
NonexistentTrack(Id<'static, TrackId>),
/// The given album ID doesn't refer to any Spotify album.
#[error("Nonexistent album ID: {0}")]
NonexistentAlbum(Id<'static, AlbumId>),
/// The given track ID doesn't refer to any Spotify track.
#[error("Nonexistent artist ID: {0}")]
NonexistentArtist(Id<'static, ArtistId>),
/// The given playlist ID doesn't refer to any Spotify playlist.
#[error("Nonexistent playlist ID: {0}")]
NonexistentPlaylist(Id<'static, PlaylistId>),
/// The given show ID doesn't refer to any Spotify show.
#[error("Nonexistent show ID: {0}")]
NonexistentShow(Id<'static, ShowId>),
/// The given episode ID doesn't refer to any Spotify episode.
#[error("Nonexistent episode ID: {0}")]
NonexistentEpisode(Id<'static, EpisodeId>),
/// Spotify returned a 429 Too Many Requests, but the Retry-After header could not be parsed as an integer. This is
/// likely an issue on Spotify's side.
#[error("Missing or invalid Retry-After header in 429 rate-limit response")]
InvalidRateLimitResponse,
/// Spotify returned an authentication error we did not expect.
#[error("Unhandled authentication error: {0:?}: {1}")]
UnhandledAuthenticationError(AuthenticationErrorKind, String),
/// Spotify returned an response status code we did not expect.
#[error("Unhandled Spotify API response status code {0}")]
UnhandledSpotifyResponseStatusCode(u16),
/// Spotify returned an unexpected empty response (HTTP 204 No Content)
#[error("Spotify returned an unexpected empty response (HTTP 204 No Content)")]
EmptyResponse,
/// Parsing a string to a Spotify [ID](crate::model::id::Id) failed.
#[error(transparent)]
InvalidSpotifyId(#[from] IdError),
/// Converting a Spotify API response JSON into a model object failed.
///
/// If the library returns this error from a standard Spotify API function call, it means there is a mismatch
/// between Spotify's API response and the library's object model.
#[error(transparent)]
Conversion(#[from] ConversionError),
/// A catch-all for errors from reqwest. Getting this error back likely means something went wrong with sending a
/// request or receiving and decoding a response.
#[error(transparent)]
HttpError(#[from] reqwest::Error),
}
/// Error type for parsing a Spotify [ID](crate::model::id::Id).
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum IdError {
/// The item type in the input is not one of known Spotify [item types](crate::model::ItemType), or the item type
/// is not applicable for the scenario.
#[error("Invalid item type: {0}")]
InvalidItemType(String),
/// The item type in the input is not the expected type for the target type.
#[error("Wrong item type in ID ({0:?})")]
WrongItemType(ItemType),
/// The ID in the input does not look like a valid Spotify ID. The ID may still be nonexistent in Spotify even if
/// it looks valid.
#[error("Invalid ID: {0}")]
InvalidId(String),
/// The input string is malformed.
#[error("Malformed string: {0}")]
MalformedString(String),
}
/// Error when converting serialized objects into model objects fails.
#[derive(Debug)]
#[non_exhaustive]
pub struct ConversionError(pub(crate) Cow<'static, str>);
impl std::error::Error for ConversionError {}
impl std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "object conversion failed: {}", self.0)
}
}
#[cfg(any(feature = "async", feature = "sync"))]
impl From<Infallible> for Error {
fn from(_: Infallible) -> Self {
panic!("how did you manage to try and convert a type that could never exist into something that does")
}
}