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
pub mod document;
27
pub mod limits;
28
/// Types for defining database schema.
29
pub mod schema;
30
/// Types for executing transactions.
31
pub mod transaction;
32

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

            
36
/// Traits for tailoring a server.
37
pub mod api;
38

            
39
/// Key trait and related types.
40
pub mod key;
41

            
42
/// Types for implementing the BonsaiDb network protocol.
43
pub mod networking;
44

            
45
/// Types for Publish/Subscribe (`PubSub`) messaging.
46
pub mod pubsub;
47

            
48
use std::string::FromUtf8Error;
49

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

            
61
use crate::{
62
    document::{DocumentId, Header, InvalidHexadecimal},
63
    key::NextValueError,
64
    schema::{ApiName, InsertError},
65
};
66

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

            
79
        /// The schema provided for the database.
80
        schema: SchemaName,
81

            
82
        /// The schema stored for the database.
83
        stored_schema: SchemaName,
84
    },
85

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

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

            
94
    /// The [`ViewName`] returned has already been registered.
95
    #[error("view '{0}' was already registered")]
96
    ViewAlreadyRegistered(ViewName),
97

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

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

            
108
    /// The view was not found.
109
    #[error("view was not found")]
110
    ViewNotFound,
111

            
112
    /// The collection was not found.
113
    #[error("collection was not found")]
114
    CollectionNotFound,
115

            
116
    /// The api invoked was not found.
117
    #[error("api '{0}' was not found")]
118
    ApiNotFound(ApiName),
119

            
120
    /// The database name already exists.
121
    #[error("a database with name '{0}' already exists")]
122
    DatabaseNameAlreadyTaken(String),
123

            
124
    /// An error from interacting with local storage.
125
    #[error("error from storage: {0}")]
126
    Database(String),
127

            
128
    /// An error serializing data.
129
    #[error("error serializing: {0}")]
130
    Serialization(String),
131

            
132
    /// An error from interacting with a server.
133
    #[error("error from server: {0}")]
134
    Server(String),
135

            
136
    /// An error occurred from the QUIC transport layer.
137
    #[error("a transport error occurred: '{0}'")]
138
    Transport(String),
139

            
140
    /// An error occurred from the websocket transport layer.
141
    #[cfg(feature = "websockets")]
142
    #[error("a websocket error occurred: '{0}'")]
143
    Websocket(String),
144

            
145
    /// An error occurred from networking.
146
    #[error("a networking error occurred: '{0}'")]
147
    Networking(networking::Error),
148

            
149
    /// An error occurred from IO.
150
    #[error("an io error occurred: '{0}'")]
151
    Io(String),
152

            
153
    /// An error occurred with the provided configuration options.
154
    #[error("a configuration error occurred: '{0}'")]
155
    Configuration(String),
156

            
157
    /// An error occurred inside of the client.
158
    #[error("an io error in the client: '{0}'")]
159
    Client(String),
160

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

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

            
169
    /// A value provided as a [`DocumentId`] exceeded [`DocumentId::MAX_LENGTH`].
170
    #[error(
171
        "an value was provided for a `DocumentId` that was larger than `DocumentId::MAX_LENGTH`"
172
    )]
173
    DocumentIdTooLong,
174

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

            
181
    /// When saving a document in a collection with unique views, a document
182
    /// emits a key that is already emitted by an existing ocument, this error
183
    /// is returned.
184
    #[error("a unique key violation occurred: document `{existing_document}` already has the same key as `{conflicting_document}` for {view}")]
185
    UniqueKeyViolation {
186
        /// The name of the view that the unique key violation occurred.
187
        view: ViewName,
188
        /// The document that caused the violation.
189
        conflicting_document: Box<Header>,
190
        /// The document that already uses the same key.
191
        existing_document: Box<Header>,
192
    },
193

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

            
198
    /// An invalid name was specified during schema creation.
199
    #[error("an invalid name was used in a schema: {0}")]
200
    InvalidName(#[from] schema::InvalidNameError),
201

            
202
    /// Permission was denied.
203
    #[error("permission error: {0}")]
204
    PermissionDenied(#[from] actionable::PermissionDenied),
205

            
206
    /// An internal error handling passwords was encountered.
207
    #[error("error with password: {0}")]
208
    Password(String),
209

            
210
    /// The user specified was not found. This will not be returned in response
211
    /// to an invalid username being used during login. It will be returned in
212
    /// other APIs that operate upon users.
213
    #[error("user not found")]
214
    UserNotFound,
215

            
216
    /// An error occurred converting from bytes to Utf-8.
217
    #[error("invalid string: {0}")]
218
    InvalidUnicode(String),
219

            
220
    /// The credentials specified are not valid.
221
    #[error("invalid credentials")]
222
    InvalidCredentials,
223

            
224
    /// Returned when the a view's reduce() function is unimplemented.
225
    #[error("reduce is unimplemented")]
226
    ReduceUnimplemented,
227

            
228
    /// A floating point operation yielded Not a Number.
229
    #[error("floating point operation yielded NaN")]
230
    NotANumber,
231
}
232

            
233
impl From<pot::Error> for Error {
234
    fn from(err: pot::Error) -> Self {
235
        Self::Serialization(err.to_string())
236
    }
237
}
238

            
239
impl<T> From<InsertError<T>> for Error {
240
    fn from(err: InsertError<T>) -> Self {
241
        err.error
242
    }
243
}
244

            
245
impl From<view::Error> for Error {
246
    fn from(err: view::Error) -> Self {
247
        Self::Database(err.to_string())
248
    }
249
}
250

            
251
impl From<FromUtf8Error> for Error {
252
    fn from(err: FromUtf8Error) -> Self {
253
        Self::InvalidUnicode(err.to_string())
254
    }
255
}
256

            
257
impl From<InvalidHexadecimal> for Error {
258
    fn from(err: InvalidHexadecimal) -> Self {
259
        Self::Serialization(err.to_string())
260
    }
261
}
262

            
263
/// Shared schemas and utilities used for unit testing.
264
#[cfg(any(feature = "test-util", test))]
265
#[allow(missing_docs)]
266
pub mod test_util;
267

            
268
/// When true, encryption was enabled at build-time.
269
#[cfg(feature = "encryption")]
270
pub const ENCRYPTION_ENABLED: bool = true;
271

            
272
/// When true, encryption was enabled at build-time.
273
#[cfg(not(feature = "encryption"))]
274
pub const ENCRYPTION_ENABLED: bool = false;
275

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

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