aingle_rkv/
lib.rs

1// Copyright 2018-2019 Mozilla
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
4// this file except in compliance with the License. You may obtain a copy of the
5// License at http://www.apache.org/licenses/LICENSE-2.0
6// Unless required by applicable law or agreed to in writing, software distributed
7// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
8// CONDITIONS OF ANY KIND, either express or implied. See the License for the
9// specific language governing permissions and limitations under the License.
10
11//! a simple, humane, typed Rust interface to [LMDB](http://www.lmdb.tech/doc/)
12//!
13//! It aims to achieve the following:
14//!
15//! - Avoid LMDB's sharp edges (e.g., obscure error codes for common situations).
16//! - Report errors via [failure](https://docs.rs/failure/).
17//! - Correctly restrict access to one handle per process via a [Manager](struct.Manager.html).
18//! - Use Rust's type system to make single-typed key stores (including LMDB's own integer-keyed stores)
19//!   safe and ergonomic.
20//! - Encode and decode values via [bincode](https://docs.rs/bincode/)/[serde](https://docs.rs/serde/)
21//!   and type tags, achieving platform-independent storage and input/output flexibility.
22//!
23//! It exposes these primary abstractions:
24//!
25//! - [Manager](struct.Manager.html): a singleton that controls access to LMDB environments
26//! - [Rkv](struct.Rkv.html): an LMDB environment that contains a set of key/value databases
27//! - [SingleStore](store/single/struct.SingleStore.html): an LMDB database that contains a set of key/value pairs
28//!
29//! Keys can be anything that implements `AsRef<[u8]>` or integers
30//! (when accessing an [IntegerStore](store/integer/struct.IntegerStore.html)).
31//! Values can be any of the types defined by the [Value](value/enum.Value.html) enum, including:
32//!
33//! - booleans (`Value::Bool`)
34//! - integers (`Value::I64`, `Value::U64`)
35//! - floats (`Value::F64`)
36//! - strings (`Value::Str`)
37//! - blobs (`Value::Blob`)
38//!
39//! See [Value](value/enum.Value.html) for the complete list of supported types.
40//!
41//! ## Basic Usage
42//! ```
43//! use rkv::{Manager, Rkv, SingleStore, Value, StoreOptions};
44//! use std::fs;
45//! use tempfile::Builder;
46//!
47//! // First determine the path to the environment, which is represented
48//! // on disk as a directory containing two files:
49//! //
50//! //   * a data file containing the key/value stores
51//! //   * a lock file containing metadata about current transactions
52//! //
53//! // In this example, we use the `tempfile` crate to create the directory.
54//! //
55//! let root = Builder::new().prefix("simple-db").tempdir().unwrap();
56//! fs::create_dir_all(root.path()).unwrap();
57//! let path = root.path();
58//!
59//! // The Manager enforces that each process opens the same environment
60//! // at most once by caching a handle to each environment that it opens.
61//! // Use it to retrieve the handle to an opened environment—or create one
62//! // if it hasn't already been opened:
63//! let created_arc = Manager::singleton().write().unwrap().get_or_create(path, Rkv::new).unwrap();
64//! let env = created_arc.read().unwrap();
65//!
66//! // Then you can use the environment handle to get a handle to a datastore:
67//! let store: SingleStore = env.open_single("mydb", StoreOptions::create()).unwrap();
68//!
69//! {
70//!     // Use a write transaction to mutate the store via a `Writer`.
71//!     // There can be only one writer for a given environment, so opening
72//!     // a second one will block until the first completes.
73//!     let mut writer = env.write().unwrap();
74//!
75//!     // Keys are `AsRef<[u8]>`, while values are `Value` enum instances.
76//!     // Use the `Blob` variant to store arbitrary collections of bytes.
77//!     // Putting data returns a `Result<(), StoreError>`, where StoreError
78//!     // is an enum identifying the reason for a failure.
79//!     store.put(&mut writer, "int", &Value::I64(1234)).unwrap();
80//!     store.put(&mut writer, "uint", &Value::U64(1234_u64)).unwrap();
81//!     store.put(&mut writer, "float", &Value::F64(1234.0.into())).unwrap();
82//!     store.put(&mut writer, "instant", &Value::Instant(1528318073700)).unwrap();
83//!     store.put(&mut writer, "boolean", &Value::Bool(true)).unwrap();
84//!     store.put(&mut writer, "string", &Value::Str("Héllo, wörld!")).unwrap();
85//!     store.put(&mut writer, "json", &Value::Json(r#"{"foo":"bar", "number": 1}"#)).unwrap();
86//!     store.put(&mut writer, "blob", &Value::Blob(b"blob")).unwrap();
87//!
88//!     // You must commit a write transaction before the writer goes out
89//!     // of scope, or the transaction will abort and the data won't persist.
90//!     writer.commit().unwrap();
91//! }
92//!
93//! {
94//!     // Use a read transaction to query the store via a `Reader`.
95//!     // There can be multiple concurrent readers for a store, and readers
96//!     // never block on a writer nor other readers.
97//!     let reader = env.read().expect("reader");
98//!
99//!     // Keys are `AsRef<u8>`, and the return value is `Result<Option<Value>, StoreError>`.
100//!     println!("Get int {:?}", store.get(&reader, "int").unwrap());
101//!     println!("Get uint {:?}", store.get(&reader, "uint").unwrap());
102//!     println!("Get float {:?}", store.get(&reader, "float").unwrap());
103//!     println!("Get instant {:?}", store.get(&reader, "instant").unwrap());
104//!     println!("Get boolean {:?}", store.get(&reader, "boolean").unwrap());
105//!     println!("Get string {:?}", store.get(&reader, "string").unwrap());
106//!     println!("Get json {:?}", store.get(&reader, "json").unwrap());
107//!     println!("Get blob {:?}", store.get(&reader, "blob").unwrap());
108//!
109//!     // Retrieving a non-existent value returns `Ok(None)`.
110//!     println!("Get non-existent value {:?}", store.get(&reader, "non-existent").unwrap());
111//!
112//!     // A read transaction will automatically close once the reader
113//!     // goes out of scope, so isn't necessary to close it explicitly,
114//!     // although you can do so by calling `Reader.abort()`.
115//! }
116//!
117//! {
118//!     // Aborting a write transaction rolls back the change(s).
119//!     let mut writer = env.write().unwrap();
120//!     store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
121//!     writer.abort();
122//!     let reader = env.read().expect("reader");
123//!     println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
124//! }
125//!
126//! {
127//!     // Explicitly aborting a transaction is not required unless an early
128//!     // abort is desired, since both read and write transactions will
129//!     // implicitly be aborted once they go out of scope.
130//!     {
131//!         let mut writer = env.write().unwrap();
132//!         store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
133//!     }
134//!     let reader = env.read().expect("reader");
135//!     println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
136//! }
137//!
138//! {
139//!     // Deleting a key/value pair also requires a write transaction.
140//!     let mut writer = env.write().unwrap();
141//!     store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
142//!     store.put(&mut writer, "bar", &Value::Str("baz")).unwrap();
143//!     store.delete(&mut writer, "foo").unwrap();
144//!
145//!     // A write transaction also supports reading, and the version of the
146//!     // store that it reads includes the changes it has made regardless of
147//!     // the commit state of that transaction.
148
149//!     // In the code above, "foo" and "bar" were put into the store,
150//!     // then "foo" was deleted so only "bar" will return a result when the
151//!     // database is queried via the writer.
152//!     println!("It should be None! ({:?})", store.get(&writer, "foo").unwrap());
153//!     println!("Get bar ({:?})", store.get(&writer, "bar").unwrap());
154//!
155//!     // But a reader won't see that change until the write transaction
156//!     // is committed.
157//!     {
158//!         let reader = env.read().expect("reader");
159//!         println!("Get foo {:?}", store.get(&reader, "foo").unwrap());
160//!         println!("Get bar {:?}", store.get(&reader, "bar").unwrap());
161//!     }
162//!     writer.commit().unwrap();
163//!     {
164//!         let reader = env.read().expect("reader");
165//!         println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
166//!         println!("Get bar {:?}", store.get(&reader, "bar").unwrap());
167//!     }
168//!
169//!     // Committing a transaction consumes the writer, preventing you
170//!     // from reusing it by failing at compile time with an error.
171//!     // This line would report error[E0382]: borrow of moved value: `writer`.
172//!     // store.put(&mut writer, "baz", &Value::Str("buz")).unwrap();
173//! }
174//!
175//! {
176//!     // Clearing all the entries in the store with a write transaction.
177//!     {
178//!         let mut writer = env.write().unwrap();
179//!         store.put(&mut writer, "foo", &Value::Str("bar")).unwrap();
180//!         store.put(&mut writer, "bar", &Value::Str("baz")).unwrap();
181//!         writer.commit().unwrap();
182//!     }
183//!
184//!     {
185//!         let mut writer = env.write().unwrap();
186//!         store.clear(&mut writer).unwrap();
187//!         writer.commit().unwrap();
188//!     }
189//!
190//!     {
191//!         let reader = env.read().expect("reader");
192//!         println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap());
193//!         println!("It should be None! ({:?})", store.get(&reader, "bar").unwrap());
194//!     }
195//!
196//! }
197//!
198//! ```
199
200#![allow(dead_code)]
201
202pub use aingle_lmdb::{
203    DatabaseFlags,
204    EnvironmentBuilder,
205    EnvironmentFlags,
206    WriteFlags,
207};
208
209mod env;
210pub mod error;
211mod manager;
212pub mod migrate;
213mod readwrite;
214pub mod store;
215pub mod value;
216
217pub use aingle_lmdb::{
218    Cursor,
219    Database,
220    Error as LmdbError,
221    Info,
222    Iter as LmdbIter,
223    RoCursor,
224    Stat,
225};
226
227pub use self::readwrite::{
228    Readable,
229    Reader,
230    Writer,
231};
232pub use self::store::integer::{
233    IntegerStore,
234    PrimitiveInt,
235};
236pub use self::store::integermulti::MultiIntegerStore;
237pub use self::store::multi::MultiStore;
238pub use self::store::single::SingleStore;
239pub use self::store::Options as StoreOptions;
240
241pub use self::env::Rkv;
242
243pub use self::error::{
244    DataError,
245    StoreError,
246};
247
248pub use self::manager::Manager;
249
250pub use self::value::{
251    OwnedValue,
252    Value,
253};
254
255fn read_transform(val: Result<&[u8], aingle_lmdb::Error>) -> Result<Option<Value>, StoreError> {
256    match val {
257        Ok(bytes) => Value::from_tagged_slice(bytes).map(Some).map_err(StoreError::DataError),
258        Err(aingle_lmdb::Error::NotFound) => Ok(None),
259        Err(e) => Err(StoreError::LmdbError(e)),
260    }
261}