#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
MeiliSearchError {
message: String,
error_code: ErrorCode,
error_type: ErrorType,
error_link: String,
},
UnreachableServer,
ParseError(serde_json::Error),
Empty,
#[cfg(not(target_arch = "wasm32"))]
HttpError(reqwest::Error),
#[cfg(target_arch = "wasm32")]
HttpError(String),
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum ErrorType {
InvalidRequest,
Internal,
Authentication,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum ErrorCode {
IndexCreationFailed,
IndexAlreadyExists,
IndexNotFound,
InvalidIndexUid,
IndexNotAccessible,
InvalidState,
MissingPrimaryKey,
PrimaryKeyAlreadyPresent,
MaxFieldsLimitExceeded,
MissingDocumentId,
InvalidFacet,
InvalidFilter,
BadParameter,
BadRequest,
DocumentNotFound,
InternalError,
InvalidToken,
Maintenance,
MissingAuthorizationHeader,
NotFound,
PayloadTooLarge,
UnretrievableDocument,
SearchError,
UnsupportedMediaType,
Unknown(UnknownErrorCode),
}
#[derive(Clone)]
pub struct UnknownErrorCode(String);
impl std::fmt::Display for UnknownErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl std::fmt::Debug for UnknownErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&self.0, f)
}
}
impl ErrorType {
pub fn as_str(&self) -> &'static str {
match self {
ErrorType::InvalidRequest => "invalid_request_error",
ErrorType::Internal => "internal_error",
ErrorType::Authentication => "authentication_error",
}
}
pub fn parse(input: &str) -> Option<Self> {
match input {
"invalid_request_error" => Some(ErrorType::InvalidRequest),
"internal_error" => Some(ErrorType::Internal),
"authentication_error" => Some(ErrorType::Authentication),
_ => None,
}
}
}
impl ErrorCode {
pub fn as_str(&self) -> &str {
match self {
ErrorCode::IndexCreationFailed => "index_creation_failed",
ErrorCode::IndexAlreadyExists => "index_already_exists",
ErrorCode::IndexNotFound => "index_not_found",
ErrorCode::InvalidIndexUid => "invalid_index_uid",
ErrorCode::IndexNotAccessible => "index_not_accessible",
ErrorCode::InvalidState => "invalid_state",
ErrorCode::MissingPrimaryKey => "missing_primary_key",
ErrorCode::PrimaryKeyAlreadyPresent => "primary_key_already_present",
ErrorCode::MaxFieldsLimitExceeded => "max_field_limit_exceeded",
ErrorCode::MissingDocumentId => "missing_document_id",
ErrorCode::InvalidFacet => "invalid_facet",
ErrorCode::InvalidFilter => "invalid_filter",
ErrorCode::BadParameter => "bad_parameter",
ErrorCode::BadRequest => "bad_request",
ErrorCode::DocumentNotFound => "document_not_found",
ErrorCode::InternalError => "internal",
ErrorCode::InvalidToken => "invalid_token",
ErrorCode::Maintenance => "maintenance",
ErrorCode::MissingAuthorizationHeader => "missing_authorization_header",
ErrorCode::NotFound => "not_found",
ErrorCode::PayloadTooLarge => "payload_too_large",
ErrorCode::UnretrievableDocument => "unretrievable_document",
ErrorCode::SearchError => "search_error",
ErrorCode::UnsupportedMediaType => "unsupported_media_type",
ErrorCode::Unknown(inner) => &inner.0,
}
}
pub fn parse(input: &str) -> Self {
match input {
"index_creation_failed" => ErrorCode::IndexCreationFailed,
"index_already_exists" => ErrorCode::IndexAlreadyExists,
"index_not_found" => ErrorCode::IndexNotFound,
"invalid_index_uid" => ErrorCode::InvalidIndexUid,
"index_not_accessible" => ErrorCode::IndexNotAccessible,
"invalid_state" => ErrorCode::InvalidState,
"missing_primary_key" => ErrorCode::MissingPrimaryKey,
"primary_key_already_present" => ErrorCode::PrimaryKeyAlreadyPresent,
"max_field_limit_exceeded" => ErrorCode::MaxFieldsLimitExceeded,
"missing_document_id" => ErrorCode::MissingDocumentId,
"invalid_facet" => ErrorCode::InvalidFacet,
"invalid_filter" => ErrorCode::InvalidFilter,
"bad_parameter" => ErrorCode::BadParameter,
"bad_request" => ErrorCode::BadRequest,
"document_not_found" => ErrorCode::DocumentNotFound,
"internal" => ErrorCode::InternalError,
"invalid_token" => ErrorCode::InvalidToken,
"maintenance" => ErrorCode::Maintenance,
"missing_authorization_header" => ErrorCode::MissingAuthorizationHeader,
"not_found" => ErrorCode::NotFound,
"payload_too_large" => ErrorCode::PayloadTooLarge,
"unretrievable_document" => ErrorCode::UnretrievableDocument,
"search_error" => ErrorCode::SearchError,
"unsupported_media_type" => ErrorCode::UnsupportedMediaType,
inner => ErrorCode::Unknown(UnknownErrorCode(inner.to_string())),
}
}
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self {
ErrorCode::Unknown(inner) => write!(fmt, "unknown ({})", inner),
_ => write!(fmt, "{}", self.as_str()),
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self {
Error::MeiliSearchError {
message,
error_code,
error_type,
error_link,
} => write!(
fmt,
"Meilisearch {}: {}: {}. {}",
error_type.as_str(),
error_code,
message,
error_link,
),
Error::UnreachableServer => write!(fmt, "The MeiliSearch server can't be reached."),
Error::ParseError(e) => write!(fmt, "Error parsing response JSON: {}", e),
Error::HttpError(e) => write!(fmt, "HTTP request failed: {}", e),
Error::Empty => write!(fmt, "An error occured without a message"),
}
}
}
impl std::error::Error for Error {}
impl From<&serde_json::Value> for Error {
fn from(json: &serde_json::Value) -> Error {
if json.is_null() {
return Error::Empty;
}
let message = json
.get("message")
.and_then(|v| v.as_str())
.map(|s| s.to_string())
.unwrap_or_else(|| json.to_string());
let error_link = json
.get("errorLink")
.and_then(|v| v.as_str())
.map(|s| s.to_string())
.unwrap_or_else(String::new);
let error_type = json
.get("errorType")
.and_then(|v| v.as_str())
.and_then(|s| ErrorType::parse(s))
.unwrap_or(ErrorType::Internal);
let error_code = json
.get("errorCode")
.and_then(|v| v.as_str())
.map(|s| ErrorCode::parse(s))
.unwrap_or_else(|| {
ErrorCode::Unknown(UnknownErrorCode(String::from("missing errorCode")))
});
Error::MeiliSearchError {
message,
error_code,
error_type,
error_link,
}
}
}
#[cfg(not(target_arch = "wasm32"))]
impl From<reqwest::Error> for Error {
fn from(error: reqwest::Error) -> Error {
match error.status() {
None => Error::UnreachableServer,
Some(_e) => Error::HttpError(error),
}
}
}