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
/*!
Error codes that can be returned by the Pandora API.
*/
// SPDX-License-Identifier: MIT AND WTFPL

/// https://6xq.net/pandora-apidoc/json/errorcodes/
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum JsonErrorKind {
    /// Code 0 - Internal error. It can denote that your account has been temporarily blocked due to having
    /// too frequent station.getPlaylist calls.
    InternalError,
    /// Code 1 - MAINTENANCE_MODE
    MaintenanceMode,
    /// Code 2 - URL_PARAM_MISSING_METHOD
    UrlParamMissingMethod,
    /// Code 3 - URL_PARAM_MISSING_AUTH_TOKEN
    UrlParamMissingAuthToken,
    /// Code 4 - URL_PARAM_MISSING_PARTNER_ID
    UrlParamMissingPartnerId,
    /// Code 5 - URL_PARAM_MISSING_USER_ID
    UrlParamMissingUserId,
    /// Code 6 - SECURE_PROTOCOL_REQUIRED
    SecureProtocolRequired,
    /// Code 7 - CERTIFICATE_REQUIRED
    CertificateRequired,
    /// Code 8 - PARAMETER_TYPE_MISMATCH
    ParameterTypeMismatch,
    /// Code 9 - PARAMETER_MISSING. Usually occurs when one or more required parameters are missing
    /// for the method called.
    ParameterMissing,
    /// Code 10 - PARAMETER_VALUE_INVALID
    ParameterValueInvalid,
    /// Code 11 - API_VERSION_NOT_SUPPORTED
    ApiVersionNotSupported,
    /// Code 12 - LICENSING_RESTRICTIONS. Pandora not available in this country.
    LicensingRestrictions,
    /// Code 13 - INSUFFICIENT_CONNECTIVITY. Bad sync time?
    InsufficientConnectivity,
    /// Code 14 - Unknown method name?
    UnknownMethodName,
    /// Code 15 - Wrong protocol (http/https)?
    WrongProtocol,
    /// Code 1000 - READ_ONLY_MODE
    ReadOnlyMode,
    /// Code 1001 - INVALID_AUTH_TOKEN. Occurs once a user auth token expires.
    InvalidAuthToken,
    /// Code 1002 - INVALID_PARTNER_LOGIN. auth.partnerLogin auth.userLogin. Can also occur for a
    /// user login.
    InvalidPartnerLogin,
    /// Code 1003 - LISTENER_NOT_AUTHORIZED. station.getPlaylist - Pandora One Subscription or
    /// Trial Expired. Possibly account suspended?
    ListenerNotAuthorized,
    /// Code 1004 - USER_NOT_AUTHORIZED. User not authorized to perform action. Is your station
    /// token correct?
    UserNotAuthorized,
    /// Code 1005 - MAX_STATIONS_REACHED. Station limit reached.
    MaxStationsReached,
    /// Code 1006 - STATION_DOES_NOT_EXIST. Station does not exist.
    StationDoesNotExist,
    /// Code 1007 - COMPLIMENTARY_PERIOD_ALREADY_IN_USE
    ComplimentaryPeriodAlreadyInUse,
    /// Code 1008 - CALL_NOT_ALLOWED. station.addFeedback - Returned when attempting to add
    /// feedback to shared station.
    CallNotAllowed,
    /// Code 1009 - DEVICE_NOT_FOUND
    DeviceNotFound,
    /// Code 1010 - PARTNER_NOT_AUTHORIZED
    PartnerNotAuthorized,
    /// Code 1011 - INVALID_USERNAME
    InvalidUsername,
    /// Code 1012 - INVALID_PASSWORD
    InvalidPassword,
    /// Code 1013 - USERNAME_ALREADY_EXISTS
    UsernameAlreadyExists,
    /// Code 1014 - DEVICE_ALREADY_ASSOCIATED_TO_ACCOUNT
    DeviceAlreadyAssociatedToAccount,
    /// Code 1015 - UPGRADE_DEVICE_MODEL_INVALID
    UpgradeDeviceModelInvalid,
    /// Code 1018 - EXPLICIT_PIN_INCORRECT
    ExplicitPinIncorrect,
    /// Code 1020 - EXPLICIT_PIN_MALFORMED
    ExplicitPinMalformed,
    /// Code 1023 - DEVICE_MODEL_INVALID
    DeviceModelInvalid,
    /// Code 1024 - ZIP_CODE_INVALID
    ZipCodeInvalid,
    /// Code 1025 - BIRTH_YEAR_INVALID
    BirthYearInvalid,
    /// Code 1026 - BIRTH_YEAR_TOO_YOUNG
    BirthYearTooYoung,
    /// Code 1027 - INVALID_COUNTRY_CODE
    InvalidCountryCode,
    /// Code 1027 - INVALID_GENDER
    InvalidGender,
    /// Code 1034 - DEVICE_DISABLED
    DeviceDisabled,
    /// Code 1035 - DAILY_TRIAL_LIMIT_REACHED
    DailyTrialLimitReached,
    /// Code 1036 - INVALID_SPONSOR
    InvalidSponsor,
    /// Code 1037 - USER_ALREADY_USED_TRIAL
    UserAlreadyUsedTrial,
    /// Code 1039 - PLAYLIST_EXCEEDED. Too many requests for a new playlist.
    PlaylistExceeded,
    /// Undocumented error code
    UnknownErrorCode(u32),
    /// No error code provided
    UnknownErrorMessage,
}

