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::pedantic,
9
    future_incompatible,
10
    rust_2018_idioms,
11
)]
12
#![allow(
13
    clippy::missing_errors_doc, // TODO clippy::missing_errors_doc
14
    clippy::option_if_let_else,
15
    clippy::module_name_repetitions,
16
    clippy::use_self, // false positives that can't be allowed on the type declaration itself.
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::fmt::Display;
49
use std::string::FromUtf8Error;
50

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

            
58
use crate::api::ApiName;
59
use crate::connection::HasSchema;
60
use crate::document::{DocumentId, Header, InvalidHexadecimal};
61
use crate::key::time::TimeError;
62
use crate::key::NextValueError;
63
use crate::schema::InsertError;
64

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

            
77
        /// The schema provided for the database.
78
        schema: SchemaName,
79

            
80
        /// The schema stored for the database.
81
        stored_schema: SchemaName,
82
    },
83

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

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

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

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

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

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

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

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

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

            
122
    /// An error occurred from networking.
123
    #[error("a networking error occurred: '{0}'")]
124
    Networking(networking::Error),
125

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

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

            
134
    /// A value provided as a [`DocumentId`] exceeded [`DocumentId::MAX_LENGTH`].
135
    #[error(
136
        "an value was provided for a `DocumentId` that was larger than `DocumentId::MAX_LENGTH`"
137
    )]
138
    DocumentIdTooLong,
139

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

            
146
    /// When saving a document in a collection with unique views, a document
147
    /// emits a key that is already emitted by an existing ocument, this error
148
    /// is returned.
149
    #[error("a unique key violation occurred: document `{existing_document}` already has the same key as `{conflicting_document}` for {view}")]
150
    UniqueKeyViolation {
151
        /// The name of the view that the unique key violation occurred.
152
        view: ViewName,
153
        /// The document that caused the violation.
154
        conflicting_document: Box<Header>,
155
        /// The document that already uses the same key.
156
        existing_document: Box<Header>,
157
    },
158

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

            
163
    /// An invalid name was specified during schema creation.
164
    #[error("an invalid name was used in a schema: {0}")]
165
    InvalidName(#[from] schema::InvalidNameError),
166

            
167
    /// Permission was denied.
168
    #[error("permission error: {0}")]
169
    PermissionDenied(#[from] actionable::PermissionDenied),
170

            
171
    /// An internal error handling passwords was encountered.
172
    #[error("error with password: {0}")]
173
    Password(String),
174

            
175
    /// The user specified was not found. This will not be returned in response
176
    /// to an invalid username being used during login. It will be returned in
177
    /// other APIs that operate upon users.
178
    #[error("user not found")]
179
    UserNotFound,
180

            
181
    /// An error occurred converting from bytes to Utf-8.
182
    #[error("invalid string: {0}")]
183
    InvalidUnicode(String),
184

            
185
    /// The credentials specified are not valid.
186
    #[error("invalid credentials")]
187
    InvalidCredentials,
188

            
189
    /// Returned when the a view's reduce() function is unimplemented.
190
    #[error("reduce is unimplemented")]
191
    ReduceUnimplemented,
192

            
193
    /// A floating point operation yielded Not a Number.
194
    #[error("floating point operation yielded NaN")]
195
    NotANumber,
196

            
197
    /// An error while operating with a time
198
    #[error("time error: {0}")]
199
    Time(#[from] TimeError),
200

            
201
    /// An error from another crate.
202
    #[error("error from {origin}: {error}")]
203
    Other {
204
        /// The origin of the error.
205
        origin: String,
206
        /// The error message.
207
        error: String,
208
    },
209
}
210

            
211
impl Error {
212
    /// Returns an instance of [`Self::Other`] with the given parameters.
213
5233
    pub fn other(origin: impl Display, error: impl Display) -> Self {
214
5233
        Self::Other {
215
5233
            origin: origin.to_string(),
216
5233
            error: error.to_string(),
217
5233
        }
218
5233
    }
219

            
220
    /// Returns true if this error is a [`Error::UniqueKeyViolation`] from
221
    /// `View`.
222
    pub fn is_unique_key_error<View: schema::View, C: HasSchema>(&self, connection: &C) -> bool {
223
        if let Self::UniqueKeyViolation { view, .. } = self {
224
            if let Ok(schema_view) = connection.schematic().view::<View>() {
225
                return view == &schema_view.view_name();
226
            }
227
        }
228

            
229
        false
230
    }
231

            
232
    /// Returns the header of the conflicting document if this error is a
233
    /// [`Error::DocumentConflict`] from `Collection`.
234
    #[must_use]
235
    pub fn conflicting_document<Collection: schema::Collection>(&self) -> Option<Header> {
236
8
        match self {
237
8
            Self::DocumentConflict(collection, header)
238
8
                if collection == &Collection::collection_name() =>
239
8
            {
240
8
                Some(header.as_ref().clone())
241
            }
242
            _ => None,
243
        }
244
8
    }
245
}
246

            
247
impl From<pot::Error> for Error {
248
    fn from(err: pot::Error) -> Self {
249
        Self::other("pot", err)
250
    }
251
}
252

            
253
impl<T> From<InsertError<T>> for Error {
254
    fn from(err: InsertError<T>) -> Self {
255
        err.error
256
    }
257
}
258

            
259
impl From<view::Error> for Error {
260
    fn from(err: view::Error) -> Self {
261
        Self::other("view", err)
262
    }
263
}
264

            
265
impl From<FromUtf8Error> for Error {
266
    fn from(err: FromUtf8Error) -> Self {
267
        Self::InvalidUnicode(err.to_string())
268
    }
269
}
270

            
271
impl From<InvalidHexadecimal> for Error {
272
    fn from(err: InvalidHexadecimal) -> Self {
273
        Self::other("invalid hexadecimal", err)
274
    }
275
}
276

            
277
/// Shared schemas and utilities used for unit testing.
278
#[cfg(any(feature = "test-util", test))]
279
#[allow(missing_docs)]
280
pub mod test_util;
281

            
282
/// When true, encryption was enabled at build-time.
283
#[cfg(feature = "encryption")]
284
pub const ENCRYPTION_ENABLED: bool = true;
285

            
286
/// When true, encryption was enabled at build-time.
287
#[cfg(not(feature = "encryption"))]
288
pub const ENCRYPTION_ENABLED: bool = false;
289

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

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