impl src/entry, traits, backends

This commit is contained in:
hinto.janai 2024-12-15 10:45:02 -05:00
parent 7b8756fa80
commit 87de6de499
No known key found for this signature in database
GPG key ID: D47CE05FA175A499
8 changed files with 233 additions and 2 deletions

View file

@ -6,6 +6,7 @@ use std::{cell::RefCell, ops::RangeBounds};
use crate::{
backend::heed::types::HeedDb,
database::{DatabaseIter, DatabaseRo, DatabaseRw},
entry::{Entry, OccupiedEntry, VacantEntry},
error::{DbResult, RuntimeError},
table::Table,
};
@ -239,6 +240,19 @@ impl<T: Table> DatabaseRw<T> for HeedTableRw<'_, '_, T> {
Ok(false) => unreachable!(),
}
}
#[inline]
fn entry<'a>(&'a mut self, key: &'a T::Key) -> DbResult<Entry<'a, T, Self>> {
match DatabaseRo::get(self, key) {
Ok(value) => Ok(Entry::Occupied(OccupiedEntry {
db: self,
key,
value,
})),
Err(RuntimeError::KeyNotFound) => Ok(Entry::Vacant(VacantEntry { db: self, key })),
Err(e) => Err(e),
}
}
}
//---------------------------------------------------------------------------------------------------- Tests

View file

@ -11,6 +11,7 @@ use crate::{
types::{RedbTableRo, RedbTableRw},
},
database::{DatabaseIter, DatabaseRo, DatabaseRw},
entry::{Entry, OccupiedEntry, VacantEntry},
error::{DbResult, RuntimeError},
table::Table,
};
@ -162,7 +163,7 @@ unsafe impl<T: Table + 'static> DatabaseRo<T> for RedbTableRw<'_, T::Key, T::Val
}
}
impl<T: Table + 'static> DatabaseRw<T> for RedbTableRw<'_, T::Key, T::Value> {
impl<T: Table> DatabaseRw<T> for RedbTableRw<'_, T::Key, T::Value> {
// `redb` returns the value after function calls so we end with Ok(()) instead.
#[inline]
@ -197,6 +198,19 @@ impl<T: Table + 'static> DatabaseRw<T> for RedbTableRw<'_, T::Key, T::Value> {
let (key, value) = redb::Table::pop_last(self)?.ok_or(RuntimeError::KeyNotFound)?;
Ok((key.value(), value.value()))
}
#[inline]
fn entry<'a>(&'a mut self, key: &'a T::Key) -> DbResult<Entry<'a, T, Self>> {
match get::<T>(self, key) {
Ok(value) => Ok(Entry::Occupied(OccupiedEntry {
db: self,
key,
value,
})),
Err(RuntimeError::KeyNotFound) => Ok(Entry::Vacant(VacantEntry { db: self, key })),
Err(e) => Err(e),
}
}
}
//---------------------------------------------------------------------------------------------------- Tests

View file

