use std::convert::Infallible; use std::error::Error as StdError; use std::{fmt, io, str}; use heed::{MdbError, Error as HeedError}; use rayon::ThreadPoolBuildError; use serde_json::{Map, Value}; use crate::search::ParserRule; use crate::{DocumentId, FieldId}; pub type Object = Map; #[derive(Debug)] pub enum Error { InternalError(InternalError), IoError(io::Error), UserError(UserError), } #[derive(Debug)] pub enum InternalError { DatabaseClosing, DatabaseMissingEntry { db_name: &'static str, key: Option<&'static str> }, FieldIdMapMissingEntry(FieldIdMapMissingEntry), Fst(fst::Error), GrenadInvalidCompressionType, IndexingMergingKeys { process: &'static str }, InvalidDatabaseTyping, RayonThreadPool(ThreadPoolBuildError), SerdeJson(serde_json::Error), Serialization(SerializationError), Store(MdbError), Utf8(str::Utf8Error), } #[derive(Debug)] pub enum SerializationError { Decoding { db_name: Option<&'static str> }, Encoding { db_name: Option<&'static str> }, InvalidNumberSerialization, } #[derive(Debug)] pub enum FieldIdMapMissingEntry { FieldId { field_id: FieldId, process: &'static str }, FieldName { field_name: String, process: &'static str }, } #[derive(Debug)] pub enum UserError { AttributeLimitReached, Csv(csv::Error), DatabaseSizeReached, DocumentLimitReached, FilterParsing(pest::error::Error), InvalidCriterionName { name: String }, InvalidDocumentId { document_id: Value }, InvalidStoreFile, MissingDocumentId { document: Object }, MissingPrimaryKey, NoSpaceLeftOnDevice, PrimaryKeyCannotBeChanged, PrimaryKeyCannotBeReset, SerdeJson(serde_json::Error), UnknownInternalDocumentId { document_id: DocumentId }, } impl From for Error { fn from(error: io::Error) -> Error { // TODO must be improved and more precise Error::IoError(error) } } impl From for Error { fn from(error: fst::Error) -> Error { Error::InternalError(InternalError::Fst(error)) } } impl From> for Error where Error: From { fn from(error: grenad::Error) -> Error { match error { grenad::Error::Io(error) => Error::IoError(error), grenad::Error::Merge(error) => Error::from(error), grenad::Error::InvalidCompressionType => { Error::InternalError(InternalError::GrenadInvalidCompressionType) }, } } } impl From for Error { fn from(error: str::Utf8Error) -> Error { Error::InternalError(InternalError::Utf8(error)) } } impl From for Error { fn from(_error: Infallible) -> Error { unreachable!() } } impl From for Error { fn from(error: HeedError) -> Error { use self::Error::*; use self::InternalError::*; use self::SerializationError::*; use self::UserError::*; match error { HeedError::Io(error) => Error::from(error), HeedError::Mdb(MdbError::MapFull) => UserError(DatabaseSizeReached), HeedError::Mdb(MdbError::Invalid) => UserError(InvalidStoreFile), HeedError::Mdb(error) => InternalError(Store(error)), HeedError::Encoding => InternalError(Serialization(Encoding { db_name: None })), HeedError::Decoding => InternalError(Serialization(Decoding { db_name: None })), HeedError::InvalidDatabaseTyping => InternalError(InvalidDatabaseTyping), HeedError::DatabaseClosing => InternalError(DatabaseClosing), } } } impl From for Error { fn from(error: ThreadPoolBuildError) -> Error { Error::InternalError(InternalError::RayonThreadPool(error)) } } impl From for Error { fn from(error: FieldIdMapMissingEntry) -> Error { Error::InternalError(InternalError::FieldIdMapMissingEntry(error)) } } impl From for Error { fn from(error: InternalError) -> Error { Error::InternalError(error) } } impl From for Error { fn from(error: UserError) -> Error { Error::UserError(error) } } impl From 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::Fst(error) => error.fmt(f), Self::GrenadInvalidCompressionType => { f.write_str("invalid compression type have been specified to grenad") }, 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::AttributeLimitReached => f.write_str("maximum number of attributes reached"), Self::Csv(error) => error.fmt(f), Self::DocumentLimitReached => f.write_str("maximum number of documents reached"), Self::FilterParsing(error) => error.fmt(f), Self::InvalidCriterionName { name } => write!(f, "invalid criterion {}", name), Self::InvalidDocumentId { document_id } => { let json = serde_json::to_string(document_id).unwrap(); write!(f, "document identifier is invalid {}", json) }, Self::MissingDocumentId { document } => { let json = serde_json::to_string(document).unwrap(); write!(f, "document doesn't have an identifier {}", json) }, Self::MissingPrimaryKey => f.write_str("missing primary key"), Self::DatabaseSizeReached => f.write_str("database size reached"), // TODO where can we find it instead of writing the text ourselves? 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 => { f.write_str("primary key cannot be changed if the database contains documents") }, Self::PrimaryKeyCannotBeReset => { f.write_str("primary key cannot be reset if the database contains documents") }, Self::SerdeJson(error) => error.fmt(f), Self::UnknownInternalDocumentId { document_id } => { write!(f, "an unknown internal document id have been used ({})", document_id) }, } } } 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 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 {}