impl From<u32> for JsonErrorKind {
    /// Create a JsonError from an error code.
    fn from(code: u32) -> Self {
        match code {
            0 => JsonErrorKind::InternalError,
            1 => JsonErrorKind::MaintenanceMode,
            2 => JsonErrorKind::UrlParamMissingMethod,
            3 => JsonErrorKind::UrlParamMissingAuthToken,
            4 => JsonErrorKind::UrlParamMissingPartnerId,
            5 => JsonErrorKind::UrlParamMissingUserId,
            6 => JsonErrorKind::SecureProtocolRequired,
            7 => JsonErrorKind::CertificateRequired,
            8 => JsonErrorKind::ParameterTypeMismatch,
            9 => JsonErrorKind::ParameterMissing,
            10 => JsonErrorKind::ParameterValueInvalid,
            11 => JsonErrorKind::ApiVersionNotSupported,
            12 => JsonErrorKind::LicensingRestrictions,
            13 => JsonErrorKind::InsufficientConnectivity,
            14 => JsonErrorKind::UnknownMethodName,
            15 => JsonErrorKind::WrongProtocol,
            1000 => JsonErrorKind::ReadOnlyMode,
            1001 => JsonErrorKind::InvalidAuthToken,
            1002 => JsonErrorKind::InvalidPartnerLogin,
            1003 => JsonErrorKind::ListenerNotAuthorized,
            1004 => JsonErrorKind::UserNotAuthorized,
            1005 => JsonErrorKind::MaxStationsReached,
            1006 => JsonErrorKind::StationDoesNotExist,
            1007 => JsonErrorKind::ComplimentaryPeriodAlreadyInUse,
            1008 => JsonErrorKind::CallNotAllowed,
            1009 => JsonErrorKind::DeviceNotFound,
            1010 => JsonErrorKind::PartnerNotAuthorized,
            1011 => JsonErrorKind::InvalidUsername,
            1012 => JsonErrorKind::InvalidPassword,
            1013 => JsonErrorKind::UsernameAlreadyExists,
            1014 => JsonErrorKind::DeviceAlreadyAssociatedToAccount,
            1015 => JsonErrorKind::UpgradeDeviceModelInvalid,
            1018 => JsonErrorKind::ExplicitPinIncorrect,
            1020 => JsonErrorKind::ExplicitPinMalformed,
            1023 => JsonErrorKind::DeviceModelInvalid,
            1024 => JsonErrorKind::ZipCodeInvalid,
            1025 => JsonErrorKind::BirthYearInvalid,
            1026 => JsonErrorKind::BirthYearTooYoung,
            /* TODO: these two error codes collide - verify value */
            1027 => JsonErrorKind::InvalidCountryCode,
            //1027 => JsonErrorKind::InvalidGender,
            1034 => JsonErrorKind::DeviceDisabled,
            1035 => JsonErrorKind::DailyTrialLimitReached,
            1036 => JsonErrorKind::InvalidSponsor,
            1037 => JsonErrorKind::UserAlreadyUsedTrial,
            1039 => JsonErrorKind::PlaylistExceeded,
            x => JsonErrorKind::UnknownErrorCode(x),
        }
    }
}

/*
impl From<String> for JsonErrorKind {
    /// Create an error message when there's no error code, only a text description.
    fn from(_msg: String) -> Self {
        JsonErrorKind::UnknownErrorMessage
    }
}
*/

