use std::fmt;
use async_hash::{Digest, Hash, Output};
use async_trait::async_trait;
use futures::Stream;
use safecast::{as_type, AsType};
use tc_error::*;
use tc_transact::{Transaction, TxnId};
use tc_value::Value;
use tcgeneric::{
path_label, Class, Instance, NativeClass, PathLabel, PathSegment, TCPathBuf, ThreadSafe,
};
pub use b_tree::Schema;
pub use file::BTreeFile;
pub use schema::{BTreeSchema, Column};
pub use slice::BTreeSlice;
pub(crate) use stream::BTreeView;
pub use stream::Keys;
pub mod public;
mod file;
mod schema;
mod slice;
mod stream;
const PREFIX: PathLabel = path_label(&["state", "collection", "btree"]);
pub type Key = b_tree::Key<Value>;
pub type Node = b_tree::Node<Vec<Key>>;
pub type Range = b_tree::Range<Value>;
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum BTreeType {
File,
Slice,
}
impl Class for BTreeType {}
impl NativeClass for BTreeType {
fn from_path(path: &[PathSegment]) -> Option<Self> {
if &path[..] == &PREFIX[..] {
Some(Self::File)
} else {
None
}
}
fn path(&self) -> TCPathBuf {
PREFIX.into()
}
}
impl Default for BTreeType {
fn default() -> Self {
Self::File
}
}
impl<D: Digest> Hash<D> for BTreeType {
fn hash(self) -> Output<D> {
Hash::<D>::hash(&self.path())
}
}
impl fmt::Debug for BTreeType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::File => f.write_str("type BTree"),
Self::Slice => f.write_str("type BTreeSlice"),
}
}
}
#[async_trait]
pub trait BTreeInstance: Clone + Instance {
type Slice: BTreeInstance;
fn schema(&self) -> &BTreeSchema;
async fn count(&self, txn_id: TxnId) -> TCResult<u64>;
async fn is_empty(&self, txn_id: TxnId) -> TCResult<bool>;
async fn keys<'a>(self, txn_id: TxnId) -> TCResult<Keys<'a>>
where
Self: 'a;
fn slice(self, range: Range, reverse: bool) -> TCResult<Self::Slice>;
}
#[async_trait]
pub trait BTreeWrite: BTreeInstance {
async fn delete(&self, txn_id: TxnId, range: Range) -> TCResult<()>;
async fn insert(&self, txn_id: TxnId, key: Key) -> TCResult<()>;
async fn try_insert_from<S>(&self, txn_id: TxnId, keys: S) -> TCResult<()>
where
S: Stream<Item = TCResult<Key>> + Send + Unpin;
}
pub enum BTree<Txn, FE> {
File(BTreeFile<Txn, FE>),
Slice(BTreeSlice<Txn, FE>),
}
as_type!(BTree<Txn, FE>, File, BTreeFile<Txn, FE>);
as_type!(BTree<Txn, FE>, Slice, BTreeSlice<Txn, FE>);
impl<Txn, FE> Clone for BTree<Txn, FE> {
fn clone(&self) -> Self {
match self {
Self::File(file) => Self::File(file.clone()),
Self::Slice(slice) => Self::Slice(slice.clone()),
}
}
}
impl<Txn, FE> Instance for BTree<Txn, FE>
where
Txn: Transaction<FE>,
FE: Send + Sync,
{
type Class = BTreeType;
fn class(&self) -> Self::Class {
match self {
Self::File(file) => file.class(),
Self::Slice(slice) => slice.class(),
}
}
}
#[async_trait]
impl<Txn, FE> BTreeInstance for BTree<Txn, FE>
where
Txn: Transaction<FE>,
FE: AsType<Node> + ThreadSafe,
{
type Slice = Self;
fn schema(&self) -> &BTreeSchema {
match self {
Self::File(file) => BTreeInstance::schema(file),
Self::Slice(slice) => BTreeInstance::schema(slice),
}
}
fn slice(self, range: Range, reverse: bool) -> TCResult<Self> {
if range == Range::default() && !reverse {
return Ok(self);
}
match self {
Self::File(file) => file.slice(range, reverse).map(BTree::Slice),
Self::Slice(slice) => slice.slice(range, reverse).map(BTree::Slice),
}
}
async fn count(&self, txn_id: TxnId) -> TCResult<u64> {
match self {
Self::File(file) => file.count(txn_id).await,
Self::Slice(slice) => slice.count(txn_id).await,
}
}
async fn is_empty(&self, txn_id: TxnId) -> TCResult<bool> {
match self {
Self::File(file) => file.is_empty(txn_id).await,
Self::Slice(slice) => slice.is_empty(txn_id).await,
}
}
async fn keys<'a>(self, txn_id: TxnId) -> TCResult<Keys<'a>>
where
Self: 'a,
{
match self {
Self::File(file) => file.keys(txn_id).await,
Self::Slice(slice) => slice.keys(txn_id).await,
}
}
}
#[async_trait]
impl<Txn, FE> BTreeWrite for BTree<Txn, FE>
where
Txn: Transaction<FE>,
FE: AsType<Node> + ThreadSafe,
{
async fn delete(&self, txn_id: TxnId, range: Range) -> TCResult<()> {
match self {
Self::File(file) => file.delete(txn_id, range).await,
slice => Err(bad_request!(
"{:?} does not support write operations",
slice
)),
}
}
async fn insert(&self, txn_id: TxnId, key: Key) -> TCResult<()> {
match self {
Self::File(file) => file.insert(txn_id, key).await,
slice => Err(bad_request!(
"{:?} does not support write operations",
slice
)),
}
}
async fn try_insert_from<S>(&self, txn_id: TxnId, keys: S) -> TCResult<()>
where
S: Stream<Item = TCResult<Key>> + Send + Unpin,
{
match self {
Self::File(file) => file.try_insert_from(txn_id, keys).await,
slice => Err(bad_request!(
"{:?} does not support write operations",
slice
)),
}
}
}
impl<Txn, FE> fmt::Debug for BTree<Txn, FE> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a BTree")
}
}