Overhaul again because the previous iteration did not provide the desired ergonomics

This commit is contained in:
Olivier 'reivilibre' 2023-05-08 21:43:33 +01:00
parent 1a7c380275
commit f5a85c4260
5 changed files with 180 additions and 25 deletions

View File

@ -4,7 +4,7 @@ description = "Fancy wrapper for libMDBX"
repository = "https://git.emunest.net/reivilibre/fancy_mdbx.git"
authors = ["Olivier 'reivilibre'"]
license = "MIT OR Apache-2.0"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -20,8 +20,9 @@ libmdbx = "0.3.3"
ouroboros = "0.14.2"
byte_lamination = "0.1.1"
byte_lamination = { version = "0.1.1", features = ["bare", "cbor", "zstd"] }
serde = { version = "1.0.162", features = ["derive"] }
[dev-dependencies]
tempfile = "3.3.0"

148
src/inouts.rs Normal file
View File

@ -0,0 +1,148 @@
use byte_lamination::serialisation::bare::SerdeBare;
use byte_lamination::serialisation::cbor::SerdeCbor;
use byte_lamination::ByteLamination;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::error::Error;
/// Type that can be passed in to a function wanting the bytes equivalent to `T`,
/// in the lifetime `'txn`.
pub trait In<'txn, T>: TryIntoCowBytes<'txn> {}
pub trait TryIntoCowBytes<'txn> {
fn to_cow_bytes(self) -> Result<Cow<'txn, [u8]>, Box<dyn Error>>;
}
/// Type that should be produced from a function giving the bytes equivalent to `Self`,
/// with lifetime `'txn`.
pub trait Out {
/// The type that should be produced.
type Produce<'txn>;
fn from_cow_bytes<'a>(cow_bytes: Cow<'a, [u8]>) -> Result<Self::Produce<'a>, Box<dyn Error>>;
}
//// Implementations for bytes
impl<'a> TryIntoCowBytes<'a> for Cow<'a, [u8]> {
fn to_cow_bytes(self) -> Result<Cow<'a, [u8]>, Box<dyn Error>> {
Ok(self)
}
}
impl<'a> TryIntoCowBytes<'a> for &'a [u8] {
fn to_cow_bytes(self) -> Result<Cow<'a, [u8]>, Box<dyn Error>> {
Ok(Cow::Borrowed(self))
}
}
impl<'a> TryIntoCowBytes<'a> for Vec<u8> {
fn to_cow_bytes(self) -> Result<Cow<'a, [u8]>, Box<dyn Error>> {
Ok(Cow::Owned(self))
}
}
impl<'a> In<'a, Vec<u8>> for Cow<'a, [u8]> {}
impl<'a> In<'a, Vec<u8>> for &'a [u8] {}
impl<'a> In<'a, Vec<u8>> for Vec<u8> {}
impl Out for Vec<u8> {
type Produce<'txn> = Cow<'txn, [u8]>;
fn from_cow_bytes<'a>(cow_bytes: Cow<'a, [u8]>) -> Result<Self::Produce<'a>, Box<dyn Error>> {
Ok(cow_bytes)
}
}
//// Implementations for strings
impl<'a> TryIntoCowBytes<'a> for Cow<'a, str> {
fn to_cow_bytes(self) -> Result<Cow<'a, [u8]>, Box<dyn Error>> {
Ok(match self {
Cow::Borrowed(str_slice) => Cow::Borrowed(str_slice.as_bytes()),
Cow::Owned(string) => Cow::Owned(string.into_bytes()),
})
}
}
impl<'a> TryIntoCowBytes<'a> for &'a str {
fn to_cow_bytes(self) -> Result<Cow<'a, [u8]>, Box<dyn Error>> {
Ok(Cow::Borrowed(self.as_bytes()))
}
}
impl<'a> TryIntoCowBytes<'a> for String {
fn to_cow_bytes(self) -> Result<Cow<'a, [u8]>, Box<dyn Error>> {
Ok(Cow::Owned(self.into_bytes()))
}
}
impl<'a> In<'a, String> for Cow<'a, str> {}
impl<'a> In<'a, String> for &'a str {}
impl<'a> In<'a, String> for String {}
impl Out for String {
type Produce<'txn> = Cow<'txn, str>;
fn from_cow_bytes<'a>(cow_bytes: Cow<'a, [u8]>) -> Result<Self::Produce<'a>, Box<dyn Error>> {
Ok(match cow_bytes {
Cow::Borrowed(bslice) => Cow::Borrowed(std::str::from_utf8(bslice)?),
Cow::Owned(bvec) => Cow::Owned(String::from_utf8(bvec)?),
})
}
}
//// Implementations for BARE
pub struct Bare<T>(pub T);
impl<'a, T: Serialize + 'a> TryIntoCowBytes<'a> for Bare<T> {
fn to_cow_bytes(self) -> Result<Cow<'a, [u8]>, Box<dyn Error>> {
let bytes = SerdeBare::serialise(&self.0)?;
Ok(Cow::Owned(bytes.into_bytes()))
}
}
// impl<'a, T: Serialize> TryIntoCowBytes<'a> for Bare<&T> {
// fn to_cow_bytes(self) -> Result<Cow<'a, [u8]>, Box<dyn Error>> {
// let bytes = SerdeBare::serialise(self.0)?;
// Ok(bytes.as_cow_bytes())
// }
// }
impl<'a, T: Serialize + 'a> In<'a, Bare<T>> for Bare<T> {}
impl<'a, T: Serialize + 'a> In<'a, Bare<T>> for Bare<&'a T> {}
// TODO BARE doesn't support borrowing bytes from the input yet.
impl<T: DeserializeOwned> Out for Bare<T> {
type Produce<'txn> = T;
fn from_cow_bytes<'a>(cow_bytes: Cow<'a, [u8]>) -> Result<Self::Produce<'a>, Box<dyn Error>> {
let serde_bare = SerdeBare::try_from_bytes(cow_bytes).unwrap();
serde_bare.deserialise()
}
}
//// Implementations for CBOR
pub struct Cbor<T>(pub T);
impl<'a, T: Serialize + 'a> TryIntoCowBytes<'a> for Cbor<T> {
fn to_cow_bytes(self) -> Result<Cow<'a, [u8]>, Box<dyn Error>> {
let bytes = SerdeCbor::serialise(&self.0)?;
Ok(Cow::Owned(bytes.into_bytes()))
}
}
impl<'a, T: Serialize + 'a> In<'a, Cbor<T>> for Cbor<T> {}
impl<'a, T: Serialize + 'a> In<'a, Cbor<T>> for Cbor<&'a T> {}
// TODO BARE doesn't support borrowing bytes from the input yet.
impl<T: for<'a> Deserialize<'a>> Out for Cbor<T> {
type Produce<'txn> = T;
fn from_cow_bytes<'a>(cow_bytes: Cow<'a, [u8]>) -> Result<Self::Produce<'a>, Box<dyn Error>> {
let serde_cbor = SerdeCbor::try_from_bytes(cow_bytes).unwrap();
serde_cbor.deserialise()
}
}

