Overhaul again because the previous iteration did not provide the desired ergonomics
This commit is contained in:
parent
1a7c380275
commit
f5a85c4260
|
@ -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"
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
pub mod environment;
|
||||
pub mod inouts;
|
||||
pub mod table;
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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!");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue