1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
//! Core functionality and types for BonsaiDb.
#![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.
)]
/// Types for creating and validating permissions.
pub mod permissions;
/// Database administration types and functionality.
pub mod admin;
/// Types for interacting with BonsaiDb.
pub mod connection;
pub mod document;
pub mod limits;
/// Types for defining database schema.
pub mod schema;
/// Types for executing transactions.
pub mod transaction;
/// Types for utilizing a lightweight atomic Key-Value store.
pub mod keyvalue;
/// Traits for tailoring a server.
pub mod api;
/// Key trait and related types.
pub mod key;
/// Types for implementing the BonsaiDb network protocol.
pub mod networking;
/// Types for Publish/Subscribe (`PubSub`) messaging.
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;
/// an enumeration of errors that this crate can produce
#[derive(Clone, thiserror::Error, Debug, Serialize, Deserialize)]
pub enum Error {
/// The database named `database_name` was created with a different schema
/// (`stored_schema`) than provided (`schema`).
#[error(
"database '{database_name}' was created with schema '{stored_schema}', not '{schema}'"
)]
SchemaMismatch {
/// The name of the database being accessed.
database_name: String,
/// The schema provided for the database.
schema: SchemaName,
/// The schema stored for the database.
stored_schema: SchemaName,
},
/// The [`SchemaName`] returned has already been registered.
#[error("schema '{0}' was already registered")]
SchemaAlreadyRegistered(SchemaName),
/// The [`SchemaName`] requested was not registered.
#[error("schema '{0}' is not registered")]
SchemaNotRegistered(SchemaName),
/// The [`ViewName`] returned has already been registered.
#[error("view '{0}' was already registered")]
ViewAlreadyRegistered(ViewName),
/// An invalid database name was specified. See
/// [`StorageConnection::create_database()`](connection::StorageConnection::create_database)
/// for database name requirements.
#[error("invalid database name: {0}")]
InvalidDatabaseName(String),
/// The database name given was not found.
#[error("database '{0}' was not found")]
DatabaseNotFound(String),
/// The view was not found.
#[error("view was not found")]
ViewNotFound,
/// The collection was not found.
#[error("collection was not found")]
CollectionNotFound,
/// The api invoked was not found.
#[error("api '{0}' was not found")]
ApiNotFound(ApiName),
/// The database name already exists.
#[error("a database with name '{0}' already exists")]
DatabaseNameAlreadyTaken(String),
/// An error occurred from networking.
#[error("a networking error occurred: '{0}'")]
Networking(networking::Error),
/// A `Collection` being added already exists. This can be caused by a collection name not being unique.
#[error("attempted to define a collection that already has been defined")]
CollectionAlreadyDefined,
/// An attempt to update a document that doesn't exist.
#[error("the requested document id {1} from collection {0} was not found")]
DocumentNotFound(CollectionName, Box<DocumentId>),
/// A value provided as a [`DocumentId`] exceeded [`DocumentId::MAX_LENGTH`].
#[error(
"an value was provided for a `DocumentId` that was larger than `DocumentId::MAX_LENGTH`"
)]
DocumentIdTooLong,
/// When updating a document, if a situation is detected where the contents
/// have changed on the server since the `Revision` provided, a Conflict
/// error will be returned.
#[error("a conflict was detected while updating document {1} from collection {0}")]
DocumentConflict(CollectionName, Box<Header>),
/// When saving a document in a collection with unique views, a document
/// emits a key that is already emitted by an existing ocument, this error
/// is returned.
#[error("a unique key violation occurred: document `{existing_document}` already has the same key as `{conflicting_document}` for {view}")]
UniqueKeyViolation {
/// The name of the view that the unique key violation occurred.
view: ViewName,
/// The document that caused the violation.
conflicting_document: Box<Header>,
/// The document that already uses the same key.
existing_document: Box<Header>,
},
/// When pushing a document, an error occurred while generating the next unique id.
#[error("an error occurred generating a new unique id for {0}: {1}")]
DocumentPush(CollectionName, NextValueError),
/// An invalid name was specified during schema creation.
#[error("an invalid name was used in a schema: {0}")]
InvalidName(#[from] schema::InvalidNameError),
/// Permission was denied.
#[error("permission error: {0}")]
PermissionDenied(#[from] actionable::PermissionDenied),
/// An internal error handling passwords was encountered.
#[error("error with password: {0}")]
Password(String),
/// The user specified was not found. This will not be returned in response
/// to an invalid username being used during login. It will be returned in
/// other APIs that operate upon users.
#[error("user not found")]
UserNotFound,
/// An error occurred converting from bytes to Utf-8.
#[error("invalid string: {0}")]
InvalidUnicode(String),
/// The credentials specified are not valid.
#[error("invalid credentials")]
InvalidCredentials,
/// Returned when the a view's reduce() function is unimplemented.
#[error("reduce is unimplemented")]
ReduceUnimplemented,
/// A floating point operation yielded Not a Number.
#[error("floating point operation yielded NaN")]
NotANumber,
/// An error while operating with a time
#[error("time error: {0}")]
Time(#[from] TimeError),
/// An error from another crate.
#[error("error from {origin}: {error}")]
Other {
/// The origin of the error.
origin: String,
/// The error message.
error: String,
},
}
impl Error {
/// Returns an instance of [`Self::Other`] with the given parameters.
pub fn other(origin: impl Display, error: impl Display) -> Self {
Self::Other {
origin: origin.to_string(),
error: error.to_string(),
}
}
/// Returns true if this error is a [`Error::UniqueKeyViolation`] from
/// `View`.
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
}
/// Returns the header of the conflicting document if this error is a
/// [`Error::DocumentConflict`] from `Collection`.
#[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)
}
}
/// Shared schemas and utilities used for unit testing.
#[cfg(any(feature = "test-util", test))]
#[allow(missing_docs)]
pub mod test_util;
/// When true, encryption was enabled at build-time.
#[cfg(feature = "encryption")]
pub const ENCRYPTION_ENABLED: bool = true;
/// When true, encryption was enabled at build-time.
#[cfg(not(feature = "encryption"))]
pub const ENCRYPTION_ENABLED: bool = false;
/// A type that implements [`Error`](std::error::Error) and is threadsafe.
pub trait AnyError: std::error::Error + Send + Sync + 'static {}
impl<T> AnyError for T where T: std::error::Error + Send + Sync + 'static {}