diff options
author | metamuffin <metamuffin@disroot.org> | 2024-01-20 00:50:20 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-01-20 00:50:20 +0100 |
commit | 46c251655db7bb3d9aa814b1a5dde85336b0b9b1 (patch) | |
tree | ab0696f2c92e8854ce6aa0737877cc15184bd8b6 /base/src/database.rs | |
parent | 1c37d32a0985ff7390313833345b9299f9f0b196 (diff) | |
download | jellything-46c251655db7bb3d9aa814b1a5dde85336b0b9b1.tar jellything-46c251655db7bb3d9aa814b1a5dde85336b0b9b1.tar.bz2 jellything-46c251655db7bb3d9aa814b1a5dde85336b0b9b1.tar.zst |
replace sled with redb
Diffstat (limited to 'base/src/database.rs')
-rw-r--r-- | base/src/database.rs | 202 |
1 files changed, 176 insertions, 26 deletions
diff --git a/base/src/database.rs b/base/src/database.rs index 3f81cca..2a57937 100644 --- a/base/src/database.rs +++ b/base/src/database.rs @@ -3,43 +3,193 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2023 metamuffin <metamuffin.org> */ -use anyhow::Context; +use bincode::{Decode, Encode}; use jellycommon::{ user::{NodeUserData, User}, Node, }; use log::info; -use std::path::Path; -use typed_sled::Tree; +use std::{borrow::Borrow, ops::Deref, path::Path}; -pub use sled; -pub use typed_sled; +pub use redb::*; -pub struct Database { - pub db: sled::Db, +pub const T_USER: TableDefinition<&str, Ser<User>> = TableDefinition::new("user"); +pub const T_USER_NODE: TableDefinition<(&str, &str), Ser<NodeUserData>> = + TableDefinition::new("user_node"); +pub const T_INVITE: TableDefinition<&str, Ser<()>> = TableDefinition::new("invite"); +pub const T_NODE: TableDefinition<&str, Ser<Node>> = TableDefinition::new("node"); +pub const T_NODE_IMPORT: TableDefinition<&str, Ser<Vec<(Vec<usize>, Node)>>> = + TableDefinition::new("node_import"); - pub user: Tree<String, User>, - pub user_node: Tree<(String, String), NodeUserData>, - pub invite: Tree<String, ()>, - pub node: Tree<String, Node>, - - pub node_import: Tree<String, Vec<(Vec<usize>, Node)>>, +pub struct DataAcid { + pub inner: redb::Database, } -impl Database { +impl DataAcid { pub fn open(path: &Path) -> Result<Self, anyhow::Error> { - info!("opening database… (might take up to O(n) time)"); - let db = sled::open(path).context("opening database")?; - info!("creating trees"); - let r = Ok(Self { - user: Tree::open(&db, "user"), - invite: Tree::open(&db, "invite"), - node: Tree::open(&db, "node"), - user_node: Tree::open(&db, "user_node"), - node_import: Tree::open(&db, "node_import"), - db, - }); + info!("database"); + let db = redb::Database::create(path)?; + let r = Self { inner: db }; + + { + let txn = r.begin_write()?; + drop(txn.open_table(T_INVITE)?); + drop(txn.open_table(T_USER)?); + drop(txn.open_table(T_USER_NODE)?); + drop(txn.open_table(T_NODE)?); + drop(txn.open_table(T_NODE_IMPORT)?); + txn.commit()?; + } + info!("ready"); - r + Ok(r) + } +} + +impl Deref for DataAcid { + type Target = Database; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +pub trait TableExt<Key, KeyRef, Value> { + fn get(self, db: &DataAcid, key: KeyRef) -> anyhow::Result<Option<Value>>; + fn insert(self, db: &DataAcid, key: KeyRef, value: Value) -> anyhow::Result<()>; + fn remove(self, db: &DataAcid, key: KeyRef) -> anyhow::Result<Option<Value>>; +} +impl<'a, 'b, 'c, Key, Value, KeyRef> TableExt<Key, KeyRef, Value> + for TableDefinition<'a, Key, Ser<Value>> +where + Key: Borrow<<Key as RedbValue>::SelfType<'b>> + redb::RedbKey, + Value: bincode::Encode + bincode::Decode + std::fmt::Debug, + KeyRef: Borrow<<Key as redb::RedbValue>::SelfType<'c>>, +{ + fn get(self, db: &DataAcid, key: KeyRef) -> anyhow::Result<Option<Value>> { + let txn = db.inner.begin_read()?; + let table = txn.open_table(self)?; + let user = table.get(key)?.map(|v| v.value().0); + drop(table); + Ok(user) + } + fn insert(self, db: &DataAcid, key: KeyRef, value: Value) -> anyhow::Result<()> { + let txn = db.inner.begin_write()?; + let mut table = txn.open_table(self)?; + table.insert(key, Ser(value))?; + drop(table); + txn.commit()?; + Ok(()) + } + fn remove(self, db: &DataAcid, key: KeyRef) -> anyhow::Result<Option<Value>> { + let txn = db.inner.begin_write()?; + let mut table = txn.open_table(self)?; + let prev = table.remove(key)?.map(|v| v.value().0); + drop(table); + txn.commit()?; + Ok(prev) + } +} + +// pub trait TableIterExt< +// 'a, +// Key: redb::RedbKey + 'static, +// Value: redb::RedbValue + 'static, +// F: FnOnce(&redb::Range<'a, Key, Value>) -> anyhow::Result<T>, +// T: 'static, +// > +// { +// fn iter(self, db: &'a DataAcid, f: F) -> anyhow::Result<T>; +// } +// impl<'a, Key, Value, F, T> TableIterExt<'a, Key, Value, F, T> +// for TableDefinition<'static, Key, Value> +// where +// Key: redb::RedbKey, +// Value: redb::RedbValue, +// F: FnOnce(&redb::Range<'a, Key, Value>) -> anyhow::Result<T>, +// T: 'static, +// { +// fn iter(self, db: &DataAcid, f: F) -> anyhow::Result<T> { +// let txn = db.begin_read()?; +// let table = txn.open_table(self)?; +// let iter = table.iter()?; +// let ret = f(&iter)?; +// drop(iter); +// drop(table); +// drop(txn); +// Ok(ret) +// } +// } + +#[derive(Debug)] +#[cfg(not(feature = "db_json"))] +pub struct Ser<T>(pub T); +#[cfg(not(feature = "db_json"))] +impl<T: Encode + Decode + std::fmt::Debug> RedbValue for Ser<T> { + type SelfType<'a> = Ser<T> + where + Self: 'a; + type AsBytes<'a> = Vec<u8> + where + Self: 'a; + + fn fixed_width() -> Option<usize> { + None + } + + fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a> + where + Self: 'a, + { + Ser(bincode::decode_from_slice(data, bincode::config::legacy()) + .unwrap() + .0) + } + + fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a> + where + Self: 'a, + Self: 'b, + { + bincode::encode_to_vec(&value.0, bincode::config::legacy()).unwrap() + } + + fn type_name() -> redb::TypeName { + TypeName::new("bincode") + } +} + +#[derive(Debug)] +#[cfg(feature = "db_json")] +pub struct Ser<T>(pub T); +#[cfg(feature = "db_json")] +impl<T: Serialize + for<'a> Deserialize<'a> + std::fmt::Debug> RedbValue for Ser<T> { + type SelfType<'a> = Ser<T> + where + Self: 'a; + type AsBytes<'a> = Vec<u8> + where + Self: 'a; + + fn fixed_width() -> Option<usize> { + None + } + + fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a> + where + Self: 'a, + { + Ser(serde_json::from_slice(data).unwrap()) + } + + fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a> + where + Self: 'a, + Self: 'b, + { + serde_json::to_vec(&value.0).unwrap() + } + + fn type_name() -> redb::TypeName { + TypeName::new("json") } } |