use heed::{RoTxn, RwTxn, WithoutTls};
use super::lmdb_env::LmdbEnv;
use super::lmdb_error::LmdbLayerError;
fn read_txn(lmdb_env: &LmdbEnv) -> Result<RoTxn<'_, WithoutTls>, LmdbLayerError> {
lmdb_env.read_txn().map_err(LmdbLayerError::Txn)
}
fn write_txn(lmdb_env: &LmdbEnv) -> Result<RwTxn<'_>, LmdbLayerError> {
lmdb_env.write_txn().map_err(LmdbLayerError::Txn)
}
fn commit(txn: RwTxn<'_>) -> Result<(), LmdbLayerError> {
txn.commit().map_err(LmdbLayerError::Txn)
}
pub fn with_read_txn<R, E>(
lmdb_env: &LmdbEnv,
f: impl FnOnce(&RoTxn<'_, WithoutTls>) -> Result<R, E>,
) -> Result<R, E>
where
E: From<LmdbLayerError>,
{
let txn = read_txn(lmdb_env)?;
f(&txn)
}
pub fn with_write_txn<R, E>(
lmdb_env: &LmdbEnv,
f: impl FnOnce(&mut RwTxn<'_>) -> Result<R, E>,
) -> Result<R, E>
where
E: From<LmdbLayerError>,
{
let mut txn = write_txn(lmdb_env)?;
let value = f(&mut txn)?;
commit(txn)?;
Ok(value)
}
#[cfg(test)]
mod tests {
use bytesize::ByteSize;
use super::*;
use crate::lmdb::lmdb_db::LmdbDb;
use crate::lmdb::lmdb_env::{LmdbEnvConfig, open_lmdb_env};
#[derive(Debug)]
struct Rollback;
impl From<LmdbLayerError> for Rollback {
fn from(_: LmdbLayerError) -> Self {
Rollback
}
}
fn test_lmdb_env() -> (tempfile::TempDir, LmdbEnv) {
let dir = tempfile::tempdir().expect("create temp dir");
let lmdb_env =
open_lmdb_env(dir.path(), &LmdbEnvConfig::new(1, ByteSize::mib(16))).expect("open env");
(dir, lmdb_env)
}
#[test]
fn with_write_txn_commits_on_ok() {
let (_dir, lmdb_env) = test_lmdb_env();
let db =
with_write_txn(&lmdb_env, |txn| LmdbDb::open(&lmdb_env, txn, "kv")).expect("open db");
with_write_txn(&lmdb_env, |txn| db.put(txn, b"k", b"v")).expect("put");
let value = with_read_txn(&lmdb_env, |txn| db.get(txn, b"k")).expect("get");
assert_eq!(value.as_deref(), Some(b"v".as_slice()));
}
#[test]
fn with_write_txn_aborts_on_err() {
let (_dir, lmdb_env) = test_lmdb_env();
let db =
with_write_txn(&lmdb_env, |txn| LmdbDb::open(&lmdb_env, txn, "kv")).expect("open db");
let result: Result<(), Rollback> = with_write_txn(&lmdb_env, |txn| {
db.put(txn, b"k", b"v")?;
Err(Rollback)
});
assert!(result.is_err());
let present = with_read_txn(&lmdb_env, |txn| db.contains(txn, b"k")).expect("contains");
assert!(
!present,
"a closure that returned Err must leave nothing committed"
);
}
}