use std::fmt::Display; use http::status::StatusCode; use log::{error, warn}; use meilisearch_core::{FstError, HeedError}; use serde::{Deserialize, Serialize}; use tide::IntoResponse; use tide::Response; use crate::helpers::meilisearch::Error as SearchError; pub type SResult = Result; pub enum ResponseError { Internal(String), BadRequest(String), InvalidToken(String), NotFound(String), IndexNotFound(String), DocumentNotFound(String), MissingHeader(String), FilterParsing(String), BadParameter(String, String), OpenIndex(String), CreateIndex(String), InvalidIndexUid, Maintenance, } impl ResponseError { pub fn internal(message: impl Display) -> ResponseError { ResponseError::Internal(message.to_string()) } pub fn bad_request(message: impl Display) -> ResponseError { ResponseError::BadRequest(message.to_string()) } pub fn invalid_token(message: impl Display) -> ResponseError { ResponseError::InvalidToken(message.to_string()) } pub fn not_found(message: impl Display) -> ResponseError { ResponseError::NotFound(message.to_string()) } pub fn index_not_found(message: impl Display) -> ResponseError { ResponseError::IndexNotFound(message.to_string()) } pub fn document_not_found(message: impl Display) -> ResponseError { ResponseError::DocumentNotFound(message.to_string()) } pub fn missing_header(message: impl Display) -> ResponseError { ResponseError::MissingHeader(message.to_string()) } pub fn bad_parameter(name: impl Display, message: impl Display) -> ResponseError { ResponseError::BadParameter(name.to_string(), message.to_string()) } pub fn open_index(message: impl Display) -> ResponseError { ResponseError::OpenIndex(message.to_string()) } pub fn create_index(message: impl Display) -> ResponseError { ResponseError::CreateIndex(message.to_string()) } } impl IntoResponse for ResponseError { fn into_response(self) -> Response { match self { ResponseError::Internal(err) => { error!("internal server error: {}", err); error("Internal server error".to_string(), StatusCode::INTERNAL_SERVER_ERROR, ) } ResponseError::FilterParsing(err) => { warn!("error paring filter: {}", err); error(format!("parsing error: {}", err), StatusCode::BAD_REQUEST) } ResponseError::BadRequest(err) => { warn!("bad request: {}", err); error(err, StatusCode::BAD_REQUEST) } ResponseError::InvalidToken(err) => { error(format!("Invalid API key: {}", err), StatusCode::FORBIDDEN) } ResponseError::NotFound(err) => error(err, StatusCode::NOT_FOUND), ResponseError::IndexNotFound(index) => { error(format!("Index {} not found", index), StatusCode::NOT_FOUND) } ResponseError::DocumentNotFound(id) => error( format!("Document with id {} not found", id), StatusCode::NOT_FOUND, ), ResponseError::MissingHeader(header) => error( format!("Header {} is missing", header), StatusCode::UNAUTHORIZED, ), ResponseError::BadParameter(param, e) => error( format!("Url parameter {} error: {}", param, e), StatusCode::BAD_REQUEST, ), ResponseError::CreateIndex(err) => error( format!("Impossible to create index; {}", err), StatusCode::BAD_REQUEST, ), ResponseError::OpenIndex(err) => error( format!("Impossible to open index; {}", err), StatusCode::BAD_REQUEST, ), ResponseError::InvalidIndexUid => error( "Index must have a valid uid; Index uid can be of type integer or string only composed of alphanumeric characters, hyphens (-) and underscores (_).".to_string(), StatusCode::BAD_REQUEST, ), ResponseError::Maintenance => error( String::from("Server is in maintenance, please try again later"), StatusCode::SERVICE_UNAVAILABLE, ), } } } #[derive(Serialize, Deserialize)] struct ErrorMessage { message: String, } fn error(message: String, status: StatusCode) -> Response { let message = ErrorMessage { message }; tide::Response::new(status.as_u16()) .body_json(&message) .unwrap() } impl From for ResponseError { fn from(err: serde_json::Error) -> ResponseError { ResponseError::internal(err) } } impl From for ResponseError { fn from(err: meilisearch_core::Error) -> ResponseError { ResponseError::internal(err) } } impl From for ResponseError { fn from(err: HeedError) -> ResponseError { ResponseError::internal(err) } } impl From for ResponseError { fn from(err: FstError) -> ResponseError { ResponseError::internal(err) } } impl From for ResponseError { fn from(err: SearchError) -> ResponseError { match err { SearchError::FilterParsing(s) => ResponseError::FilterParsing(s), _ => ResponseError::internal(err), } } } impl From for ResponseError { fn from(err: meilisearch_core::settings::RankingRuleConversionError) -> ResponseError { ResponseError::internal(err) } } pub trait IntoInternalError { fn into_internal_error(self) -> SResult; } impl IntoInternalError for Option { fn into_internal_error(self) -> SResult { match self { Some(value) => Ok(value), None => Err(ResponseError::internal("Heed cannot find requested value")), } } }