#![forbid(unsafe_code)]
#![warn(
clippy::cargo,
missing_docs,
// clippy::missing_docs_in_private_items,
clippy::pedantic,
future_incompatible,
rust_2018_idioms,
)]
#![allow(
clippy::missing_errors_doc, // TODO clippy::missing_errors_doc
clippy::option_if_let_else,
clippy::module_name_repetitions,
clippy::use_self, // false positives that can't be allowed on the type declaration itself.
)]
pub mod permissions;
pub mod admin;
pub mod connection;
pub mod document;
pub mod limits;
pub mod schema;
pub mod transaction;
pub mod keyvalue;
pub mod api;
pub mod key;
pub mod networking;
pub mod pubsub;
use std::fmt::Display;
use std::string::FromUtf8Error;
use schema::{view, CollectionName, SchemaName, ViewName};
use serde::{Deserialize, Serialize};
pub use {
actionable, arc_bytes, async_trait, circulate, num_traits, ordered_varint, transmog,
transmog_pot,
};
use crate::api::ApiName;
use crate::connection::HasSchema;
use crate::document::{DocumentId, Header, InvalidHexadecimal};
use crate::key::time::TimeError;
use crate::key::NextValueError;
use crate::schema::InsertError;
#[derive(Clone, thiserror::Error, Debug, Serialize, Deserialize)]
pub enum Error {
#[error(
"database '{database_name}' was created with schema '{stored_schema}', not '{schema}'"
)]
SchemaMismatch {
database_name: String,
schema: SchemaName,
stored_schema: SchemaName,
},
#[error("schema '{0}' was already registered")]
SchemaAlreadyRegistered(SchemaName),
#[error("schema '{0}' is not registered")]
SchemaNotRegistered(SchemaName),
#[error("view '{0}' was already registered")]
ViewAlreadyRegistered(ViewName),
#[error("invalid database name: {0}")]
InvalidDatabaseName(String),
#[error("database '{0}' was not found")]
DatabaseNotFound(String),
#[error("view was not found")]
ViewNotFound,
#[error("collection was not found")]
CollectionNotFound,
#[error("api '{0}' was not found")]
ApiNotFound(ApiName),
#[error("a database with name '{0}' already exists")]
DatabaseNameAlreadyTaken(String),
#[error("a networking error occurred: '{0}'")]
Networking(networking::Error),
#[error("attempted to define a collection that already has been defined")]
CollectionAlreadyDefined,
#[error("the requested document id {1} from collection {0} was not found")]
DocumentNotFound(CollectionName, Box<DocumentId>),
#[error(
"an value was provided for a `DocumentId` that was larger than `DocumentId::MAX_LENGTH`"
)]
DocumentIdTooLong,
#[error("a conflict was detected while updating document {1} from collection {0}")]
DocumentConflict(CollectionName, Box<Header>),
#[error("a unique key violation occurred: document `{existing_document}` already has the same key as `{conflicting_document}` for {view}")]
UniqueKeyViolation {
view: ViewName,
conflicting_document: Box<Header>,
existing_document: Box<Header>,
},
#[error("an error occurred generating a new unique id for {0}: {1}")]
DocumentPush(CollectionName, NextValueError),
#[error("an invalid name was used in a schema: {0}")]
InvalidName(#[from] schema::InvalidNameError),
#[error("permission error: {0}")]
PermissionDenied(#[from] actionable::PermissionDenied),
#[error("error with password: {0}")]
Password(String),
#[error("user not found")]
UserNotFound,
#[error("invalid string: {0}")]
InvalidUnicode(String),
#[error("invalid credentials")]
InvalidCredentials,
#[error("reduce is unimplemented")]
ReduceUnimplemented,
#[error("floating point operation yielded NaN")]
NotANumber,
#[error("time error: {0}")]
Time(#[from] TimeError),
#[error("error from {origin}: {error}")]
Other {
origin: String,
error: String,
},
}
impl Error {
pub fn other(origin: impl Display, error: impl Display) -> Self {
Self::Other {
origin: origin.to_string(),
error: error.to_string(),
}
}
pub fn is_unique_key_error<View: schema::View, C: HasSchema>(&self, connection: &C) -> bool {
if let Self::UniqueKeyViolation { view, .. } = self {
if let Ok(schema_view) = connection.schematic().view::<View>() {
return view == &schema_view.view_name();
}
}
false
}
#[must_use]
pub fn conflicting_document<Collection: schema::Collection>(&self) -> Option<Header> {
match self {
Self::DocumentConflict(collection, header)
if collection == &Collection::collection_name() =>
{
Some(header.as_ref().clone())
}
_ => None,
}
}
}
impl From<pot::Error> for Error {
fn from(err: pot::Error) -> Self {
Self::other("pot", err)
}
}
impl<T> From<InsertError<T>> for Error {
fn from(err: InsertError<T>) -> Self {
err.error
}
}
impl From<view::Error> for Error {
fn from(err: view::Error) -> Self {
Self::other("view", err)
}
}
impl From<FromUtf8Error> for Error {
fn from(err: FromUtf8Error) -> Self {
Self::InvalidUnicode(err.to_string())
}
}
impl From<InvalidHexadecimal> for Error {
fn from(err: InvalidHexadecimal) -> Self {
Self::other("invalid hexadecimal", err)
}
}
#[cfg(any(feature = "test-util", test))]
#[allow(missing_docs)]
pub mod test_util;
#[cfg(feature = "encryption")]
pub const ENCRYPTION_ENABLED: bool = true;
#[cfg(not(feature = "encryption"))]
pub const ENCRYPTION_ENABLED: bool = false;
pub trait AnyError: std::error::Error + Send + Sync + 'static {}
impl<T> AnyError for T where T: std::error::Error + Send + Sync + 'static {}