use std::{
borrow::Cow,
collections::HashMap,
fmt::{Debug, Display},
marker::PhantomData,
ops::RangeBounds,
path::Path,
sync::Arc,
};
use crate::{
error::Error,
io::{File, ManagedFile},
roots::AnyTransactionTree,
transaction::TransactionManager,
tree::{
btree_entry::ScanArgs, state::AnyTreeState, Modification, PageHeader, PagedWriter, Reducer,
ScanEvaluation, State, TreeFile,
},
vault::AnyVault,
AbortError, ArcBytes, ChunkCache, Context, TransactionTree, Vault,
};
pub trait Root: Default + Debug + Send + Sync + Clone + 'static {
const HEADER: PageHeader;
type Index: Clone + Debug + 'static;
type ReducedIndex: Reducer<Self::Index> + Clone + Debug + 'static;
fn count(&self) -> u64;
fn tree<File: ManagedFile>(name: impl Into<Cow<'static, str>>) -> TreeRoot<Self, File> {
TreeRoot {
name: name.into(),
vault: None,
_phantom: PhantomData,
}
}
fn dirty(&self) -> bool;
fn initialized(&self) -> bool;
fn initialize_default(&mut self);
fn serialize(
&mut self,
paged_writer: &mut PagedWriter<'_>,
output: &mut Vec<u8>,
) -> Result<(), Error>;
fn deserialize(bytes: ArcBytes<'_>) -> Result<Self, Error>;
fn transaction_id(&self) -> u64;
fn modify<'a, 'w>(
&'a mut self,
modification: Modification<'_, ArcBytes<'static>>,
writer: &'a mut PagedWriter<'w>,
max_order: Option<usize>,
) -> Result<(), Error>;
fn get_multiple<'keys, KeyEvaluator, KeyReader, Keys>(
&self,
keys: &mut Keys,
key_evaluator: &mut KeyEvaluator,
key_reader: &mut KeyReader,
file: &mut dyn File,
vault: Option<&dyn AnyVault>,
cache: Option<&ChunkCache>,
) -> Result<(), Error>
where
KeyEvaluator: FnMut(&ArcBytes<'static>) -> ScanEvaluation,
KeyReader: FnMut(ArcBytes<'static>, ArcBytes<'static>) -> Result<(), Error>,
Keys: Iterator<Item = &'keys [u8]>;
fn scan<
'keys,
CallerError: Display + Debug,
NodeEvaluator,
KeyRangeBounds,
KeyEvaluator,
ScanDataCallback,
>(
&self,
range: &'keys KeyRangeBounds,
args: &mut ScanArgs<
Self::Index,
Self::ReducedIndex,
CallerError,
NodeEvaluator,
KeyEvaluator,
ScanDataCallback,
>,
file: &mut dyn File,
vault: Option<&dyn AnyVault>,
cache: Option<&ChunkCache>,
) -> Result<bool, AbortError<CallerError>>
where
NodeEvaluator: FnMut(&ArcBytes<'static>, &Self::ReducedIndex, usize) -> ScanEvaluation,
KeyEvaluator: FnMut(&ArcBytes<'static>, &Self::Index) -> ScanEvaluation,
KeyRangeBounds: RangeBounds<&'keys [u8]> + Debug + ?Sized,
ScanDataCallback: FnMut(
ArcBytes<'static>,
&Self::Index,
ArcBytes<'static>,
) -> Result<(), AbortError<CallerError>>;
fn copy_data_to(
&mut self,
include_nodes: bool,
file: &mut dyn File,
copied_chunks: &mut HashMap<u64, u64>,
writer: &mut PagedWriter<'_>,
vault: Option<&dyn AnyVault>,
) -> Result<(), Error>;
}
#[must_use]
pub struct TreeRoot<R: Root, File: ManagedFile> {
pub name: Cow<'static, str>,
pub vault: Option<Arc<dyn AnyVault>>,
_phantom: PhantomData<(R, File)>,
}
impl<R: Root, File: ManagedFile> TreeRoot<R, File> {
pub fn with_vault<V: Vault>(mut self, vault: V) -> Self {
self.vault = Some(Arc::new(vault));
self
}
}
impl<R: Root, File: ManagedFile> Clone for TreeRoot<R, File> {
fn clone(&self) -> Self {
Self {
name: self.name.clone(),
vault: self.vault.clone(),
_phantom: PhantomData,
}
}
}
pub trait AnyTreeRoot<File: ManagedFile> {
fn name(&self) -> &str;
fn default_state(&self) -> Box<dyn AnyTreeState>;
fn begin_transaction(
&self,
transaction_id: u64,
file_path: &Path,
state: &dyn AnyTreeState,
context: &Context<File::Manager>,
transactions: Option<&TransactionManager<File::Manager>>,
) -> Result<Box<dyn AnyTransactionTree<File>>, Error>;
}
impl<R: Root, File: ManagedFile> AnyTreeRoot<File> for TreeRoot<R, File> {
fn name(&self) -> &str {
&self.name
}
fn default_state(&self) -> Box<dyn AnyTreeState> {
Box::new(State::<R>::default())
}
fn begin_transaction(
&self,
transaction_id: u64,
file_path: &Path,
state: &dyn AnyTreeState,
context: &Context<File::Manager>,
transactions: Option<&TransactionManager<File::Manager>>,
) -> Result<Box<dyn AnyTransactionTree<File>>, Error> {
let context = self.vault.as_ref().map_or_else(
|| Cow::Borrowed(context),
|vault| Cow::Owned(context.clone().with_any_vault(vault.clone())),
);
let tree = TreeFile::write(
file_path,
state.as_any().downcast_ref::<State<R>>().unwrap().clone(),
&context,
transactions,
)?;
Ok(Box::new(TransactionTree {
transaction_id,
tree,
}))
}
}