This commit is contained in:
Aode (lion) 2022-03-11 19:26:32 -06:00
commit 8ddc953db5
4 changed files with 272 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/target
Cargo.lock
db.persy

9
Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "persy-trees"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
persy = "1.2"

34
examples/demo.rs Normal file
View file

@ -0,0 +1,34 @@
use persy::ByteVec;
use persy_trees::{Db, Tree};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let db = Db::open("./db.persy")?;
let tree: Tree<String, ByteVec> = db.open_tree("some-tree-name")?;
let mut tx1 = tree.transaction()?;
let mut tx2 = tree.transaction()?;
let put: ByteVec = vec![0, 1, 2, 3, 4].into();
tx1.insert("howdy".into(), put.clone())?;
tx2.insert("meowdy".into(), put.clone())?;
let got1 = tx1.get(&"howdy".into())?.expect("We just inserted this");
let got2 = tx2.get(&"meowdy".into())?.expect("We just inserted this");
assert_eq!(got1, put);
assert_eq!(got2, put);
for (k, v) in tx1.range(..)? {
println!("{}: {:?}", k, &*v);
}
for (k, v) in tx2.range(..)? {
println!("{}: {:?}", k, &*v);
}
tx1.commit()?;
tx2.commit()?;
Ok(())
}

226
src/lib.rs Normal file
View file

@ -0,0 +1,226 @@
use persy::{Config, IndexId, IndexType, Persy, PersyError, SegmentId, TxIndexIter, ValueMode, PE};
use std::marker::PhantomData;
use std::ops::RangeBounds;
use std::path::Path;
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
}
#[derive(Debug)]
pub enum ErrorKind {
Persy(PersyError),
}
#[derive(Clone)]
pub struct Db {
persy: Persy,
}
pub struct Tree<K, V> {
name: String,
_segment_id: SegmentId,
_index_id: IndexId,
persy: Persy,
_key: PhantomData<K>,
_value: PhantomData<V>,
}
pub struct Transaction<'a, K, V> {
tree: &'a Tree<K, V>,
tx: persy::Transaction,
}
pub struct Iter<'a, K: IndexType, V: IndexType> {
iter: TxIndexIter<'a, K, V>,
}
impl Db {
pub fn open(path: impl AsRef<Path>) -> Result<Self, Error> {
let persy = Persy::open_or_create_with(path, Config::new(), |_| Ok(()))?;
Ok(Self { persy })
}
pub fn open_tree<K, V, N>(&self, tree_name: N) -> Result<Tree<K, V>, Error>
where
K: IndexType,
V: IndexType,
N: Into<String>,
{
let segment_id;
let index_id;
let name = tree_name.into();
let mut tx = self.persy.begin()?;
if tx.exists_segment(&name)? {
segment_id = tx.solve_segment_id(&name)?;
index_id = tx.solve_index_id(&name)?;
} else {
segment_id = tx.create_segment(&name)?;
tx.create_index::<K, V>(&name, ValueMode::Replace)?;
index_id = tx.solve_index_id(&name)?;
}
let tx = tx.prepare()?;
tx.commit()?;
Ok(Tree {
name,
_segment_id: segment_id,
_index_id: index_id,
persy: self.persy.clone(),
_key: PhantomData,
_value: PhantomData,
})
}
}
impl<K, V> Tree<K, V> {
pub fn transaction(&self) -> Result<Transaction<'_, K, V>, Error> {
let tx = self.persy.begin()?;
Ok(Transaction { tree: self, tx })
}
}
impl<'a, K, V> Transaction<'a, K, V>
where
K: IndexType,
V: IndexType,
{
pub fn get(&mut self, key: &K) -> Result<Option<V>, Error> {
Ok(self.tx.one(&self.tree.name, key)?)
}
pub fn range<R: RangeBounds<K>>(&mut self, range: R) -> Result<Iter<'_, K, V>, Error> {
Ok(Iter::new(self.tx.range(&self.tree.name, range)?))
}
pub fn insert(&mut self, key: K, value: V) -> Result<(), Error> {
self.tx.put(&self.tree.name, key, value)?;
Ok(())
}
pub fn remove(&mut self, key: K) -> Result<(), Error> {
self.tx.remove(&self.tree.name, key, None as Option<V>)?;
Ok(())
}
pub fn commit(self) -> Result<(), Error> {
self.tx.prepare()?.commit()?;
Ok(())
}
pub fn rollback(self) -> Result<(), Error> {
self.tx.rollback()?;
Ok(())
}
}
impl<'a, K, V> Iter<'a, K, V>
where
K: IndexType,
V: IndexType,
{
fn new(iter: TxIndexIter<'a, K, V>) -> Self {
Self { iter }
}
}
impl<'a, K, V> Iterator for Iter<'a, K, V>
where
K: IndexType,
V: IndexType,
{
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
loop {
let (key, mut values) = self.iter.next()?;
if let Some(value) = values.next() {
return Some((key, value));
}
}
}
}
impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V>
where
K: IndexType,
V: IndexType,
{
fn next_back(&mut self) -> Option<Self::Item> {
loop {
let (key, mut values) = self.iter.next_back()?;
if let Some(value) = values.next() {
return Some((key, value));
}
}
}
}
impl<K, V> Clone for Tree<K, V> {
fn clone(&self) -> Self {
Self {
name: self.name.clone(),
_segment_id: self._segment_id,
_index_id: self._index_id.clone(),
persy: self.persy.clone(),
_key: PhantomData,
_value: PhantomData,
}
}
}
impl<T: Into<PersyError>> From<PE<T>> for ErrorKind {
fn from(e: PE<T>) -> Self {
ErrorKind::Persy(e.persy_error())
}
}
impl<T> From<T> for Error
where
ErrorKind: From<T>,
{
fn from(e: T) -> Self {
ErrorKind::from(e).into()
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.kind, f)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.kind.source()
}
}
impl std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Persy(_) => write!(f, "Error in storage engine"),
}
}
}
impl std::error::Error for ErrorKind {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Persy(ref err) => Some(err),
}
}
}