mirror of
https://github.com/meilisearch/MeiliSearch
synced 2025-01-11 14:04:31 +01:00
Simplify the error creation with thiserror and a smol friendly macro
This commit is contained in:
parent
c55368ddd4
commit
484a9ddb27
@ -35,6 +35,7 @@ smallstr = { version = "0.3.0", features = ["serde"] }
|
|||||||
smallvec = "1.8.0"
|
smallvec = "1.8.0"
|
||||||
smartstring = "1.0.1"
|
smartstring = "1.0.1"
|
||||||
tempfile = "3.3.0"
|
tempfile = "3.3.0"
|
||||||
|
thiserror = "1.0.31"
|
||||||
time = { version = "0.3.7", features = ["serde-well-known", "formatting", "parsing", "macros"] }
|
time = { version = "0.3.7", features = ["serde-well-known", "formatting", "parsing", "macros"] }
|
||||||
uuid = { version = "0.8.2", features = ["v4"] }
|
uuid = { version = "0.8.2", features = ["v4"] }
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ use std::fmt;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::error::is_reserved_keyword;
|
use crate::error::is_reserved_keyword;
|
||||||
use crate::{CriterionError, Error, UserError};
|
use crate::{CriterionError, Error, UserError};
|
||||||
@ -153,14 +154,24 @@ impl FromStr for AscDesc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum SortError {
|
pub enum SortError {
|
||||||
|
#[error("{}", AscDescError::InvalidLatitude)]
|
||||||
InvalidLatitude,
|
InvalidLatitude,
|
||||||
|
#[error("{}", AscDescError::InvalidLongitude)]
|
||||||
InvalidLongitude,
|
InvalidLongitude,
|
||||||
|
#[error("Invalid syntax for the geo parameter: expected expression formated like \
|
||||||
|
`_geoPoint(latitude, longitude)` and ending by `:asc` or `:desc`, found `{name}`.")]
|
||||||
BadGeoPointUsage { name: String },
|
BadGeoPointUsage { name: String },
|
||||||
|
#[error("Invalid syntax for the sort parameter: expected expression ending by `:asc` or `:desc`, found `{name}`.")]
|
||||||
InvalidName { name: String },
|
InvalidName { name: String },
|
||||||
|
#[error("`{name}` is a reserved keyword and thus can't be used as a sort expression.")]
|
||||||
ReservedName { name: String },
|
ReservedName { name: String },
|
||||||
|
#[error("`{name}` is a reserved keyword and thus can't be used as a sort expression. \
|
||||||
|
Use the _geoPoint(latitude, longitude) built-in rule to sort on _geo field coordinates.")]
|
||||||
ReservedNameForSettings { name: String },
|
ReservedNameForSettings { name: String },
|
||||||
|
#[error("`{name}` is a reserved keyword and thus can't be used as a sort expression. \
|
||||||
|
Use the _geoPoint(latitude, longitude) built-in rule to sort on _geo field coordinates.")]
|
||||||
ReservedNameForFilter { name: String },
|
ReservedNameForFilter { name: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,41 +195,6 @@ impl From<AscDescError> for SortError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SortError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::InvalidLatitude => write!(f, "{}", AscDescError::InvalidLatitude),
|
|
||||||
Self::InvalidLongitude => write!(f, "{}", AscDescError::InvalidLongitude),
|
|
||||||
Self::BadGeoPointUsage { name } => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Invalid syntax for the geo parameter: expected expression formated like \
|
|
||||||
`_geoPoint(latitude, longitude)` and ending by `:asc` or `:desc`, found `{}`.",
|
|
||||||
name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Self::InvalidName { name } => {
|
|
||||||
write!(f, "Invalid syntax for the sort parameter: expected expression ending by `:asc` or `:desc`, found `{}`.", name)
|
|
||||||
}
|
|
||||||
Self::ReservedName { name } => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"`{}` is a reserved keyword and thus can't be used as a sort expression.",
|
|
||||||
name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Self::ReservedNameForSettings { name } | Self::ReservedNameForFilter { name } => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"`{}` is a reserved keyword and thus can't be used as a sort expression. \
|
|
||||||
Use the _geoPoint(latitude, longitude) built-in rule to sort on _geo field coordinates.",
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SortError> for Error {
|
impl From<SortError> for Error {
|
||||||
fn from(error: SortError) -> Self {
|
fn from(error: SortError) -> Self {
|
||||||
Self::UserError(UserError::SortError(error))
|
Self::UserError(UserError::SortError(error))
|
||||||
|
@ -2,55 +2,28 @@ use std::fmt;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::{AscDesc, Member};
|
||||||
use crate::{AscDesc, Member, UserError};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum CriterionError {
|
pub enum CriterionError {
|
||||||
|
#[error("`{name}` ranking rule is invalid. Valid ranking rules are Words, Typo, Sort, Proximity, Attribute, Exactness and custom ranking rules.")]
|
||||||
InvalidName { name: String },
|
InvalidName { name: String },
|
||||||
|
#[error("`{name}` is a reserved keyword and thus can't be used as a ranking rule")]
|
||||||
ReservedName { name: String },
|
ReservedName { name: String },
|
||||||
|
#[error(
|
||||||
|
"`{name}` is a reserved keyword and thus can't be used as a ranking rule. \
|
||||||
|
`{name}` can only be used for sorting at search time"
|
||||||
|
)]
|
||||||
ReservedNameForSort { name: String },
|
ReservedNameForSort { name: String },
|
||||||
|
#[error(
|
||||||
|
"`{name}` is a reserved keyword and thus can't be used as a ranking rule. \
|
||||||
|
`{name}` can only be used for filtering at search time"
|
||||||
|
)]
|
||||||
ReservedNameForFilter { name: String },
|
ReservedNameForFilter { name: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CriterionError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::InvalidName { name } => write!(f, "`{}` ranking rule is invalid. Valid ranking rules are Words, Typo, Sort, Proximity, Attribute, Exactness and custom ranking rules.", name),
|
|
||||||
Self::ReservedName { name } => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"`{}` is a reserved keyword and thus can't be used as a ranking rule",
|
|
||||||
name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Self::ReservedNameForSort { name } => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"`{}` is a reserved keyword and thus can't be used as a ranking rule. \
|
|
||||||
`{}` can only be used for sorting at search time",
|
|
||||||
name, name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Self::ReservedNameForFilter { name } => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"`{}` is a reserved keyword and thus can't be used as a ranking rule. \
|
|
||||||
`{}` can only be used for filtering at search time",
|
|
||||||
name, name
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<CriterionError> for Error {
|
|
||||||
fn from(error: CriterionError) -> Self {
|
|
||||||
Self::UserError(UserError::CriterionError(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
pub enum Criterion {
|
pub enum Criterion {
|
||||||
/// Sorted by decreasing number of matched query terms.
|
/// Sorted by decreasing number of matched query terms.
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::error::Error as StdError;
|
use std::{io, str};
|
||||||
use std::{fmt, io, str};
|
|
||||||
|
|
||||||
use heed::{Error as HeedError, MdbError};
|
use heed::{Error as HeedError, MdbError};
|
||||||
use rayon::ThreadPoolBuildError;
|
use rayon::ThreadPoolBuildError;
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{CriterionError, DocumentId, FieldId, SortError};
|
use crate::{CriterionError, DocumentId, FieldId, SortError};
|
||||||
|
|
||||||
@ -15,92 +15,172 @@ pub fn is_reserved_keyword(keyword: &str) -> bool {
|
|||||||
["_geo", "_geoDistance", "_geoPoint", "_geoRadius"].contains(&keyword)
|
["_geo", "_geoDistance", "_geoPoint", "_geoRadius"].contains(&keyword)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
InternalError(InternalError),
|
#[error("internal: {0}.")]
|
||||||
IoError(io::Error),
|
InternalError(#[from] InternalError),
|
||||||
UserError(UserError),
|
#[error(transparent)]
|
||||||
|
IoError(#[from] io::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
UserError(#[from] UserError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum InternalError {
|
pub enum InternalError {
|
||||||
|
#[error("{}", HeedError::DatabaseClosing)]
|
||||||
DatabaseClosing,
|
DatabaseClosing,
|
||||||
|
#[error("Missing {} in the {db_name} database.", key.unwrap_or("key"))]
|
||||||
DatabaseMissingEntry { db_name: &'static str, key: Option<&'static str> },
|
DatabaseMissingEntry { db_name: &'static str, key: Option<&'static str> },
|
||||||
FieldIdMapMissingEntry(FieldIdMapMissingEntry),
|
#[error(transparent)]
|
||||||
|
FieldIdMapMissingEntry(#[from] FieldIdMapMissingEntry),
|
||||||
|
#[error("Missing {key} in the field id mapping.")]
|
||||||
FieldIdMappingMissingEntry { key: FieldId },
|
FieldIdMappingMissingEntry { key: FieldId },
|
||||||
Fst(fst::Error),
|
#[error(transparent)]
|
||||||
|
Fst(#[from] fst::Error),
|
||||||
|
#[error("Invalid compression type have been specified to grenad.")]
|
||||||
GrenadInvalidCompressionType,
|
GrenadInvalidCompressionType,
|
||||||
|
#[error("Invalid grenad file with an invalid version format.")]
|
||||||
GrenadInvalidFormatVersion,
|
GrenadInvalidFormatVersion,
|
||||||
|
#[error("Invalid merge while processing {process}.")]
|
||||||
IndexingMergingKeys { process: &'static str },
|
IndexingMergingKeys { process: &'static str },
|
||||||
|
#[error("{}", HeedError::InvalidDatabaseTyping)]
|
||||||
InvalidDatabaseTyping,
|
InvalidDatabaseTyping,
|
||||||
RayonThreadPool(ThreadPoolBuildError),
|
#[error(transparent)]
|
||||||
SerdeJson(serde_json::Error),
|
RayonThreadPool(#[from] ThreadPoolBuildError),
|
||||||
Serialization(SerializationError),
|
#[error(transparent)]
|
||||||
Store(MdbError),
|
SerdeJson(#[from] serde_json::Error),
|
||||||
Utf8(str::Utf8Error),
|
#[error(transparent)]
|
||||||
|
Serialization(#[from] SerializationError),
|
||||||
|
#[error(transparent)]
|
||||||
|
Store(#[from] MdbError),
|
||||||
|
#[error(transparent)]
|
||||||
|
Utf8(#[from] str::Utf8Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum SerializationError {
|
pub enum SerializationError {
|
||||||
|
#[error("{}", match .db_name {
|
||||||
|
Some(name) => format!("decoding from the {name} database failed"),
|
||||||
|
None => "decoding failed".to_string(),
|
||||||
|
})]
|
||||||
Decoding { db_name: Option<&'static str> },
|
Decoding { db_name: Option<&'static str> },
|
||||||
|
#[error("{}", match .db_name {
|
||||||
|
Some(name) => format!("encoding into the {name} database failed"),
|
||||||
|
None => "encoding failed".to_string(),
|
||||||
|
})]
|
||||||
Encoding { db_name: Option<&'static str> },
|
Encoding { db_name: Option<&'static str> },
|
||||||
|
#[error("number is not a valid finite number")]
|
||||||
InvalidNumberSerialization,
|
InvalidNumberSerialization,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum FieldIdMapMissingEntry {
|
pub enum FieldIdMapMissingEntry {
|
||||||
|
#[error("unknown field id {field_id} coming from the {process} process")]
|
||||||
FieldId { field_id: FieldId, process: &'static str },
|
FieldId { field_id: FieldId, process: &'static str },
|
||||||
|
#[error("unknown field name {field_name} coming from the {process} process")]
|
||||||
FieldName { field_name: String, process: &'static str },
|
FieldName { field_name: String, process: &'static str },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum UserError {
|
pub enum UserError {
|
||||||
|
#[error("A document cannot contain more than 65,535 fields.")]
|
||||||
AttributeLimitReached,
|
AttributeLimitReached,
|
||||||
CriterionError(CriterionError),
|
#[error(transparent)]
|
||||||
|
CriterionError(#[from] CriterionError),
|
||||||
|
#[error("Maximum number of documents reached.")]
|
||||||
DocumentLimitReached,
|
DocumentLimitReached,
|
||||||
|
#[error(
|
||||||
|
"Document identifier `{}` is invalid. \
|
||||||
|
A document identifier can be of type integer or string, \
|
||||||
|
only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_).", .document_id.to_string()
|
||||||
|
)]
|
||||||
InvalidDocumentId { document_id: Value },
|
InvalidDocumentId { document_id: Value },
|
||||||
|
#[error("Invalid facet distribution, the fields `{}` are not set as filterable.",
|
||||||
|
.invalid_facets_name.iter().map(AsRef::as_ref).collect::<Vec<_>>().join(", ")
|
||||||
|
)]
|
||||||
InvalidFacetsDistribution { invalid_facets_name: BTreeSet<String> },
|
InvalidFacetsDistribution { invalid_facets_name: BTreeSet<String> },
|
||||||
InvalidGeoField(GeoError),
|
#[error(transparent)]
|
||||||
|
InvalidGeoField(#[from] GeoError),
|
||||||
|
#[error("{0}")]
|
||||||
InvalidFilter(String),
|
InvalidFilter(String),
|
||||||
|
#[error("Attribute `{}` is not sortable. {}",
|
||||||
|
.field,
|
||||||
|
match .valid_fields.is_empty() {
|
||||||
|
true => "This index does not have configured sortable attributes.".to_string(),
|
||||||
|
false => format!("Available sortable attributes are: `{}`.",
|
||||||
|
valid_fields.iter().map(AsRef::as_ref).collect::<Vec<_>>().join(", ")
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)]
|
||||||
InvalidSortableAttribute { field: String, valid_fields: BTreeSet<String> },
|
InvalidSortableAttribute { field: String, valid_fields: BTreeSet<String> },
|
||||||
|
#[error("The sort ranking rule must be specified in the ranking rules settings to use the sort parameter at search time.")]
|
||||||
SortRankingRuleMissing,
|
SortRankingRuleMissing,
|
||||||
|
#[error("The database file is in an invalid state.")]
|
||||||
InvalidStoreFile,
|
InvalidStoreFile,
|
||||||
|
#[error("Maximum database size has been reached.")]
|
||||||
MaxDatabaseSizeReached,
|
MaxDatabaseSizeReached,
|
||||||
|
#[error("Document doesn't have a `{}` attribute: `{}`.", .primary_key, serde_json::to_string(.document).unwrap())]
|
||||||
MissingDocumentId { primary_key: String, document: Object },
|
MissingDocumentId { primary_key: String, document: Object },
|
||||||
|
#[error("The primary key inference process failed because the engine did not find any fields containing `id` substring in their name. If your document identifier does not contain any `id` substring, you can set the primary key of the index.")]
|
||||||
MissingPrimaryKey,
|
MissingPrimaryKey,
|
||||||
|
#[error("There is no more space left on the device. Consider increasing the size of the disk/partition.")]
|
||||||
NoSpaceLeftOnDevice,
|
NoSpaceLeftOnDevice,
|
||||||
|
#[error("Index already has a primary key: `{0}`.")]
|
||||||
PrimaryKeyCannotBeChanged(String),
|
PrimaryKeyCannotBeChanged(String),
|
||||||
|
#[error(transparent)]
|
||||||
SerdeJson(serde_json::Error),
|
SerdeJson(serde_json::Error),
|
||||||
SortError(SortError),
|
#[error(transparent)]
|
||||||
|
SortError(#[from] SortError),
|
||||||
|
#[error("An unknown internal document id have been used: `{document_id}`.")]
|
||||||
UnknownInternalDocumentId { document_id: DocumentId },
|
UnknownInternalDocumentId { document_id: DocumentId },
|
||||||
|
#[error("`minWordSizeForTypos` setting is invalid. `oneTypo` and `twoTypos` fields should be between `0` and `255`, and `twoTypos` should be greater or equals to `oneTypo` but found `oneTypo: {0}` and twoTypos: {1}`.")]
|
||||||
InvalidMinTypoWordLenSetting(u8, u8),
|
InvalidMinTypoWordLenSetting(u8, u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum GeoError {
|
pub enum GeoError {
|
||||||
|
#[error("Could not find latitude in the document with the id: `{document_id}`. Was expecting a `_geo.lat` field.")]
|
||||||
MissingLatitude { document_id: Value },
|
MissingLatitude { document_id: Value },
|
||||||
|
#[error("Could not find longitude in the document with the id: `{document_id}`. Was expecting a `_geo.lng` field.")]
|
||||||
MissingLongitude { document_id: Value },
|
MissingLongitude { document_id: Value },
|
||||||
|
#[error("Could not parse latitude in the document with the id: `{document_id}`. Was expecting a number but instead got `{value}`.")]
|
||||||
BadLatitude { document_id: Value, value: Value },
|
BadLatitude { document_id: Value, value: Value },
|
||||||
|
#[error("Could not parse longitude in the document with the id: `{document_id}`. Was expecting a number but instead got `{value}`.")]
|
||||||
BadLongitude { document_id: Value, value: Value },
|
BadLongitude { document_id: Value, value: Value },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
/// A little macro helper to autogenerate From implementation that needs two `Into`.
|
||||||
fn from(error: io::Error) -> Error {
|
/// Given the following parameters: `error_from_sub_error!(FieldIdMapMissingEntry => InternalError)`
|
||||||
// TODO must be improved and more precise
|
/// the macro will create the following code:
|
||||||
Error::IoError(error)
|
/// ```ignore
|
||||||
|
/// impl From<FieldIdMapMissingEntry> for Error {
|
||||||
|
/// fn from(error: FieldIdMapMissingEntry) -> Error {
|
||||||
|
/// Error::from(InternalError::from(error))
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
macro_rules! error_from_sub_error {
|
||||||
|
() => {};
|
||||||
|
($sub:ty => $intermediate:ty) => {
|
||||||
|
impl From<$sub> for Error {
|
||||||
|
fn from(error: $sub) -> Error {
|
||||||
|
Error::from(<$intermediate>::from(error))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($($sub:ty => $intermediate:ty $(,)?),+) => {
|
||||||
|
$(error_from_sub_error!($sub => $intermediate);)+
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<fst::Error> for Error {
|
error_from_sub_error! {
|
||||||
fn from(error: fst::Error) -> Error {
|
FieldIdMapMissingEntry => InternalError,
|
||||||
Error::InternalError(InternalError::Fst(error))
|
fst::Error => InternalError,
|
||||||
}
|
str::Utf8Error => InternalError,
|
||||||
}
|
ThreadPoolBuildError => InternalError,
|
||||||
|
SerializationError => InternalError,
|
||||||
impl From<GeoError> for Error {
|
GeoError => UserError,
|
||||||
fn from(error: GeoError) -> Error {
|
CriterionError => UserError,
|
||||||
Error::UserError(UserError::InvalidGeoField(error))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> From<grenad::Error<E>> for Error
|
impl<E> From<grenad::Error<E>> for Error
|
||||||
@ -121,12 +201,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<str::Utf8Error> for Error {
|
|
||||||
fn from(error: str::Utf8Error) -> Error {
|
|
||||||
Error::InternalError(InternalError::Utf8(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Infallible> for Error {
|
impl From<Infallible> for Error {
|
||||||
fn from(_error: Infallible) -> Error {
|
fn from(_error: Infallible) -> Error {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
@ -153,216 +227,6 @@ impl From<HeedError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ThreadPoolBuildError> for Error {
|
|
||||||
fn from(error: ThreadPoolBuildError) -> Error {
|
|
||||||
Error::InternalError(InternalError::RayonThreadPool(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FieldIdMapMissingEntry> for Error {
|
|
||||||
fn from(error: FieldIdMapMissingEntry) -> Error {
|
|
||||||
Error::InternalError(InternalError::FieldIdMapMissingEntry(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<InternalError> for Error {
|
|
||||||
fn from(error: InternalError) -> Error {
|
|
||||||
Error::InternalError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<UserError> for Error {
|
|
||||||
fn from(error: UserError) -> Error {
|
|
||||||
Error::UserError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SerializationError> for Error {
|
|
||||||
fn from(error: SerializationError) -> Error {
|
|
||||||
Error::InternalError(InternalError::Serialization(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::InternalError(error) => write!(f, "internal: {}.", error),
|
|
||||||
Self::IoError(error) => error.fmt(f),
|
|
||||||
Self::UserError(error) => error.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdError for Error {}
|
|
||||||
|
|
||||||
impl fmt::Display for InternalError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::DatabaseMissingEntry { db_name, key } => {
|
|
||||||
write!(f, "Missing {} in the {} database.", key.unwrap_or("key"), db_name)
|
|
||||||
}
|
|
||||||
Self::FieldIdMapMissingEntry(error) => error.fmt(f),
|
|
||||||
Self::FieldIdMappingMissingEntry { key } => {
|
|
||||||
write!(f, "Missing {} in the field id mapping.", key)
|
|
||||||
}
|
|
||||||
Self::Fst(error) => error.fmt(f),
|
|
||||||
Self::GrenadInvalidCompressionType => {
|
|
||||||
f.write_str("Invalid compression type have been specified to grenad.")
|
|
||||||
}
|
|
||||||
Self::GrenadInvalidFormatVersion => {
|
|
||||||
f.write_str("Invalid grenad file with an invalid version format.")
|
|
||||||
}
|
|
||||||
Self::IndexingMergingKeys { process } => {
|
|
||||||
write!(f, "Invalid merge while processing {}.", process)
|
|
||||||
}
|
|
||||||
Self::Serialization(error) => error.fmt(f),
|
|
||||||
Self::InvalidDatabaseTyping => HeedError::InvalidDatabaseTyping.fmt(f),
|
|
||||||
Self::RayonThreadPool(error) => error.fmt(f),
|
|
||||||
Self::SerdeJson(error) => error.fmt(f),
|
|
||||||
Self::DatabaseClosing => HeedError::DatabaseClosing.fmt(f),
|
|
||||||
Self::Store(error) => error.fmt(f),
|
|
||||||
Self::Utf8(error) => error.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdError for InternalError {}
|
|
||||||
|
|
||||||
impl fmt::Display for UserError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::InvalidFilter(error) => f.write_str(error),
|
|
||||||
Self::AttributeLimitReached => f.write_str("A document cannot contain more than 65,535 fields."),
|
|
||||||
Self::CriterionError(error) => write!(f, "{}", error),
|
|
||||||
Self::DocumentLimitReached => f.write_str("Maximum number of documents reached."),
|
|
||||||
Self::InvalidFacetsDistribution { invalid_facets_name } => {
|
|
||||||
let name_list =
|
|
||||||
invalid_facets_name.iter().map(AsRef::as_ref).collect::<Vec<_>>().join(", ");
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Invalid facet distribution, the fields `{}` are not set as filterable.",
|
|
||||||
name_list
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Self::InvalidGeoField(error) => write!(f, "{error}"),
|
|
||||||
Self::InvalidDocumentId { document_id } => {
|
|
||||||
let document_id = match document_id {
|
|
||||||
Value::String(id) => id.clone(),
|
|
||||||
_ => document_id.to_string(),
|
|
||||||
};
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Document identifier `{}` is invalid. \
|
|
||||||
A document identifier can be of type integer or string, \
|
|
||||||
only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_).",
|
|
||||||
document_id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Self::InvalidSortableAttribute { field, valid_fields } => {
|
|
||||||
let valid_names =
|
|
||||||
valid_fields.iter().map(AsRef::as_ref).collect::<Vec<_>>().join(", ");
|
|
||||||
|
|
||||||
if valid_names.is_empty() {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Attribute `{}` is not sortable. This index does not have configured sortable attributes.",
|
|
||||||
field
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Attribute `{}` is not sortable. Available sortable attributes are: `{}`.",
|
|
||||||
field, valid_names
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::SortRankingRuleMissing => f.write_str(
|
|
||||||
"The sort ranking rule must be specified in the \
|
|
||||||
ranking rules settings to use the sort parameter at search time.",
|
|
||||||
),
|
|
||||||
Self::MissingDocumentId { primary_key, document } => {
|
|
||||||
let json = serde_json::to_string(document).unwrap();
|
|
||||||
write!(f, "Document doesn't have a `{}` attribute: `{}`.", primary_key, json)
|
|
||||||
}
|
|
||||||
Self::MissingPrimaryKey => f.write_str("The primary key inference process failed because the engine did not find any fields containing `id` substring in their name. If your document identifier does not contain any `id` substring, you can set the primary key of the index."),
|
|
||||||
Self::MaxDatabaseSizeReached => f.write_str("Maximum database size has been reached."),
|
|
||||||
Self::NoSpaceLeftOnDevice => f.write_str("There is no more space left on the device. Consider increasing the size of the disk/partition."),
|
|
||||||
Self::InvalidStoreFile => f.write_str("The database file is in an invalid state."),
|
|
||||||
Self::PrimaryKeyCannotBeChanged(primary_key) => {
|
|
||||||
write!(f, "Index already has a primary key: `{}`.", primary_key)
|
|
||||||
}
|
|
||||||
Self::SerdeJson(error) => error.fmt(f),
|
|
||||||
Self::SortError(error) => write!(f, "{}", error),
|
|
||||||
Self::UnknownInternalDocumentId { document_id } => {
|
|
||||||
write!(f, "An unknown internal document id have been used: `{}`.", document_id)
|
|
||||||
}
|
|
||||||
Self::InvalidMinTypoWordLenSetting(one, two) => write!(f, "`minWordSizeForTypos` setting is invalid. `oneTypo` and `twoTypos` fields should be between `0` and `255`, and `twoTypos` should be greater or equals to `oneTypo` but found `oneTypo: {}` and twoTypos: {}`.", one, two),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdError for UserError {}
|
|
||||||
|
|
||||||
impl fmt::Display for FieldIdMapMissingEntry {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::FieldId { field_id, process } => {
|
|
||||||
write!(f, "unknown field id {} coming from the {} process", field_id, process)
|
|
||||||
}
|
|
||||||
Self::FieldName { field_name, process } => {
|
|
||||||
write!(f, "unknown field name {} coming from the {} process", field_name, process)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdError for FieldIdMapMissingEntry {}
|
|
||||||
|
|
||||||
impl From<GeoError> for UserError {
|
|
||||||
fn from(geo_error: GeoError) -> Self {
|
|
||||||
UserError::InvalidGeoField(geo_error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for GeoError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
GeoError::MissingLatitude { document_id } => {
|
|
||||||
write!(f, "Could not find latitude in the document with the id: `{document_id}`. Was expecting a `_geo.lat` field.")
|
|
||||||
}
|
|
||||||
GeoError::MissingLongitude { document_id } => {
|
|
||||||
write!(f, "Could not find longitude in the document with the id: `{document_id}`. Was expecting a `_geo.lng` field.")
|
|
||||||
}
|
|
||||||
GeoError::BadLatitude { document_id, value } => {
|
|
||||||
write!(f, "Could not parse latitude in the document with the id: `{document_id}`. Was expecting a number but instead got `{value}`.")
|
|
||||||
}
|
|
||||||
GeoError::BadLongitude { document_id, value } => {
|
|
||||||
write!(f, "Could not parse longitude in the document with the id: `{document_id}`. Was expecting a number but instead got `{value}`.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdError for GeoError {}
|
|
||||||
|
|
||||||
impl fmt::Display for SerializationError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Decoding { db_name: Some(name) } => {
|
|
||||||
write!(f, "decoding from the {} database failed", name)
|
|
||||||
}
|
|
||||||
Self::Decoding { db_name: None } => f.write_str("decoding failed"),
|
|
||||||
Self::Encoding { db_name: Some(name) } => {
|
|
||||||
write!(f, "encoding into the {} database failed", name)
|
|
||||||
}
|
|
||||||
Self::Encoding { db_name: None } => f.write_str("encoding failed"),
|
|
||||||
Self::InvalidNumberSerialization => f.write_str("number is not a valid finite number"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdError for SerializationError {}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn conditionally_lookup_for_error_message() {
|
fn conditionally_lookup_for_error_message() {
|
||||||
let prefix = "Attribute `name` is not sortable.";
|
let prefix = "Attribute `name` is not sortable.";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user