From f5a85c4260337def676d5741705099d08204ee11 Mon Sep 17 00:00:00 2001 From: Olivier 'reivilibre Date: Mon, 8 May 2023 21:43:33 +0100 Subject: [PATCH] Overhaul again because the previous iteration did not provide the desired ergonomics --- Cargo.toml | 5 +- src/inouts.rs | 148 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/table/wrapped.rs | 49 +++++++------- src/tests.rs | 2 +- 5 files changed, 180 insertions(+), 25 deletions(-) create mode 100644 src/inouts.rs diff --git a/Cargo.toml b/Cargo.toml index 3acd7ce..4cec893 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/inouts.rs b/src/inouts.rs new file mode 100644 index 0000000..97fa2c2 --- /dev/null +++ b/src/inouts.rs @@ -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, Box>; +} + +/// 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, Box>; +} + +//// Implementations for bytes + +impl<'a> TryIntoCowBytes<'a> for Cow<'a, [u8]> { + fn to_cow_bytes(self) -> Result, Box> { + Ok(self) + } +} + +impl<'a> TryIntoCowBytes<'a> for &'a [u8] { + fn to_cow_bytes(self) -> Result, Box> { + Ok(Cow::Borrowed(self)) + } +} + +impl<'a> TryIntoCowBytes<'a> for Vec { + fn to_cow_bytes(self) -> Result, Box> { + Ok(Cow::Owned(self)) + } +} + +impl<'a> In<'a, Vec> for Cow<'a, [u8]> {} +impl<'a> In<'a, Vec> for &'a [u8] {} +impl<'a> In<'a, Vec> for Vec {} + +impl Out for Vec { + type Produce<'txn> = Cow<'txn, [u8]>; + + fn from_cow_bytes<'a>(cow_bytes: Cow<'a, [u8]>) -> Result, Box> { + Ok(cow_bytes) + } +} + +//// Implementations for strings + +impl<'a> TryIntoCowBytes<'a> for Cow<'a, str> { + fn to_cow_bytes(self) -> Result, Box> { + 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, Box> { + Ok(Cow::Borrowed(self.as_bytes())) + } +} + +impl<'a> TryIntoCowBytes<'a> for String { + fn to_cow_bytes(self) -> Result, Box> { + 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, Box> { + 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(pub T); + +impl<'a, T: Serialize + 'a> TryIntoCowBytes<'a> for Bare { + fn to_cow_bytes(self) -> Result, Box> { + 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, Box> { +// let bytes = SerdeBare::serialise(self.0)?; +// Ok(bytes.as_cow_bytes()) +// } +// } + +impl<'a, T: Serialize + 'a> In<'a, Bare> for Bare {} +impl<'a, T: Serialize + 'a> In<'a, Bare> for Bare<&'a T> {} + +// TODO BARE doesn't support borrowing bytes from the input yet. +impl Out for Bare { + type Produce<'txn> = T; + + fn from_cow_bytes<'a>(cow_bytes: Cow<'a, [u8]>) -> Result, Box> { + let serde_bare = SerdeBare::try_from_bytes(cow_bytes).unwrap(); + serde_bare.deserialise() + } +} + +//// Implementations for CBOR + +pub struct Cbor(pub T); + +impl<'a, T: Serialize + 'a> TryIntoCowBytes<'a> for Cbor { + fn to_cow_bytes(self) -> Result, Box> { + let bytes = SerdeCbor::serialise(&self.0)?; + Ok(Cow::Owned(bytes.into_bytes())) + } +} + +impl<'a, T: Serialize + 'a> In<'a, Cbor> for Cbor {} +impl<'a, T: Serialize + 'a> In<'a, Cbor> for Cbor<&'a T> {} + +// TODO BARE doesn't support borrowing bytes from the input yet. +impl Deserialize<'a>> Out for Cbor { + type Produce<'txn> = T; + + fn from_cow_bytes<'a>(cow_bytes: Cow<'a, [u8]>) -> Result, Box> { + let serde_cbor = SerdeCbor::try_from_bytes(cow_bytes).unwrap(); + serde_cbor.deserialise() + } +} diff --git a/src/lib.rs b/src/lib.rs index 14ae332..16e1136 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod environment; +pub mod inouts; pub mod table; #[cfg(test)] diff --git a/src/table/wrapped.rs b/src/table/wrapped.rs index b26ff31..29d7c05 100644 --- a/src/table/wrapped.rs +++ b/src/table/wrapped.rs @@ -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 { +pub struct Table { pub raw: RawTable, - pub _marker: PhantomData<(K, V)>, + pub _marker: PhantomData<(KRep, VRep)>, } -impl ByteLamination<'a>, V: for<'a> ByteLamination<'a>> Table { - pub fn get<'txn, 'qk, QK: ByteLaminationHelper<'qk, K>, TK: TransactionKind>( - &self, - txn: &Txn<'txn, TK>, +impl Table { + pub fn get<'txn, 'this, 'qk, QK: In<'qk, KRep>, TK: TransactionKind>( + &'this self, + txn: &'this Txn<'txn, TK>, k: QK, - ) -> anyhow::Result> { - let k_bytes = k.as_cow_bytes_helper(); + ) -> anyhow::Result>> { + 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 Table { + 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 + ?Sized), + k: QK, ) -> anyhow::Result { - 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) } diff --git a/src/tests.rs b/src/tests.rs index d9900de..d230a9d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -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!"); } }