use std::marker::PhantomData;
use std::str::from_utf8;
use serde::{de::DeserializeOwned, Serialize};
use serde_json::{from_str, to_string};
type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
SystemError(String),
Serialization(String),
PermissionDenied,
}
pub trait Collection {
fn put<T>(&mut self, key: &str, value: T) -> Result<Option<T>>
where
T: Serialize + DeserializeOwned;
fn get<T>(&mut self, key: &str) -> Result<Option<T>>
where
T: Serialize + DeserializeOwned;
fn prefix<'a, T>(&mut self, prefix: &str) -> Box<dyn Iterator<Item = Result<T>> + 'a>
where
T: Serialize + DeserializeOwned + 'a;
}
pub struct Sled {
db: sled::Db,
}
impl Sled {
pub fn open(file: &str) -> Result<Self> {
let db = match sled::open(file) {
Ok(db) => db,
Err(e) => return Err(Error::SystemError(e.to_string())),
};
Ok(Self { db: db })
}
}
impl Collection for Sled {
fn put<T>(&mut self, key: &str, value: T) -> Result<Option<T>>
where
T: Serialize + DeserializeOwned,
{
let json = match to_string(&value) {
Ok(str) => str,
Err(err) => return Err(Error::Serialization(err.to_string())),
};
let value = match self.db.insert(key.as_bytes(), json.as_bytes()) {
Ok(value) => match value {
Some(value) => value,
None => return Ok(None),
},
Err(err) => return Err(Error::SystemError(err.to_string())),
};
let json = match from_utf8(&value) {
Ok(json) => json,
Err(err) => return Err(Error::Serialization(err.to_string())),
};
let obj: T = match from_str(json) {
Ok(json) => json,
Err(err) => return Err(Error::Serialization(err.to_string())),
};
Ok(Some(obj))
}
fn get<T>(&mut self, key: &str) -> Result<Option<T>>
where
T: Serialize + DeserializeOwned,
{
let ivec = match self.db.get(key.as_bytes()) {
Err(err) => return Err(Error::SystemError(err.to_string())),
Ok(ivec) => match ivec {
Some(ivec) => ivec,
None => return Ok(None),
},
};
let json = match from_utf8(&ivec) {
Ok(json) => json,
Err(err) => return Err(Error::Serialization(err.to_string())),
};
let json: T = match from_str(json) {
Ok(json) => json,
Err(err) => return Err(Error::Serialization(err.to_string())),
};
Ok(Some(json))
}
fn prefix<'a, T>(&mut self, prefix: &str) -> Box<dyn Iterator<Item = Result<T>> + 'a>
where
T: Serialize + DeserializeOwned + 'a,
{
Box::new(SledIter {
p: PhantomData,
iter: self.db.scan_prefix(prefix.as_bytes()),
})
}
}
pub struct SledIter<T> {
p: PhantomData<T>,
iter: sled::Iter,
}
impl<T> Iterator for SledIter<T>
where
T: DeserializeOwned,
{
type Item = Result<T>;
fn next(&mut self) -> Option<Self::Item> {
let data = match self.iter.next() {
Some(r) => match r {
Ok(data) => data,
Err(e) => return Some(Err(Error::SystemError(e.to_string()))),
},
None => return None,
};
let json = match from_utf8(&(data.1)) {
Ok(json) => json,
Err(err) => return Some(Err(Error::Serialization(err.to_string()))),
};
let json: T = match from_str(json) {
Ok(json) => json,
Err(err) => return Some(Err(Error::Serialization(err.to_string()))),
};
Some(Ok(json))
}
}
#[cfg(test)]
mod tests {
use crate::collection::*;
use serde::{Deserialize, Serialize};
use tempfile::tempdir;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
struct Person {
name: String,
}
#[test]
fn collection_get_put() {
let file = tempdir().expect("unable to make temp file");
let mut s = Sled::open(file.path().to_str().unwrap()).expect("failed to open with sled");
let p = Person {
name: "foo".to_string(),
};
s.put::<Person>(p.name.as_str(), p.clone())
.expect("failed to put foo");
let r = s
.get::<Person>(p.name.as_str())
.expect("failed to get foo")
.unwrap();
assert_eq!(p, r);
assert_eq!(
s.get::<Person>("i don't exist")
.expect("failed to get non-existant"),
None
);
}
#[test]
fn collection_prefix() {
let file = tempdir().expect("unable to make temp file");
let mut s = Sled::open(file.path().to_str().unwrap()).expect("failed to open with sled");
let pp = vec![
Person {
name: "foo".to_string(),
},
Person {
name: "bar/foo0".to_string(),
},
Person {
name: "bar/foo1".to_string(),
},
Person {
name: "bar/foo2".to_string(),
},
Person {
name: "baz".to_string(),
},
];
for p in pp {
s.put::<Person>(p.name.as_str(), p.clone())
.expect("failed to put");
}
let rr = s
.prefix::<Person>("bar/")
.map(|r| r.expect("collecting"))
.collect::<Vec<Person>>();
let exp = vec![
Person {
name: "bar/foo0".to_string(),
},
Person {
name: "bar/foo1".to_string(),
},
Person {
name: "bar/foo2".to_string(),
},
];
assert_eq!(rr, exp);
}
}