use crate::prelude::*;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ApiError {
#[error("BadRequestError: Bad request - {message}")]
BadRequestError {
message: String,
field: Option<String>,
details: Option<String>,
},
#[error("UnauthorizedError: Authentication failed - {message}")]
UnauthorizedError {
message: String,
auth_type: Option<String>,
},
#[error("ForbiddenError: Access forbidden - {message}")]
ForbiddenError {
message: String,
resource: Option<String>,
required_permission: Option<String>,
},
#[error("NotFoundError: Resource not found - {message}")]
NotFoundError {
message: String,
resource_id: Option<String>,
resource_type: Option<String>,
},
#[error("UnprocessableEntityError: Unprocessable entity - {message}")]
UnprocessableEntityError {
message: String,
field: Option<String>,
validation_error: Option<String>,
},
#[error("TooManyRequestsError: Rate limit exceeded - {message}")]
TooManyRequestsError {
message: String,
retry_after_seconds: Option<u64>,
limit_type: Option<String>,
},
#[error("InternalServerError: Internal server error - {message}")]
InternalServerError {
message: String,
error_id: Option<String>,
},
#[error("ConflictError: Conflict - {message}")]
ConflictError {
message: String,
error: Option<V1ErrorResponseError>,
},
#[error("ServiceUnavailableError: {message}")]
ServiceUnavailableError {
message: String,
error: Option<V1ErrorResponseError>,
},
#[error("PaymentRequiredError: {message}")]
PaymentRequiredError {
message: String,
error: Option<PaymentRequiredErrorBodyError>,
},
#[error("HTTP error {status}: {message}")]
Http { status: u16, message: String },
#[error("Network error: {0}")]
Network(reqwest::Error),
#[error("Request executor error: {0}")]
Executor(Box<dyn std::error::Error + Send + Sync>),
#[error("Serialization error: {0}")]
Serialization(serde_json::Error),
#[error("Configuration error: {0}")]
Configuration(String),
#[error("Invalid header value")]
InvalidHeader,
#[error("Could not clone request for retry")]
RequestClone,
#[error("SSE stream terminated")]
StreamTerminated,
#[error("SSE stream timed out waiting for next event")]
StreamTimeout,
#[error("SSE parse error: {0}")]
SseParseError(String),
}
impl ApiError {
pub fn from_response(status_code: u16, body: Option<&str>) -> Self {
match status_code {
400 => {
if let Some(body_str) = body {
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
return Self::BadRequestError {
message: parsed
.get("message")
.and_then(|v| v.as_str())
.unwrap_or("Unknown error")
.to_string(),
field: parsed
.get("field")
.and_then(|v| v.as_str().map(|s| s.to_string())),
details: parsed
.get("details")
.and_then(|v| v.as_str().map(|s| s.to_string())),
};
}
}
return Self::BadRequestError {
message: body.unwrap_or("Unknown error").to_string(),
field: None,
details: None,
};
}
401 => {
if let Some(body_str) = body {
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
return Self::UnauthorizedError {
message: parsed
.get("message")
.and_then(|v| v.as_str())
.unwrap_or("Unknown error")
.to_string(),
auth_type: parsed
.get("authType")
.and_then(|v| v.as_str().map(|s| s.to_string())),
};
}
}
return Self::UnauthorizedError {
message: body.unwrap_or("Unknown error").to_string(),
auth_type: None,
};
}
403 => {
if let Some(body_str) = body {
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
return Self::ForbiddenError {
message: parsed
.get("message")
.and_then(|v| v.as_str())
.unwrap_or("Unknown error")
.to_string(),
resource: parsed
.get("resource")
.and_then(|v| v.as_str().map(|s| s.to_string())),
required_permission: parsed
.get("requiredPermission")
.and_then(|v| v.as_str().map(|s| s.to_string())),
};
}
}
return Self::ForbiddenError {
message: body.unwrap_or("Unknown error").to_string(),
resource: None,
required_permission: None,
};
}
404 => {
if let Some(body_str) = body {
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
return Self::NotFoundError {
message: parsed
.get("message")
.and_then(|v| v.as_str())
.unwrap_or("Unknown error")
.to_string(),
resource_id: parsed
.get("resourceId")
.and_then(|v| v.as_str().map(|s| s.to_string())),
resource_type: parsed
.get("resourceType")
.and_then(|v| v.as_str().map(|s| s.to_string())),
};
}
}
return Self::NotFoundError {
message: body.unwrap_or("Unknown error").to_string(),
resource_id: None,
resource_type: None,
};
}
422 => {
if let Some(body_str) = body {
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
return Self::UnprocessableEntityError {
message: parsed
.get("message")
.and_then(|v| v.as_str())
.unwrap_or("Unknown error")
.to_string(),
field: parsed
.get("field")
.and_then(|v| v.as_str().map(|s| s.to_string())),
validation_error: parsed
.get("validationError")
.and_then(|v| v.as_str().map(|s| s.to_string())),
};
}
}
return Self::UnprocessableEntityError {
message: body.unwrap_or("Unknown error").to_string(),
field: None,
validation_error: None,
};
}
429 => {
if let Some(body_str) = body {
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
return Self::TooManyRequestsError {
message: parsed
.get("message")
.and_then(|v| v.as_str())
.unwrap_or("Unknown error")
.to_string(),
retry_after_seconds: parsed
.get("retryAfterSeconds")
.and_then(|v| v.as_u64()),
limit_type: parsed
.get("limitType")
.and_then(|v| v.as_str().map(|s| s.to_string())),
};
}
}
return Self::TooManyRequestsError {
message: body.unwrap_or("Unknown error").to_string(),
retry_after_seconds: None,
limit_type: None,
};
}
500 => {
if let Some(body_str) = body {
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
return Self::InternalServerError {
message: parsed
.get("message")
.and_then(|v| v.as_str())
.unwrap_or("Unknown error")
.to_string(),
error_id: parsed
.get("errorId")
.and_then(|v| v.as_str().map(|s| s.to_string())),
};
}
}
return Self::InternalServerError {
message: body.unwrap_or("Unknown error").to_string(),
error_id: None,
};
}
409 => {
if let Some(body_str) = body {
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
return Self::ConflictError {
message: parsed
.get("message")
.and_then(|v| v.as_str())
.unwrap_or("Unknown error")
.to_string(),
error: parsed.get("error").and_then(|v| {
serde_json::from_value::<V1ErrorResponseError>(v.clone()).ok()
}),
};
}
}
return Self::ConflictError {
message: body.unwrap_or("Unknown error").to_string(),
error: None,
};
}
503 => {
if let Some(body_str) = body {
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
return Self::ServiceUnavailableError {
message: parsed
.get("message")
.and_then(|v| v.as_str())
.unwrap_or("Unknown error")
.to_string(),
error: parsed.get("error").and_then(|v| {
serde_json::from_value::<V1ErrorResponseError>(v.clone()).ok()
}),
};
}
}
return Self::ServiceUnavailableError {
message: body.unwrap_or("Unknown error").to_string(),
error: None,
};
}
402 => {
if let Some(body_str) = body {
if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(body_str) {
return Self::PaymentRequiredError {
message: parsed
.get("message")
.and_then(|v| v.as_str())
.unwrap_or("Unknown error")
.to_string(),
error: parsed.get("error").and_then(|v| {
serde_json::from_value::<PaymentRequiredErrorBodyError>(v.clone())
.ok()
}),
};
}
}
return Self::PaymentRequiredError {
message: body.unwrap_or("Unknown error").to_string(),
error: None,
};
}
_ => Self::Http {
status: status_code,
message: body.unwrap_or("Unknown error").to_string(),
},
}
}
}
#[derive(Debug)]
pub struct BuildError {
field: &'static str,
}
impl BuildError {
pub fn missing_field(field: &'static str) -> Self {
Self { field }
}
}
impl std::fmt::Display for BuildError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "`{}` was not set but is required", self.field)
}
}
impl std::error::Error for BuildError {}