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
//! An ergonomic, embedded, single-threaded database for Rustaceans.
//!
//! ## Strengths
//!
//! -   Define a schema in Rust.
//! -   Use **your** types in the database as long as they implement `Serialize` and `Deserialize`. You don't have to fuss around
//!     with converting your data to database-specific types.
//! -   All your database interactions are typesafe. When you type `db.`, your tooling will suggest a list of your tables. When you
//!     select a table, you'll be greeted with that table-type's contract populated with your types. No need to wrap your db
//!     in a handwritten type safe contract.
//! -   Supports a variety of simple data-structures, including LookupTables, Lists, and many more. Implementing your own
//!     table types is trivial.
//! -   All table mutations are persisted to an append only log using the fast & compact bincode representation of your types.
//! -   You can `begin_transaction()`s to express atomic updates to multiple tables.
//!
//! ## Quickstart
//!
//! Add the following to your `Cargo.toml`:
//!
//! ```toml
//! db-rs = "0.2.1"
//! db-rs-derive = "0.2.1"
//! ```
//!
//! Define your schema:
//!
//! ```ignore
//! use db_rs_derive::Schema;
//! use db_rs::{Single, List, LookupTable};
//!
//! #[derive(Schema)]
//! struct SchemaV1 {
//!     owner: Single<Username>,
//!     admins: List<Username>,
//!     users: LookupTable<Username, Account>,
//! }
//! ```
//!
//! Initialize your DB:
//!
//! ```ignore
//! use db_rs::Db;
//! use db_rs::Config;
//!
//! let mut db = SchemaV1::init(Config::in_folder("/tmp/test/"))?;
//! db.owner.insert("Parth".to_string())?;
//!
//! println!("{}", db.owner.data().unwrap());
//! ```
//!
//! ## Table Types
//!
//! Each table has an in-memory representation and a corresponding log entry format. For instance
//! [List]'s in memory format is a [Vec], and you can look at it's corresponding [list::LogEntry]
//! to see how writes will be written to disk.
//!
//! Tables that start with `Lookup` have a `HashMap` as part of their in memory format.
//! [LookupTable] is the most general form, while [LookupList] and [LookupSet] are specializations
//! for people who want `HashMap<K, Vec<V>>` or `HashMap<K, HashSet<V>>`. Their reason for
//! existence is better log performance in the case of small modifications to the `Vec` or
//! `HashSet` in question (see [lookup_list::LogEntry] or [lookup_set::LogEntry]).
//!
//!
//! ## Log Compaction
//!
//! At any point you can call [Db::compact_log] on your database. This will atomically write a
//! compact representation of all your current tables. For example if there's a key in a
//! LookupTable that was written to many times, the compact representation will only contain the
//! last value. Each table type descibes it's own compact representation.
//!
//! If your database is in an `Arc<Mutex>>` you can additionally use the [BackgroundCompacter]
//! which will perform compactions periodically in a separate thread.
//!
//! ## TXs and Batch Writing
//!
//! You can [Db::begin_transaction] which will allow you to express batch operations that can be
//! discarded as a set if your program is interrupted. Presently there is no way to abort a
//! transaction. TXs are also a mechanism for batch writing, log entries are kept in memory until
//! the transaction completes and written once to disk.
//!
//! ## Active areas of thought and research
//!
//! -   Because the db implementation (like redis) is single threaded, it forces you to achieve application throughput via low
//!     latency rather than concurrency. Currently, this suits our needs. Simply being embedded gives us more than enough
//!     throughput compared to something like Postgres. For use in a server-style setting put the database in
//!     an `Arc<Mutex<>>`.
//! -   The database offers no tools at the moment to define integrity constraints beyond what the Rust type system implicitly
//!     enforces (non-null for instance). At the moment for us, this is simply an application side concern.
//!
//! ## Features
//!
//! `clone` - derive clone on all table types. Consistency between cloned database is not provided.
//! Useful in testing situations.
//!
//! ## Used by
//!
//! -   [Lockbook](https://github.com/lockbook/lockbook)
//!

pub use crate::compacter::BackgroundCompacter;
pub use crate::compacter::CancelSig;
pub use crate::config::Config;
pub use crate::db::Db;
pub use crate::errors::DbError;
pub use crate::errors::DbResult;
pub use crate::logger::Logger;
pub use crate::logger::TxHandle;

pub use crate::list::List;
pub use crate::lookup::LookupTable;
pub use crate::lookup_list::LookupList;
pub use crate::lookup_set::LookupSet;
pub use crate::single::Single;

pub mod compacter;
pub mod config;
pub mod db;
pub mod errors;
pub mod list;
pub mod logger;
pub mod lookup;
pub mod lookup_list;
pub mod lookup_set;
pub mod single;
pub mod table;

pub type TableId = u8;
pub type ByteCount = u32;