use crate::response::Response;
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum RestError {
#[error("REST API error: {message}")]
Api {
message: String,
code: Option<i32>,
request_id: Option<String>,
response: Box<Response>,
},
#[error("HTTP error {status}: {body}")]
Http {
status: u16,
body: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
},
#[error("login required")]
LoginRequired,
#[error("no client_id provided for token renewal")]
NoClientId,
#[error("no refresh token available and access token has expired")]
NoRefreshToken,
#[error("failed to build request: {0}")]
RequestBuild(String),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("transport error: {0}")]
Transport(#[from] rsurl::Error),
#[error("Base64 decode error: {0}")]
Base64Decode(#[from] base64::DecodeError),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Other(String),
}
impl RestError {
pub fn from_response(response: Response) -> Self {
let message = response
.error
.clone()
.unwrap_or_else(|| "unknown error".to_string());
let code = response.code;
let request_id = response.request_id.clone();
RestError::Api {
message,
code,
request_id,
response: Box::new(response),
}
}
pub fn http(
status: u16,
body: String,
source: Option<Box<dyn std::error::Error + Send + Sync>>,
) -> Self {
RestError::Http {
status,
body,
source,
}
}
pub fn is_permission_denied(&self) -> bool {
matches!(
self,
RestError::Api {
code: Some(403),
..
}
)
}
pub fn is_not_found(&self) -> bool {
matches!(
self,
RestError::Api {
code: Some(404),
..
}
)
}
pub fn status_code(&self) -> Option<i32> {
match self {
RestError::Api { code, .. } => *code,
RestError::Http { status, .. } => Some(*status as i32),
_ => None,
}
}
}
pub type Result<T> = std::result::Result<T, RestError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_permission_denied() {
let response = Response {
result: "error".to_string(),
data: None,
error: Some("permission denied".to_string()),
code: Some(403),
extra: None,
token: None,
paging: None,
job: None,
time: None,
access: None,
exception: None,
redirect_url: None,
redirect_code: None,
request_id: None,
};
let error = RestError::from_response(response);
assert!(error.is_permission_denied());
}
#[test]
fn test_error_not_found() {
let response = Response {
result: "error".to_string(),
data: None,
error: Some("not found".to_string()),
code: Some(404),
extra: None,
token: None,
paging: None,
job: None,
time: None,
access: None,
exception: None,
redirect_url: None,
redirect_code: None,
request_id: None,
};
let error = RestError::from_response(response);
assert!(error.is_not_found());
}
}