2021-06-23 13:56:13 +02:00
|
|
|
use std::collections::HashSet;
|
2021-06-14 16:58:38 +02:00
|
|
|
use std::convert::Infallible;
|
2021-06-10 17:31:08 +02:00
|
|
|
use std::error::Error as StdError;
|
2021-06-14 16:58:38 +02:00
|
|
|
use std::{fmt, io, str};
|
2021-06-09 17:05:46 +02:00
|
|
|
|
2021-06-16 18:33:33 +02:00
|
|
|
use heed::{Error as HeedError, MdbError};
|
2021-06-14 16:58:38 +02:00
|
|
|
use rayon::ThreadPoolBuildError;
|
2021-06-10 17:31:08 +02:00
|
|
|
use serde_json::{Map, Value};
|
|
|
|
|
2021-10-25 11:58:00 +02:00
|
|
|
use crate::search::ParserRule;
|
2021-09-27 19:07:22 +02:00
|
|
|
use crate::{CriterionError, DocumentId, FieldId, SortError};
|
2021-06-10 15:55:22 +02:00
|
|
|
|
2021-06-10 17:31:08 +02:00
|
|
|
pub type Object = Map<String, Value>;
|
2021-06-09 17:05:46 +02:00
|
|
|
|
2021-09-02 15:57:40 +02:00
|
|
|
pub fn is_reserved_keyword(keyword: &str) -> bool {
|
2021-09-20 17:21:02 +02:00
|
|
|
["_geo", "_geoDistance", "_geoPoint", "_geoRadius"].contains(&keyword)
|
2021-09-02 15:57:40 +02:00
|
|
|
}
|
|
|
|
|
2021-06-10 17:31:08 +02:00
|
|
|
#[derive(Debug)]
|
2021-06-09 17:05:46 +02:00
|
|
|
pub enum Error {
|
|
|
|
InternalError(InternalError),
|
|
|
|
IoError(io::Error),
|
|
|
|
UserError(UserError),
|
|
|
|
}
|
|
|
|
|
2021-06-10 17:31:08 +02:00
|
|
|
#[derive(Debug)]
|
2021-06-09 17:05:46 +02:00
|
|
|
pub enum InternalError {
|
2021-06-14 16:58:38 +02:00
|
|
|
DatabaseClosing,
|
2021-06-10 17:31:08 +02:00
|
|
|
DatabaseMissingEntry { db_name: &'static str, key: Option<&'static str> },
|
2021-06-09 17:05:46 +02:00
|
|
|
FieldIdMapMissingEntry(FieldIdMapMissingEntry),
|
2021-06-14 16:58:38 +02:00
|
|
|
Fst(fst::Error),
|
|
|
|
GrenadInvalidCompressionType,
|
2021-06-10 17:31:08 +02:00
|
|
|
IndexingMergingKeys { process: &'static str },
|
2021-06-10 15:55:22 +02:00
|
|
|
InvalidDatabaseTyping,
|
2021-06-14 16:58:38 +02:00
|
|
|
RayonThreadPool(ThreadPoolBuildError),
|
|
|
|
SerdeJson(serde_json::Error),
|
|
|
|
Serialization(SerializationError),
|
|
|
|
Store(MdbError),
|
|
|
|
Utf8(str::Utf8Error),
|
2021-06-10 15:55:22 +02:00
|
|
|
}
|
|
|
|
|
2021-06-10 17:31:08 +02:00
|
|
|
#[derive(Debug)]
|
2021-06-10 15:55:22 +02:00
|
|
|
pub enum SerializationError {
|
|
|
|
Decoding { db_name: Option<&'static str> },
|
|
|
|
Encoding { db_name: Option<&'static str> },
|
|
|
|
InvalidNumberSerialization,
|
2021-06-09 17:05:46 +02:00
|
|
|
}
|
|
|
|
|
2021-06-10 17:31:08 +02:00
|
|
|
#[derive(Debug)]
|
2021-06-09 17:05:46 +02:00
|
|
|
pub enum FieldIdMapMissingEntry {
|
2021-06-15 11:10:50 +02:00
|
|
|
FieldId { field_id: FieldId, process: &'static str },
|
|
|
|
FieldName { field_name: String, process: &'static str },
|
2021-06-09 17:05:46 +02:00
|
|
|
}
|
|
|
|
|
2021-06-10 17:31:08 +02:00
|
|
|
#[derive(Debug)]
|
2021-06-09 17:05:46 +02:00
|
|
|
pub enum UserError {
|
2021-06-10 15:55:22 +02:00
|
|
|
AttributeLimitReached,
|
2021-09-22 16:02:07 +02:00
|
|
|
CriterionError(CriterionError),
|
2021-06-10 15:55:22 +02:00
|
|
|
DocumentLimitReached,
|
2021-06-10 17:31:08 +02:00
|
|
|
InvalidDocumentId { document_id: Value },
|
2021-06-23 13:56:13 +02:00
|
|
|
InvalidFacetsDistribution { invalid_facets_name: HashSet<String> },
|
2021-10-26 17:49:35 +02:00
|
|
|
InvalidFilter(FilterError),
|
2021-09-02 15:57:40 +02:00
|
|
|
InvalidGeoField { document_id: Value, object: Value },
|
2021-08-23 11:37:18 +02:00
|
|
|
InvalidSortableAttribute { field: String, valid_fields: HashSet<String> },
|
2021-09-07 10:37:57 +02:00
|
|
|
SortRankingRuleMissing,
|
2021-06-14 16:58:38 +02:00
|
|
|
InvalidStoreFile,
|
2021-06-23 13:56:13 +02:00
|
|
|
MaxDatabaseSizeReached,
|
2021-10-26 17:49:35 +02:00
|
|
|
MissingDocumentId { primary_key: String, document: Object },
|
2021-06-10 15:55:22 +02:00
|
|
|
MissingPrimaryKey,
|
|
|
|
NoSpaceLeftOnDevice,
|
2021-10-26 17:49:35 +02:00
|
|
|
PrimaryKeyCannotBeChanged(String),
|
2021-06-14 16:58:38 +02:00
|
|
|
SerdeJson(serde_json::Error),
|
2021-09-27 19:07:22 +02:00
|
|
|
SortError(SortError),
|
2021-06-14 16:58:38 +02:00
|
|
|
UnknownInternalDocumentId { document_id: DocumentId },
|
2021-06-10 15:55:22 +02:00
|
|
|
}
|
|
|
|
|
2021-10-26 17:49:35 +02:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum FilterError {
|
|
|
|
InvalidAttribute { field: String, valid_fields: HashSet<String> },
|
|
|
|
ReservedKeyword { field: String, context: Option<String> },
|
|
|
|
Syntax(pest::error::Error<ParserRule>),
|
|
|
|
}
|
|
|
|
|
2021-06-10 15:55:22 +02:00
|
|
|
impl From<io::Error> for Error {
|
|
|
|
fn from(error: io::Error) -> Error {
|
|
|
|
// TODO must be improved and more precise
|
|
|
|
Error::IoError(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-14 16:58:38 +02:00
|
|
|
impl From<fst::Error> for Error {
|
|
|
|
fn from(error: fst::Error) -> Error {
|
|
|
|
Error::InternalError(InternalError::Fst(error))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-16 18:33:33 +02:00
|
|
|
impl<E> From<grenad::Error<E>> for Error
|
|
|
|
where
|
|
|
|
Error: From<E>,
|
|
|
|
{
|
2021-06-14 16:58:38 +02:00
|
|
|
fn from(error: grenad::Error<E>) -> Error {
|
|
|
|
match error {
|
|
|
|
grenad::Error::Io(error) => Error::IoError(error),
|
|
|
|
grenad::Error::Merge(error) => Error::from(error),
|
|
|
|
grenad::Error::InvalidCompressionType => {
|
|
|
|
Error::InternalError(InternalError::GrenadInvalidCompressionType)
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-06-14 16:58:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<str::Utf8Error> for Error {
|
|
|
|
fn from(error: str::Utf8Error) -> Error {
|
|
|
|
Error::InternalError(InternalError::Utf8(error))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Infallible> for Error {
|
|
|
|
fn from(_error: Infallible) -> Error {
|
|
|
|
unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-10 15:55:22 +02:00
|
|
|
impl From<HeedError> for Error {
|
|
|
|
fn from(error: HeedError) -> Error {
|
|
|
|
use self::Error::*;
|
|
|
|
use self::InternalError::*;
|
|
|
|
use self::SerializationError::*;
|
|
|
|
use self::UserError::*;
|
2021-06-09 17:05:46 +02:00
|
|
|
|
2021-06-10 15:55:22 +02:00
|
|
|
match error {
|
|
|
|
HeedError::Io(error) => Error::from(error),
|
2021-06-15 17:20:33 +02:00
|
|
|
HeedError::Mdb(MdbError::MapFull) => UserError(MaxDatabaseSizeReached),
|
2021-06-10 15:55:22 +02:00
|
|
|
HeedError::Mdb(MdbError::Invalid) => UserError(InvalidStoreFile),
|
2021-06-14 16:58:38 +02:00
|
|
|
HeedError::Mdb(error) => InternalError(Store(error)),
|
|
|
|
HeedError::Encoding => InternalError(Serialization(Encoding { db_name: None })),
|
|
|
|
HeedError::Decoding => InternalError(Serialization(Decoding { db_name: None })),
|
2021-06-10 15:55:22 +02:00
|
|
|
HeedError::InvalidDatabaseTyping => InternalError(InvalidDatabaseTyping),
|
|
|
|
HeedError::DatabaseClosing => InternalError(DatabaseClosing),
|
|
|
|
}
|
|
|
|
}
|
2021-06-09 17:05:46 +02:00
|
|
|
}
|
2021-06-10 17:31:08 +02:00
|
|
|
|
2021-06-14 16:58:38 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-26 17:49:35 +02:00
|
|
|
impl From<FilterError> for Error {
|
|
|
|
fn from(error: FilterError) -> Error {
|
|
|
|
Error::UserError(UserError::InvalidFilter(error))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-14 16:58:38 +02:00
|
|
|
impl From<SerializationError> for Error {
|
|
|
|
fn from(error: SerializationError) -> Error {
|
|
|
|
Error::InternalError(InternalError::Serialization(error))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-10 17:31:08 +02:00
|
|
|
impl fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
2021-10-26 17:49:35 +02:00
|
|
|
Self::InternalError(error) => write!(f, "internal: {}.", error),
|
2021-06-10 17:31:08 +02:00
|
|
|
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 } => {
|
2021-10-26 17:49:35 +02:00
|
|
|
write!(f, "Missing {} in the {} database.", key.unwrap_or("key"), db_name)
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-06-10 17:31:08 +02:00
|
|
|
Self::FieldIdMapMissingEntry(error) => error.fmt(f),
|
2021-06-14 16:58:38 +02:00
|
|
|
Self::Fst(error) => error.fmt(f),
|
|
|
|
Self::GrenadInvalidCompressionType => {
|
2021-10-26 17:49:35 +02:00
|
|
|
f.write_str("Invalid compression type have been specified to grenad.")
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-06-10 17:31:08 +02:00
|
|
|
Self::IndexingMergingKeys { process } => {
|
2021-10-26 17:49:35 +02:00
|
|
|
write!(f, "Invalid merge while processing {}.", process)
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-06-14 16:58:38 +02:00
|
|
|
Self::Serialization(error) => error.fmt(f),
|
2021-06-10 17:31:08 +02:00
|
|
|
Self::InvalidDatabaseTyping => HeedError::InvalidDatabaseTyping.fmt(f),
|
2021-06-14 16:58:38 +02:00
|
|
|
Self::RayonThreadPool(error) => error.fmt(f),
|
|
|
|
Self::SerdeJson(error) => error.fmt(f),
|
2021-06-10 17:31:08 +02:00
|
|
|
Self::DatabaseClosing => HeedError::DatabaseClosing.fmt(f),
|
2021-06-14 16:58:38 +02:00
|
|
|
Self::Store(error) => error.fmt(f),
|
|
|
|
Self::Utf8(error) => error.fmt(f),
|
2021-06-10 17:31:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StdError for InternalError {}
|
|
|
|
|
|
|
|
impl fmt::Display for UserError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
2021-10-26 17:49:35 +02:00
|
|
|
Self::AttributeLimitReached => f.write_str("Maximum number of attributes reached."),
|
2021-09-22 16:59:23 +02:00
|
|
|
Self::CriterionError(error) => write!(f, "{}", error),
|
2021-10-26 17:49:35 +02:00
|
|
|
Self::DocumentLimitReached => f.write_str("Maximum number of documents reached."),
|
2021-06-23 13:56:13 +02:00
|
|
|
Self::InvalidFacetsDistribution { invalid_facets_name } => {
|
|
|
|
let name_list =
|
|
|
|
invalid_facets_name.iter().map(AsRef::as_ref).collect::<Vec<_>>().join(", ");
|
|
|
|
write!(
|
|
|
|
f,
|
2021-10-26 17:49:35 +02:00
|
|
|
"Invalid facet distribution, the fields `{}` are not set as filterable.",
|
2021-06-23 13:56:13 +02:00
|
|
|
name_list
|
|
|
|
)
|
|
|
|
}
|
2021-10-25 11:58:00 +02:00
|
|
|
Self::InvalidFilter(error) => error.fmt(f),
|
2021-09-02 15:57:40 +02:00
|
|
|
Self::InvalidGeoField { document_id, object } => write!(
|
|
|
|
f,
|
2021-10-26 17:49:35 +02:00
|
|
|
"The document with the id: `{}` contains an invalid _geo field: `{}`.",
|
2021-09-02 15:57:40 +02:00
|
|
|
document_id, object
|
|
|
|
),
|
2021-06-10 17:31:08 +02:00
|
|
|
Self::InvalidDocumentId { document_id } => {
|
2021-10-26 17:49:35 +02:00
|
|
|
let document_id = match document_id {
|
|
|
|
Value::String(id) => id.clone(),
|
|
|
|
_ => document_id.to_string(),
|
|
|
|
};
|
2021-06-22 11:31:58 +02:00
|
|
|
write!(
|
|
|
|
f,
|
2021-10-26 17:49:35 +02:00
|
|
|
"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
|
2021-06-22 11:31:58 +02:00
|
|
|
)
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-08-23 11:37:18 +02:00
|
|
|
Self::InvalidSortableAttribute { field, valid_fields } => {
|
|
|
|
let valid_names =
|
|
|
|
valid_fields.iter().map(AsRef::as_ref).collect::<Vec<_>>().join(", ");
|
|
|
|
write!(
|
|
|
|
f,
|
2021-10-26 17:49:35 +02:00
|
|
|
"Attribute `{}` is not sortable. Available sortable attributes are: `{}`.",
|
2021-08-23 11:37:18 +02:00
|
|
|
field, valid_names
|
|
|
|
)
|
|
|
|
}
|
2021-09-07 10:37:57 +02:00
|
|
|
Self::SortRankingRuleMissing => f.write_str(
|
2021-10-26 17:49:35 +02:00
|
|
|
"The sort ranking rule must be specified in the \
|
|
|
|
ranking rules settings to use the sort parameter at search time.",
|
2021-09-07 10:37:57 +02:00
|
|
|
),
|
2021-10-26 17:49:35 +02:00
|
|
|
Self::MissingDocumentId { primary_key, document } => {
|
2021-06-10 17:31:08 +02:00
|
|
|
let json = serde_json::to_string(document).unwrap();
|
2021-10-26 17:49:35 +02:00
|
|
|
write!(f, "Document doesn't have a `{}` attribute: `{}`.", primary_key, json)
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-10-26 17:49:35 +02:00
|
|
|
Self::MissingPrimaryKey => f.write_str("Missing primary key."),
|
|
|
|
Self::MaxDatabaseSizeReached => f.write_str("Maximum database size reached."),
|
2021-06-10 17:31:08 +02:00
|
|
|
// TODO where can we find it instead of writing the text ourselves?
|
2021-10-26 17:49:35 +02:00
|
|
|
Self::NoSpaceLeftOnDevice => f.write_str("No space left on device."),
|
|
|
|
Self::InvalidStoreFile => f.write_str("Store file is not a valid database file."),
|
|
|
|
Self::PrimaryKeyCannotBeChanged(primary_key) => {
|
|
|
|
write!(f, "Index already has a primary key: `{}`.", primary_key)
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-06-14 16:58:38 +02:00
|
|
|
Self::SerdeJson(error) => error.fmt(f),
|
2021-09-27 19:07:22 +02:00
|
|
|
Self::SortError(error) => write!(f, "{}", error),
|
2021-06-14 16:58:38 +02:00
|
|
|
Self::UnknownInternalDocumentId { document_id } => {
|
2021-10-26 17:49:35 +02:00
|
|
|
write!(f, "An unknown internal document id have been used: `{}`.", document_id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for FilterError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Self::InvalidAttribute { field, valid_fields } => write!(
|
|
|
|
f,
|
|
|
|
"Attribute `{}` is not filterable. Available filterable attributes are: `{}`.",
|
|
|
|
field,
|
|
|
|
valid_fields
|
|
|
|
.clone()
|
|
|
|
.into_iter()
|
|
|
|
.reduce(|left, right| left + "`, `" + &right)
|
|
|
|
.unwrap_or_default()
|
|
|
|
),
|
|
|
|
Self::ReservedKeyword { field, context: Some(context) } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"`{}` is a reserved keyword and thus can't be used as a filter expression. {}",
|
|
|
|
field, context
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Self::ReservedKeyword { field, context: None } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"`{}` is a reserved keyword and thus can't be used as a filter expression.",
|
|
|
|
field
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Self::Syntax(syntax_helper) => {
|
|
|
|
write!(f, "Invalid syntax for the filter parameter: `{}`.", syntax_helper)
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-06-10 17:31:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StdError for UserError {}
|
|
|
|
|
|
|
|
impl fmt::Display for FieldIdMapMissingEntry {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
2021-06-15 11:10:50 +02:00
|
|
|
Self::FieldId { field_id, process } => {
|
|
|
|
write!(f, "unknown field id {} coming from the {} process", field_id, process)
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-06-15 11:10:50 +02:00
|
|
|
Self::FieldName { field_name, process } => {
|
|
|
|
write!(f, "unknown field name {} coming from the {} process", field_name, process)
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-06-10 17:31:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StdError for FieldIdMapMissingEntry {}
|
|
|
|
|
|
|
|
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)
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-06-10 17:31:08 +02:00
|
|
|
Self::Decoding { db_name: None } => f.write_str("decoding failed"),
|
|
|
|
Self::Encoding { db_name: Some(name) } => {
|
|
|
|
write!(f, "encoding into the {} database failed", name)
|
2021-06-16 18:33:33 +02:00
|
|
|
}
|
2021-06-10 17:31:08 +02:00
|
|
|
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 {}
|