1
1
//! Core functionality and types for BonsaiDb.
2

            
3
#![forbid(unsafe_code)]
4
#![warn(
5
    clippy::cargo,
6
    missing_docs,
7
    // clippy::missing_docs_in_private_items,
8
    clippy::nursery,
9
    clippy::pedantic,
10
    future_incompatible,
11
    rust_2018_idioms,
12
)]
13
#![allow(
14
    clippy::missing_errors_doc, // TODO clippy::missing_errors_doc
15
    clippy::option_if_let_else,
16
    clippy::module_name_repetitions,
17
)]
18

            
19
/// Types for creating and validating permissions.
20
pub mod permissions;
21

            
22
/// Database administration types and functionality.
23
pub mod admin;
24
/// Types for interacting with BonsaiDb.
25
pub mod connection;
26
/// Types for interacting with `Document`s.
27
pub mod document;
28
/// Limits used within BonsaiDb.
29
pub mod limits;
30
/// Types for defining database schema.
31
pub mod schema;
32
/// Types for executing transactions.
33
pub mod transaction;
34

            
35
/// Types for utilizing a lightweight atomic Key-Value store.
36
pub mod keyvalue;
37

            
38
/// Traits for tailoring a server.
39
pub mod custom_api;
40

            
41
/// Key trait and related types.
42
pub mod key;
43

            
44
#[cfg(feature = "networking")]
45
/// Types for implementing the BonsaiDb network protocol.
46
pub mod networking;
47

            
48
/// Types for Publish/Subscribe (`PubSub`) messaging.
49
pub mod pubsub;
50

            
51
use std::string::FromUtf8Error;
52

            
53
pub use actionable;
54
pub use arc_bytes;
55
pub use async_trait;
56
pub use circulate;
57
pub use num_traits;
58
pub use ordered_varint;
59
use schema::{view, CollectionName, SchemaName, ViewName};
60
use serde::{Deserialize, Serialize};
61
pub use transmog;
62
pub use transmog_pot;
63

            
64
use crate::{
65
    document::{DocumentId, Header, InvalidHexadecimal},
66
    key::NextValueError,
67
    schema::InsertError,
68
};
69

            
70
/// an enumeration of errors that this crate can produce
71
3124
#[derive(Clone, thiserror::Error, Debug, Serialize, Deserialize)]
72
pub enum Error {
73
    /// The database named `database_name` was created with a different schema
74
    /// (`stored_schema`) than provided (`schema`).
75
    #[error(
76
        "database '{database_name}' was created with schema '{stored_schema}', not '{schema}'"
77
    )]
78
    SchemaMismatch {
79
        /// The name of the database being accessed.
80
        database_name: String,
81

            
82
        /// The schema provided for the database.
83
        schema: SchemaName,
84

            
85
        /// The schema stored for the database.
86
        stored_schema: SchemaName,
87
    },
88

            
89
    /// The [`SchemaName`] returned has already been registered.
90
    #[error("schema '{0}' was already registered")]
91
    SchemaAlreadyRegistered(SchemaName),
92

            
93
    /// The [`SchemaName`] requested was not registered.
94
    #[error("schema '{0}' is not registered")]
95
    SchemaNotRegistered(SchemaName),
96

            
97
    /// An invalid database name was specified. See
98
    /// [`StorageConnection::create_database()`](connection::StorageConnection::create_database)
99
    /// for database name requirements.
100
    #[error("invalid database name: {0}")]
101
    InvalidDatabaseName(String),
102

            
103
    /// The database name given was not found.
104
    #[error("database '{0}' was not found")]
105
    DatabaseNotFound(String),
106

            
107
    /// The database name already exists.
108
    #[error("a database with name '{0}' already exists")]
109
    DatabaseNameAlreadyTaken(String),
110

            
111
    /// An error from interacting with local storage.
112
    #[error("error from storage: {0}")]
113
    Database(String),
114

            
115
    /// An error serializing data.
116
    #[error("error serializing: {0}")]
117
    Serialization(String),
118

            
119
    /// An error from interacting with a server.
120
    #[error("error from server: {0}")]
121
    Server(String),
122

            
123
    /// An error occurred from the QUIC transport layer.
124
    #[error("a transport error occurred: '{0}'")]
125
    Transport(String),
126

            
127
    /// An error occurred from the websocket transport layer.
128
    #[cfg(feature = "websockets")]
129
    #[error("a websocket error occurred: '{0}'")]
130
    Websocket(String),
131

            
132
    /// An error occurred from networking.
133
    #[cfg(feature = "networking")]
134
    #[error("a networking error occurred: '{0}'")]
135
    Networking(networking::Error),
136

            
137
    /// An error occurred from IO.
138
    #[error("an io error occurred: '{0}'")]
139
    Io(String),
140

            
141
    /// An error occurred with the provided configuration options.
142
    #[error("a configuration error occurred: '{0}'")]
143
    Configuration(String),
144

            
145
    /// An error occurred inside of the client.
146
    #[error("an io error in the client: '{0}'")]
