mod directory_layer;
mod directory_partition;
mod directory_subspace;
mod error;
mod node;
use crate::tuple::{PackResult, Subspace, TuplePack, TupleUnpack};
use crate::Transaction;
use async_trait::async_trait;
use core::cmp;
pub use directory_layer::DirectoryLayer;
pub use directory_partition::DirectoryPartition;
pub use directory_subspace::DirectorySubspace;
pub use error::DirectoryError;
use std::cmp::Ordering;
#[async_trait]
pub trait Directory {
async fn create_or_open(
&self,
txn: &Transaction,
path: &[String],
prefix: Option<&[u8]>,
layer: Option<&[u8]>,
) -> Result<DirectoryOutput, DirectoryError>;
async fn create(
&self,
txn: &Transaction,
path: &[String],
prefix: Option<&[u8]>,
layer: Option<&[u8]>,
) -> Result<DirectoryOutput, DirectoryError>;
async fn open(
&self,
txn: &Transaction,
path: &[String],
layer: Option<&[u8]>,
) -> Result<DirectoryOutput, DirectoryError>;
async fn exists(&self, trx: &Transaction, path: &[String]) -> Result<bool, DirectoryError>;
async fn move_directory(
&self,
trx: &Transaction,
new_path: &[String],
) -> Result<DirectoryOutput, DirectoryError>;
async fn move_to(
&self,
trx: &Transaction,
old_path: &[String],
new_path: &[String],
) -> Result<DirectoryOutput, DirectoryError>;
async fn remove(&self, trx: &Transaction, path: &[String]) -> Result<bool, DirectoryError>;
async fn remove_if_exists(
&self,
trx: &Transaction,
path: &[String],
) -> Result<bool, DirectoryError>;
async fn list(&self, trx: &Transaction, path: &[String])
-> Result<Vec<String>, DirectoryError>;
}
pub(crate) fn compare_slice<T: Ord>(a: &[T], b: &[T]) -> cmp::Ordering {
for (ai, bi) in a.iter().zip(b.iter()) {
match ai.cmp(bi) {
Ordering::Equal => continue,
ord => return ord,
}
}
a.len().cmp(&b.len())
}
#[derive(Clone, Debug)]
pub enum DirectoryOutput {
DirectorySubspace(DirectorySubspace),
DirectoryPartition(DirectoryPartition),
}
impl DirectoryOutput {
pub fn subspace<T: TuplePack>(&self, t: &T) -> Result<Subspace, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => Ok(d.subspace(t)),
DirectoryOutput::DirectoryPartition(_) => {
Err(DirectoryError::CannotOpenDirectoryPartition)
}
}
}
pub fn bytes(&self) -> Result<&[u8], DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => Ok(d.bytes()),
DirectoryOutput::DirectoryPartition(_) => {
Err(DirectoryError::CannotOpenDirectoryPartition)
}
}
}
pub fn pack<T: TuplePack>(&self, t: &T) -> Result<Vec<u8>, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => Ok(d.pack(t)),
DirectoryOutput::DirectoryPartition(_) => {
Err(DirectoryError::CannotPackDirectoryPartition)
}
}
}
pub fn unpack<'de, T: TupleUnpack<'de>>(
&self,
key: &'de [u8],
) -> Result<PackResult<T>, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => Ok(d.unpack(key)),
DirectoryOutput::DirectoryPartition(_) => {
Err(DirectoryError::CannotPackDirectoryPartition)
}
}
}
pub fn range(&self) -> Result<(Vec<u8>, Vec<u8>), DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => Ok(d.range()),
DirectoryOutput::DirectoryPartition(_) => {
Err(DirectoryError::CannotRangeDirectoryPartition)
}
}
}
pub fn get_path(&self) -> &[String] {
match self {
DirectoryOutput::DirectorySubspace(d) => d.get_path(),
DirectoryOutput::DirectoryPartition(d) => d.get_path(),
}
}
pub fn get_layer(&self) -> &[u8] {
match self {
DirectoryOutput::DirectorySubspace(d) => d.get_layer(),
DirectoryOutput::DirectoryPartition(d) => d.get_layer(),
}
}
}
#[async_trait]
impl Directory for DirectoryOutput {
async fn create_or_open(
&self,
txn: &Transaction,
path: &[String],
prefix: Option<&[u8]>,
layer: Option<&[u8]>,
) -> Result<DirectoryOutput, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => {
d.create_or_open(txn, path, prefix, layer).await
}
DirectoryOutput::DirectoryPartition(d) => {
d.create_or_open(txn, path, prefix, layer).await
}
}
}
async fn create(
&self,
txn: &Transaction,
path: &[String],
prefix: Option<&[u8]>,
layer: Option<&[u8]>,
) -> Result<DirectoryOutput, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => d.create(txn, path, prefix, layer).await,
DirectoryOutput::DirectoryPartition(d) => d.create(txn, path, prefix, layer).await,
}
}
async fn open(
&self,
txn: &Transaction,
path: &[String],
layer: Option<&[u8]>,
) -> Result<DirectoryOutput, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => d.open(txn, path, layer).await,
DirectoryOutput::DirectoryPartition(d) => d.open(txn, path, layer).await,
}
}
async fn exists(&self, trx: &Transaction, path: &[String]) -> Result<bool, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => d.exists(trx, path).await,
DirectoryOutput::DirectoryPartition(d) => d.exists(trx, path).await,
}
}
async fn move_directory(
&self,
trx: &Transaction,
new_path: &[String],
) -> Result<DirectoryOutput, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => d.move_directory(trx, new_path).await,
DirectoryOutput::DirectoryPartition(d) => d.move_directory(trx, new_path).await,
}
}
async fn move_to(
&self,
trx: &Transaction,
old_path: &[String],
new_path: &[String],
) -> Result<DirectoryOutput, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => d.move_to(trx, old_path, new_path).await,
DirectoryOutput::DirectoryPartition(d) => d.move_to(trx, old_path, new_path).await,
}
}
async fn remove(&self, trx: &Transaction, path: &[String]) -> Result<bool, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => d.remove(trx, path).await,
DirectoryOutput::DirectoryPartition(d) => d.remove(trx, path).await,
}
}
async fn remove_if_exists(
&self,
trx: &Transaction,
path: &[String],
) -> Result<bool, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => d.remove_if_exists(trx, path).await,
DirectoryOutput::DirectoryPartition(d) => d.remove_if_exists(trx, path).await,
}
}
async fn list(
&self,
trx: &Transaction,
path: &[String],
) -> Result<Vec<String>, DirectoryError> {
match self {
DirectoryOutput::DirectorySubspace(d) => d.list(trx, path).await,
DirectoryOutput::DirectoryPartition(d) => d.list(trx, path).await,
}
}
}
pub(crate) fn strinc(key: Vec<u8>) -> Vec<u8> {
let mut key = key;
for i in (0..key.len()).rev() {
if key[i] != 0xff {
key[i] += 1;
break;
} else {
key.remove(i);
}
}
key
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_strinc() {
assert_eq!(strinc(Vec::from("a".as_bytes())), Vec::from("b".as_bytes()));
assert_eq!(strinc(Vec::from("y".as_bytes())), Vec::from("z".as_bytes()));
assert_eq!(
strinc(Vec::from("!".as_bytes())),
Vec::from("\"".as_bytes())
);
assert_eq!(strinc(Vec::from("*".as_bytes())), Vec::from("+".as_bytes()));
assert_eq!(
strinc(Vec::from("fdb".as_bytes())),
Vec::from("fdc".as_bytes())
);
assert_eq!(
strinc(Vec::from("foundation database 6".as_bytes())),
Vec::from("foundation database 7".as_bytes())
);
assert_eq!(strinc(vec![61u8, 62u8, 255u8]), vec![61u8, 63u8]);
assert_eq!(strinc(vec![253u8, 255u8]), vec![254u8]);
assert_eq!(strinc(vec![253u8, 255u8, 255u8]), vec![254u8]);
assert_eq!(strinc(vec![255u8, 255u8, 255u8]), Vec::<u8>::new());
}
}