134 lines
4.4 KiB
Rust
Raw Normal View History

use rkyv::ser::Serializer;
use rkyv::ser::serializers::AllocSerializer;
2022-06-02 17:46:26 +02:00
use thiserror::Error;
2022-03-16 18:10:59 +01:00
use crate::db;
2022-05-05 15:50:44 +02:00
use crate::db::{AlignedAdapter, ArchivedValue, RawDB, DB};
use lmdb::{DatabaseFlags, Environment, EnvironmentFlags, Transaction, WriteFlags};
2024-11-09 13:01:26 +01:00
use miette::Diagnostic;
use std::fmt::Debug;
2022-03-16 18:10:59 +01:00
use std::{path::Path, sync::Arc};
2021-10-07 16:44:01 +02:00
2022-03-10 20:52:34 +01:00
use crate::resources::state::State;
2022-03-16 18:10:59 +01:00
#[derive(Debug, Clone)]
2021-10-07 16:44:01 +02:00
pub struct StateDB {
env: Arc<Environment>,
2022-03-16 18:10:59 +01:00
db: DB<AlignedAdapter<State>>,
}
#[derive(Clone, Debug, PartialEq, Eq, Error, Diagnostic)]
2022-06-02 17:46:26 +02:00
pub enum StateDBError {
#[error("opening the state db environment failed")]
#[diagnostic(
code(bffh::db::state::open_env),
help("does the parent directory for state_db exist?")
)]
OpenEnv(#[source] db::Error),
#[error("opening the state db failed")]
#[diagnostic(code(bffh::db::state::open))]
Open(#[source] db::Error),
#[error("creating the state db failed")]
#[diagnostic(code(bffh::db::state::create))]
Create(#[source] db::Error),
}
impl StateDB {
2022-06-02 17:46:26 +02:00
pub fn open_env<P: AsRef<Path>>(path: P) -> Result<Arc<Environment>, StateDBError> {
2021-10-07 16:44:01 +02:00
Environment::new()
2022-03-16 18:10:59 +01:00
.set_flags(
EnvironmentFlags::WRITE_MAP
| EnvironmentFlags::NO_SUB_DIR
| EnvironmentFlags::NO_TLS
| EnvironmentFlags::NO_READAHEAD,
)
2022-03-16 19:01:09 +01:00
.set_max_dbs(8)
2021-10-07 16:44:01 +02:00
.open(path.as_ref())
2022-03-13 22:50:37 +01:00
.map(Arc::new)
2022-06-02 17:46:26 +02:00
.map_err(|e| StateDBError::OpenEnv(e.into()))
}
2022-03-16 18:10:59 +01:00
fn new(env: Arc<Environment>, db: RawDB) -> Self {
let db = DB::new(db);
Self { env, db }
}
2022-06-02 17:46:26 +02:00
pub fn open_with_env(env: Arc<Environment>) -> Result<Self, StateDBError> {
let db = RawDB::open(&env, Some("state"))
.map_err(|e| StateDBError::Open(e.into()))?;
2022-03-16 18:10:59 +01:00
Ok(Self::new(env, db))
}
2022-06-02 17:46:26 +02:00
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, StateDBError> {
2021-10-07 16:44:01 +02:00
let env = Self::open_env(path)?;
2022-03-16 18:10:59 +01:00
Self::open_with_env(env)
2021-10-07 16:44:01 +02:00
}
2022-06-02 17:46:26 +02:00
pub fn create_with_env(env: Arc<Environment>) -> Result<Self, StateDBError> {
2021-10-20 18:37:50 +02:00
let flags = DatabaseFlags::empty();
let db = RawDB::create(&env, Some("state"), flags)
.map_err(|e| StateDBError::Create(e.into()))?;
2021-10-20 18:37:50 +02:00
2022-03-16 18:10:59 +01:00
Ok(Self::new(env, db))
2021-10-20 18:37:50 +02:00
}
2022-06-02 17:46:26 +02:00
pub fn create<P: AsRef<Path>>(path: P) -> Result<Self, StateDBError> {
2022-03-13 22:50:37 +01:00
let env = Self::open_env(path)?;
Self::create_with_env(env)
}
2022-03-16 18:10:59 +01:00
pub fn begin_ro_txn(&self) -> Result<impl Transaction + '_, db::Error> {
2022-06-02 17:46:26 +02:00
self.env.begin_ro_txn().map_err(db::Error::from)
}
2022-03-16 18:10:59 +01:00
pub fn get(&self, key: impl AsRef<[u8]>) -> Result<Option<ArchivedValue<State>>, db::Error> {
let txn = self.env.begin_ro_txn()?;
self.db.get(&txn, &key.as_ref())
}
2022-03-16 18:10:59 +01:00
pub fn get_all<'txn, T: Transaction>(
&self,
txn: &'txn T,
2022-05-05 15:50:44 +02:00
) -> Result<impl IntoIterator<Item = (&'txn [u8], ArchivedValue<State>)>, db::Error> {
2022-03-16 18:10:59 +01:00
self.db.get_all(txn)
}
2022-03-16 18:10:59 +01:00
pub fn put(&self, key: &impl AsRef<[u8]>, val: &ArchivedValue<State>) -> Result<(), db::Error> {
let mut txn = self.env.begin_rw_txn()?;
let flags = WriteFlags::empty();
self.db.put(&mut txn, key, val, flags)?;
2022-06-02 17:46:26 +02:00
Ok(txn.commit()?)
2022-03-16 18:10:59 +01:00
}
pub fn load_map(&self, map: &std::collections::HashMap<String, State>) -> miette::Result<()> {
use miette::IntoDiagnostic;
let mut txn = self.env.begin_rw_txn().into_diagnostic()?;
let flags = WriteFlags::empty();
for (key, val) in map {
let mut serializer = AllocSerializer::<1024>::default();
serializer.serialize_value(val).into_diagnostic()?;
let serialized = ArchivedValue::new(serializer.into_serializer().into_inner());
self.db.put(&mut txn, &key.as_bytes(), &serialized, flags)?;
}
txn.commit().into_diagnostic()?;
Ok(())
}
pub fn dump_map(&self) -> miette::Result<std::collections::HashMap<String, State>> {
let mut map = std::collections::HashMap::new();
for (key, val) in self.get_all(&self.begin_ro_txn()?)? {
let key_str = core::str::from_utf8(&key).map_err(|_e| miette::Error::msg("state key not UTF8"))?.to_string();
let val_state: State = rkyv::Deserialize::deserialize(val.as_ref(), &mut rkyv::Infallible).unwrap();
map.insert(key_str, val_state);
}
Ok(map)
}
}
2021-10-18 11:27:42 +02:00
#[cfg(test)]
mod tests {
use super::*;
2021-10-18 11:27:42 +02:00
use std::ops::Deref;
}