147
    Client(String),
148

            
149
    /// An attempt to use a `Collection` with a `Database` that it wasn't defined within.
150
    #[error("attempted to access a collection not registered with this schema")]
151
    CollectionNotFound,
152

            
153
    /// A `Collection` being added already exists. This can be caused by a collection name not being unique.
154
    #[error("attempted to define a collection that already has been defined")]
155
    CollectionAlreadyDefined,
156

            
157
    /// An attempt to update a document that doesn't exist.
158
    #[error("the requested document id {1} from collection {0} was not found")]
159
    DocumentNotFound(CollectionName, Box<DocumentId>),
160

            
161
    /// A value provided as a [`DocumentId`] exceeded [`DocumentId::MAX_LENGTH`].
162
    #[error(
163
        "an value was provided for a `DocumentId` that was larger than `DocumentId::MAX_LENGTH`"
164
    )]
165
    DocumentIdTooLong,
166

            
167
    /// When updating a document, if a situation is detected where the contents
168
    /// have changed on the server since the `Revision` provided, a Conflict
169
    /// error will be returned.
170
    #[error("a conflict was detected while updating document {1} from collection {0}")]
171
    DocumentConflict(CollectionName, Box<Header>),
172

            
173
    /// When saving a document in a collection with unique views, a document
174
    /// emits a key that is already emitted by an existing ocument, this error
175
    /// is returned.
176
    #[error("a unique key violation occurred: document `{existing_document}` already has the same key as `{conflicting_document}` for {view}")]
177
    UniqueKeyViolation {
178
        /// The name of the view that the unique key violation occurred.
179
        view: ViewName,
180
        /// The document that caused the violation.
181
        conflicting_document: Box<Header>,
182
        /// The document that already uses the same key.
183
        existing_document: Box<Header>,
184
    },
185

            
186
    /// When pushing a document, an error occurred while generating the next unique id.
187
    #[error("an error occurred generating a new unique id for {0}: {1}")]
188
    DocumentPush(CollectionName, NextValueError),
189

            
190
    /// An invalid name was specified during schema creation.
191
    #[error("an invalid name was used in a schema: {0}")]
192
    InvalidName(#[from] schema::InvalidNameError),
193

            
194
    /// Permission was denied.
195
    #[error("permission error: {0}")]
196
    PermissionDenied(#[from] actionable::PermissionDenied),
197

            
198
    /// An internal error handling passwords was encountered.
199
    #[error("error with password: {0}")]
200
    Password(String),
201

            
202
    /// The user specified was not found. This will not be returned in response
203
    /// to an invalid username being used during login. It will be returned in
204
    /// other APIs that operate upon users.
205
    #[error("user not found")]
206
    UserNotFound,
207

            
208
    /// An error occurred converting from bytes to Utf-8.
209
    #[error("invalid string: {0}")]
210
    InvalidUnicode(String),
211

            
212
    /// The credentials specified are not valid.
213
    #[error("invalid credentials")]
214
    InvalidCredentials,
215

            
216
    /// Returned when the a view's reduce() function is unimplemented.
217
    #[error("reduce is unimplemented")]
218
    ReduceUnimplemented,
219

            
220
    /// A floating point operation yielded Not a Number.
221
    #[error("floating point operation yielded NaN")]
222
    NotANumber,
223
}
224

            
225
impl From<pot::Error> for Error {
226
    fn from(err: pot::Error) -> Self {
227
        Self::Serialization(err.to_string())
228
    }
229
}
230

            
231
impl<T> From<InsertError<T>> for Error {
232
    fn from(err: InsertError<T>) -> Self {
233
        err.error
234
    }
235
}
236

            
237
impl From<view::Error> for Error {
238
    fn from(err: view::Error) -> Self {
239
        Self::Database(err.to_string())
240
    }
241
}
242

            
243
impl From<FromUtf8Error> for Error {
244
    fn from(err: FromUtf8Error) -> Self {
245
        Self::InvalidUnicode(err.to_string())
246
    }
247
}
248

            
249
impl From<InvalidHexadecimal> for Error {
250
    fn from(err: InvalidHexadecimal) -> Self {
251
        Self::Serialization(err.to_string())
252
    }
253
}
254

            
255
/// Shared schemas and utilities used for unit testing.
256
#[cfg(any(feature = "test-util", test))]
257
#[allow(missing_docs)]
258
pub mod test_util;
259

            
260
/// When true, encryption was enabled at build-time.
261
#[cfg(feature = "encryption")]
262
pub const ENCRYPTION_ENABLED: bool = true;
263

            
264
/// When true, encryption was enabled at build-time.
265
#[cfg(not(feature = "encryption"))]
266
pub const ENCRYPTION_ENABLED: bool = false;
267

            
268
/// A type that implements [`Error`](std::error::Error) and is threadsafe.
269
pub trait AnyError: std::error::Error + Send + Sync + 'static {}
270

            
271
impl<T> AnyError for T where T: std::error::Error + Send + Sync + 'static {}