View File

@ -1,4 +1,5 @@
pub mod environment;
pub mod inouts;
pub mod table;
#[cfg(test)]

View File

@ -1,62 +1,67 @@
use crate::table::raw::RawTable;
use crate::environment::Txn;
use crate::inouts::{In, Out};
use anyhow::anyhow;
use byte_lamination::ByteLamination;
use libmdbx::{TransactionKind, RW};
use std::borrow::{Borrow, Cow};
use std::borrow::Cow;
use std::marker::PhantomData;
pub struct Table<K, V> {
pub struct Table<KRep, VRep> {
pub raw: RawTable,
pub _marker: PhantomData<(K, V)>,
pub _marker: PhantomData<(KRep, VRep)>,
}
impl<K: for<'a> ByteLamination<'a>, V: for<'a> ByteLamination<'a>> Table<K, V> {
pub fn get<'txn, 'qk, QK: ByteLaminationHelper<'qk, K>, TK: TransactionKind>(
&self,
txn: &Txn<'txn, TK>,
impl<KRep, VRep: Out> Table<KRep, VRep> {
pub fn get<'txn, 'this, 'qk, QK: In<'qk, KRep>, TK: TransactionKind>(
&'this self,
txn: &'this Txn<'txn, TK>,
k: QK,
) -> anyhow::Result<Option<V>> {
let k_bytes = k.as_cow_bytes_helper();
) -> anyhow::Result<Option<VRep::Produce<'this>>> {
let k_bytes = k
.to_cow_bytes()
.map_err(|e| anyhow!("failed to convert key value to bytes: {e:?}"))?;
let val_opt = self.raw.get(txn, &k_bytes)?;
match val_opt {
Some(cow_bytes) => {
let value = V::try_from_bytes(cow_bytes)
let value = VRep::from_cow_bytes(cow_bytes)
.map_err(|e| anyhow!("failed to convert value bytes to value: {e:?}"))?;
Ok(Some(value))
}
None => Ok(None),
}
}
}
pub fn put<
'txn,
'qk,
'qv,
QK: ByteLaminationHelper<'qk, K>,
QV: ByteLaminationHelper<'qv, V>,
>(
impl<KRep, VRep> Table<KRep, VRep> {
pub fn put<'txn, 'qk, 'qv, QK: In<'qk, KRep>, QV: In<'qv, VRep>>(
&self,
txn: &Txn<'txn, RW>,
k: QK,
v: QV,
) -> anyhow::Result<()> {
let k_bytes = k.as_cow_bytes_helper();
let v_bytes = v.as_cow_bytes_helper();
let k_bytes = k
.to_cow_bytes()
.map_err(|e| anyhow!("failed to convert key value to bytes: {e:?}"))?;
let v_bytes = v
.to_cow_bytes()
.map_err(|e| anyhow!("failed to convert value value to bytes: {e:?}"))?;
self.raw.put(txn, &k_bytes, &v_bytes)?;
Ok(())
}
/// Returns true if the key was present.
pub fn delete<'txn>(
pub fn delete<'txn, 'qk, QK: In<'qk, KRep>>(
&self,
txn: &Txn<'txn, RW>,
k: &(impl Borrow<K> + ?Sized),
k: QK,
) -> anyhow::Result<bool> {
let k_bytes = k.borrow().as_cow_bytes();
let k_bytes = k
.to_cow_bytes()
.map_err(|e| anyhow!("failed to convert key value to bytes: {e:?}"))?;
self.raw.delete(txn, &k_bytes)
}

View File

@ -45,6 +45,6 @@ fn test_wrapped_str_basics() {
{
let txn = env.ro_txn().unwrap();
assert_eq!(db.get(&txn, "ooh").unwrap().unwrap().as_str(), "cool!");
assert_eq!(db.get(&txn, "ooh").unwrap().unwrap().as_ref(), "cool!");
}
}