@ -4,6 +4,7 @@
use std::ops::RangeBounds;
use crate::{
entry::Entry,
error::{DbResult, RuntimeError},
table::Table,
};
@ -151,7 +152,7 @@ pub unsafe trait DatabaseRo<T: Table> {
/// Database (key-value store) read/write abstraction.
///
/// All [`DatabaseRo`] functions are also callable by [`DatabaseRw`].
pub trait DatabaseRw<T: Table>: DatabaseRo<T> {
pub trait DatabaseRw<T: Table>: DatabaseRo<T> + Sized {
/// Insert a key-value pair into the database.
///
/// This will overwrite any existing key-value pairs.
@ -211,4 +212,7 @@ pub trait DatabaseRw<T: Table>: DatabaseRo<T> {
///
#[doc = doc_database!()]
fn pop_last(&mut self) -> DbResult<(T::Key, T::Value)>;
/// TODO
fn entry<'a>(&'a mut self, key: &'a T::Key) -> DbResult<Entry<'a, T, Self>>;
}

View file

@ -0,0 +1,96 @@
//! TODO
use crate::{
entry::{OccupiedEntry, VacantEntry},
DatabaseRw, DbResult, Table,
};
pub enum Entry<'a, T, D>
where
T: Table,
D: DatabaseRw<T>,
{
Vacant(VacantEntry<'a, T, D>),
Occupied(OccupiedEntry<'a, T, D>),
}
impl<'a, T, D> Entry<'a, T, D>
where
T: Table,
D: DatabaseRw<T>,
{
/// TODO
pub fn or_insert(self, default: &T::Value) -> DbResult<()> {
match self {
Self::Occupied(_) => Ok(()),
Self::Vacant(entry) => entry.insert(default),
}
}
/// TODO
pub fn or_insert_with<F>(self, default: F) -> DbResult<()>
where
F: FnOnce() -> &'a T::Value,
{
match self {
Self::Occupied(_) => Ok(()),
Self::Vacant(entry) => entry.insert(default()),
}
}
/// TODO
pub fn or_insert_with_key<F>(self, default: F) -> DbResult<()>
where
F: FnOnce(&'a T::Key) -> &'a T::Value,
{
match self {
Self::Occupied(_) => Ok(()),
Self::Vacant(entry) => {
let key = entry.key;
entry.insert(default(key))
}
}
}
/// TODO
pub const fn key(&self) -> &T::Key {
match self {
Self::Occupied(entry) => entry.key(),
Self::Vacant(entry) => entry.key(),
}
}
/// TODO
pub const fn value(&self) -> Option<&T::Value> {
match self {
Self::Occupied(entry) => Some(entry.value()),
Self::Vacant(_) => None,
}
}
/// TODO
pub fn and_update<F>(self, f: F) -> DbResult<Self>
where
F: FnOnce(&mut T::Value),
{
Ok(match self {
Self::Occupied(mut entry) => {
entry.update(f)?;
Self::Occupied(entry)
}
Self::Vacant(entry) => Self::Vacant(entry),
})
}
}
impl<'a, T, D> Entry<'a, T, D>
where
T: Table,
<T as Table>::Value: Default,
D: DatabaseRw<T>,
{
/// TODO
pub fn or_default(self) -> &'a mut T::Value {
todo!()
}
}

View file

@ -0,0 +1,10 @@
//! TODO
#[expect(clippy::module_inception)]
mod entry;
mod occupied_entry;
mod vacant_entry;
pub use entry::Entry;
pub use occupied_entry::OccupiedEntry;
pub use vacant_entry::VacantEntry;

View file

@ -0,0 +1,58 @@
//! TODO
use crate::{DatabaseRw, DbResult, Table};
pub struct OccupiedEntry<'a, T, D>
where
T: Table,
D: DatabaseRw<T>,
{
pub(crate) db: &'a mut D,
pub(crate) key: &'a T::Key,
pub(crate) value: T::Value,
}
impl<T, D> OccupiedEntry<'_, T, D>
where
T: Table,
D: DatabaseRw<T>,
{
/// TODO
pub const fn key(&self) -> &T::Key {
self.key
}
/// TODO
pub const fn value(&self) -> &T::Value {
&self.value
}
/// TODO
pub fn update<F>(&mut self, f: F) -> DbResult<()>
where
F: FnOnce(&mut T::Value),
{
f(&mut self.value);
DatabaseRw::put(self.db, self.key, &self.value)
}
/// TODO
pub fn insert(&mut self, value: &T::Value) -> DbResult<()> {
DatabaseRw::put(self.db, self.key, value)
}
/// TODO
pub fn remove(self) -> DbResult<T::Value> {
DatabaseRw::delete(self.db, self.key).map(|()| Ok(self.value))?
}
/// TODO
pub const fn get(&self) -> &T::Value {
&self.value
}
/// TODO
pub fn into_value(self) -> T::Value {
self.value
}
}

View file

@ -0,0 +1,34 @@
//! TODO
use crate::{DatabaseRw, DbResult, RuntimeError, Table};
pub struct VacantEntry<'a, T, D>
where
T: Table,
D: DatabaseRw<T>,
{
pub(crate) db: &'a mut D,
pub(crate) key: &'a T::Key,
}
impl<T, D> VacantEntry<'_, T, D>
where
T: Table,
D: DatabaseRw<T>,
{
/// TODO
pub const fn key(&self) -> &T::Key {
self.key
}
/// TODO
pub fn insert(self, value: &T::Value) -> DbResult<()> {
match DatabaseRw::put(self.db, self.key, value) {
Ok(()) => Ok(()),
Err(RuntimeError::KeyExists) => {
unreachable!("this error popping up while VacantEntry exists is a logical error")
}
Err(e) => Err(e),
}
}
}

View file

@ -42,6 +42,7 @@ mod tables;
mod transaction;
pub mod config;
pub mod entry;
pub mod resize;
pub use backend::ConcreteEnv;