impl std::fmt::Display for JsonErrorKind {
    /// Format this type for display
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            // Seen in the wild, accompanied with a message "Service method not found"
            JsonErrorKind::InternalError => write!(f, "Internal"),
            JsonErrorKind::MaintenanceMode => write!(f, "Maintenance Mode"),
            JsonErrorKind::UrlParamMissingMethod => write!(f, "Url Param Missing Method"),
            JsonErrorKind::UrlParamMissingAuthToken => write!(f, "Url Param Missing Auth Token"),
            JsonErrorKind::UrlParamMissingPartnerId => write!(f, "Url Param Missing Partner ID"),
            JsonErrorKind::UrlParamMissingUserId => write!(f, "Url Param Missing User ID"),
            JsonErrorKind::SecureProtocolRequired => write!(f, "Secure Protocol Required"),
            JsonErrorKind::CertificateRequired => write!(f, "Certificate Required"),
            JsonErrorKind::ParameterTypeMismatch => write!(f, "Parameter Type Mismatch"),
            JsonErrorKind::ParameterMissing => write!(f, "Parameter Missing."),
            JsonErrorKind::ParameterValueInvalid => write!(f, "Parameter Value Invalid"),
            JsonErrorKind::ApiVersionNotSupported => write!(f, "API Version Not Supported"),
            JsonErrorKind::LicensingRestrictions => write!(f, "Licensing Restriction"),
            JsonErrorKind::InsufficientConnectivity => write!(f, "Insufficient Connectivity"),
            JsonErrorKind::UnknownMethodName => write!(f, "Unknown Method Name"),
            JsonErrorKind::WrongProtocol => write!(f, "Incorrect Protocol"),
            JsonErrorKind::ReadOnlyMode => write!(f, "Read Only Mode"),
            JsonErrorKind::InvalidAuthToken => write!(f, "Invalid Auth Token"),
            JsonErrorKind::InvalidPartnerLogin => write!(f, "Invalid Partner Login"),
            JsonErrorKind::ListenerNotAuthorized => write!(f, "Listener Not Authorized"),
            JsonErrorKind::UserNotAuthorized => write!(f, "User Not Authorized"),
            JsonErrorKind::MaxStationsReached => write!(f, "Max Stations Reached"),
            JsonErrorKind::StationDoesNotExist => write!(f, "Station Does Not Exist"),
            JsonErrorKind::ComplimentaryPeriodAlreadyInUse => {
                write!(f, "Complimentary Period Already In Use")
            }
            JsonErrorKind::CallNotAllowed => write!(f, "Call Not Allowed"),
            JsonErrorKind::DeviceNotFound => write!(f, "Device Not Found"),
            JsonErrorKind::PartnerNotAuthorized => write!(f, "Partner Not Authorized"),
            JsonErrorKind::InvalidUsername => write!(f, "Invalid Username"),
            JsonErrorKind::InvalidPassword => write!(f, "Invalid Password"),
            JsonErrorKind::UsernameAlreadyExists => write!(f, "Username Already Exists"),
            JsonErrorKind::DeviceAlreadyAssociatedToAccount => {
                write!(f, "Device Already Associated to Account")
            }
            JsonErrorKind::UpgradeDeviceModelInvalid => write!(f, "Upgrade Device Model Invalid"),
            JsonErrorKind::ExplicitPinIncorrect => write!(f, "Explicit Pin Incorrect"),
            JsonErrorKind::ExplicitPinMalformed => write!(f, "Explicit Pin Malformed"),
            JsonErrorKind::DeviceModelInvalid => write!(f, "Device Model Invalid"),
            JsonErrorKind::ZipCodeInvalid => write!(f, "Zip Code Invalid"),
            JsonErrorKind::BirthYearInvalid => write!(f, "Birth Year Invalid"),
            JsonErrorKind::BirthYearTooYoung => write!(f, "Birth Year Too Young"),
            JsonErrorKind::InvalidCountryCode => write!(f, "Invalid Country Code"),
            JsonErrorKind::InvalidGender => write!(f, "Invalid Gender"),
            JsonErrorKind::DeviceDisabled => write!(f, "Device Disabled"),
            JsonErrorKind::DailyTrialLimitReached => write!(f, "Daily Trial Limit Reached"),
            JsonErrorKind::InvalidSponsor => write!(f, "Invalid Sponsor"),
            JsonErrorKind::UserAlreadyUsedTrial => write!(f, "User Already Used Trial"),
            JsonErrorKind::PlaylistExceeded => write!(
                f,
                "Playlist Exceeded. Too many requests for a new playlist."
            ),
            JsonErrorKind::UnknownErrorCode(x) => write!(f, "Unrecognized Error Code ({})", x),
            JsonErrorKind::UnknownErrorMessage => write!(f, "Missing Error Code."),
        }
    }
}

/// Pandora JSON API call error description
#[derive(Debug, Clone, PartialEq)]
pub struct JsonError {
    pub(crate) kind: JsonErrorKind,
    pub(crate) message: Option<String>,
}

impl JsonError {
    /// Initialize a JsonError from some combination of code and message.
    pub fn new(code: Option<u32>, message: Option<String>) -> Self {
        let kind = match code {
            Some(code) => JsonErrorKind::from(code),
            None => JsonErrorKind::UnknownErrorMessage,
        };
        JsonError { kind, message }
    }

    /// Return what kind of error this is.
    pub fn kind(&self) -> JsonErrorKind {
        self.kind
    }
}

impl std::error::Error for JsonError {
    /// Get the source error, if any, for this error.
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl std::fmt::Display for JsonError {
    /// Format this error for display
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "Pandora API Call Error ({} Error)", self.kind)?;
        if let Some(msg) = &self.message {
            write!(f, ": {}", msg)?;
        }
        Ok(())
    }
}