/* This file is part of jellything (https://codeberg.org/metamuffin/jellything) which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin */ use crate::backends::{ Database, ReadTransaction, ReadTxnFunction, WriteTransaction, WriteTxnFunction, }; use anyhow::Result; use redb::{AccessGuard, ReadableDatabase, ReadableTable, StorageError, Table, TableDefinition}; use std::path::Path; const TABLE: TableDefinition<&[u8], &[u8]> = TableDefinition::new("kv"); pub fn new(path: &Path) -> Result { Ok(redb::Database::create(path)?) } impl Database for redb::Database { fn write_transaction(&self, f: &mut WriteTxnFunction) -> Result<()> { let txn = self.begin_write()?; let mut table = txn.open_table(TABLE)?; f(&mut table)?; drop(table); txn.commit()?; Ok(()) } fn read_transaction(&self, f: &mut ReadTxnFunction) -> Result<()> { let mut txn = self.begin_read()?; f(&mut txn)?; Ok(()) } } impl WriteTransaction for Table<'_, &[u8], &[u8]> { fn set(&mut self, key: &[u8], value: &[u8]) -> Result<()> { self.insert(key, value)?; Ok(()) } fn del(&mut self, key: &[u8]) -> Result<()> { self.remove(key)?; Ok(()) } } impl ReadTransaction for Table<'_, &[u8], &[u8]> { fn get<'a>(&'a self, key: &[u8]) -> Result>> { match >::get(self, key)? { Some(v) => Ok(Some(v.value().to_vec())), None => Ok(None), } } fn iter<'a>( &'a self, key: &[u8], reverse: bool, ) -> Result>> + 'a>> { let k = |e: Result<(AccessGuard<'_, &[u8]>, AccessGuard<'_, &[u8]>), StorageError>| { e.map(|e| e.0.value().to_vec()).map_err(|e| e.into()) }; Ok(if reverse { Box::new(self.range(..=key)?.rev().map(k)) } else { Box::new(self.range(key..)?.map(k)) }) } } impl ReadTransaction for redb::ReadTransaction { fn get<'a>(&'a self, key: &[u8]) -> Result>> { match self.open_table(TABLE)?.get(key)? { Some(v) => Ok(Some(v.value().to_vec())), None => Ok(None), } } fn iter<'a>( &'a self, key: &[u8], reverse: bool, ) -> Result>> + 'a>> { let k = |e: Result<(AccessGuard<'_, &[u8]>, AccessGuard<'_, &[u8]>), StorageError>| { e.map(|e| e.0.value().to_vec()).map_err(|e| e.into()) }; Ok(if reverse { Box::new(self.open_table(TABLE)?.range(..=key)?.rev().map(k)) } else { Box::new(self.open_table(TABLE)?.range(key..)?.map(k)) }) } }