use std::marker::PhantomData;
use crate::{BlockId, SequenceHash};
use kvbm_logical::blocks::{BlockMetadata, ImmutableBlock, WeakBlock};
#[derive(Debug, Clone, Copy)]
pub struct ExternalBlock<T: BlockMetadata> {
pub block_id: BlockId,
pub sequence_hash: SequenceHash,
_marker: PhantomData<T>,
}
impl<T: BlockMetadata> ExternalBlock<T> {
pub fn new(block_id: BlockId, sequence_hash: SequenceHash) -> Self {
Self {
block_id,
sequence_hash,
_marker: PhantomData,
}
}
}
#[derive(Debug)]
pub enum SourceBlock<T: BlockMetadata> {
External(ExternalBlock<T>),
Strong(ImmutableBlock<T>),
Weak(WeakBlock<T>),
}
impl<T: BlockMetadata> SourceBlock<T> {
pub fn block_id(&self) -> Option<BlockId> {
match self {
SourceBlock::External(ext) => Some(ext.block_id),
SourceBlock::Strong(block) => Some(block.block_id()),
SourceBlock::Weak(_) => None,
}
}
pub fn sequence_hash(&self) -> Option<SequenceHash> {
match self {
SourceBlock::External(ext) => Some(ext.sequence_hash),
SourceBlock::Strong(block) => Some(block.sequence_hash()),
SourceBlock::Weak(weak) => Some(weak.sequence_hash()),
}
}
pub fn is_external(&self) -> bool {
matches!(self, SourceBlock::External(_))
}
pub fn is_strong(&self) -> bool {
matches!(self, SourceBlock::Strong(_))
}
pub fn is_weak(&self) -> bool {
matches!(self, SourceBlock::Weak(_))
}
}
impl<T: BlockMetadata> From<ExternalBlock<T>> for SourceBlock<T> {
fn from(ext: ExternalBlock<T>) -> Self {
SourceBlock::External(ext)
}
}
impl<T: BlockMetadata> From<ImmutableBlock<T>> for SourceBlock<T> {
fn from(block: ImmutableBlock<T>) -> Self {
SourceBlock::Strong(block)
}
}
impl<T: BlockMetadata> From<WeakBlock<T>> for SourceBlock<T> {
fn from(block: WeakBlock<T>) -> Self {
SourceBlock::Weak(block)
}
}
#[derive(Debug)]
pub enum SourceBlocks<T: BlockMetadata> {
External(Vec<ExternalBlock<T>>),
Strong(Vec<ImmutableBlock<T>>),
Weak(Vec<WeakBlock<T>>),
}
impl<T: BlockMetadata> SourceBlocks<T> {
pub fn empty_external() -> Self {
SourceBlocks::External(Vec::new())
}
pub fn empty_strong() -> Self {
SourceBlocks::Strong(Vec::new())
}
pub fn empty_weak() -> Self {
SourceBlocks::Weak(Vec::new())
}
pub fn len(&self) -> usize {
match self {
SourceBlocks::External(blocks) => blocks.len(),
SourceBlocks::Strong(blocks) => blocks.len(),
SourceBlocks::Weak(blocks) => blocks.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn external_blocks(&self) -> Option<&[ExternalBlock<T>]> {
match self {
SourceBlocks::External(blocks) => Some(blocks),
_ => None,
}
}
pub fn strong_blocks(&self) -> Option<&[ImmutableBlock<T>]> {
match self {
SourceBlocks::Strong(blocks) => Some(blocks),
_ => None,
}
}
pub fn weak_blocks(&self) -> Option<&[WeakBlock<T>]> {
match self {
SourceBlocks::Weak(blocks) => Some(blocks),
_ => None,
}
}
pub fn is_external(&self) -> bool {
matches!(self, SourceBlocks::External(_))
}
pub fn is_strong(&self) -> bool {
matches!(self, SourceBlocks::Strong(_))
}
pub fn is_weak(&self) -> bool {
matches!(self, SourceBlocks::Weak(_))
}
}
impl<T: BlockMetadata> From<Vec<ExternalBlock<T>>> for SourceBlocks<T> {
fn from(blocks: Vec<ExternalBlock<T>>) -> Self {
SourceBlocks::External(blocks)
}
}
impl<T: BlockMetadata> From<Vec<ImmutableBlock<T>>> for SourceBlocks<T> {
fn from(blocks: Vec<ImmutableBlock<T>>) -> Self {
SourceBlocks::Strong(blocks)
}
}
impl<T: BlockMetadata> From<Vec<WeakBlock<T>>> for SourceBlocks<T> {
fn from(blocks: Vec<WeakBlock<T>>) -> Self {
SourceBlocks::Weak(blocks)
}
}
impl<T: BlockMetadata> From<SourceBlock<T>> for SourceBlocks<T> {
fn from(block: SourceBlock<T>) -> Self {
match block {
SourceBlock::External(ext) => SourceBlocks::External(vec![ext]),
SourceBlock::Strong(b) => SourceBlocks::Strong(vec![b]),
SourceBlock::Weak(b) => SourceBlocks::Weak(vec![b]),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use kvbm_common::tokens::TokenBlockSequence;
use kvbm_logical::KvbmSequenceHashProvider;
fn test_seq_hash(position: usize) -> SequenceHash {
let tokens_per_block = 4;
let total_tokens = (position + 1) * tokens_per_block;
let tokens: Vec<u32> = (0..total_tokens as u32).collect();
let seq = TokenBlockSequence::from_slice(&tokens, tokens_per_block as u32, Some(1337));
seq.blocks()[position].kvbm_sequence_hash()
}
#[test]
fn test_external_block_creation() {
let hash = test_seq_hash(0);
let ext: ExternalBlock<()> = ExternalBlock::new(42, hash);
assert_eq!(ext.block_id, 42);
assert_eq!(ext.sequence_hash, hash);
}
#[test]
fn test_source_blocks_from_vec_external() {
let ext1: ExternalBlock<()> = ExternalBlock::new(1, test_seq_hash(0));
let ext2: ExternalBlock<()> = ExternalBlock::new(2, test_seq_hash(1));
let ext3: ExternalBlock<()> = ExternalBlock::new(3, test_seq_hash(2));
let blocks: SourceBlocks<()> = vec![ext1, ext2, ext3].into();
assert!(blocks.is_external());
assert_eq!(blocks.len(), 3);
let external = blocks.external_blocks().unwrap();
assert_eq!(external[0].block_id, 1);
assert_eq!(external[1].block_id, 2);
assert_eq!(external[2].block_id, 3);
}
#[test]
fn test_source_blocks_empty() {
let blocks: SourceBlocks<()> = SourceBlocks::empty_external();
assert!(blocks.is_empty());
assert!(blocks.is_external());
}
#[test]
fn test_source_block_accessors() {
let hash = test_seq_hash(5);
let ext: ExternalBlock<()> = ExternalBlock::new(42, hash);
let block: SourceBlock<()> = ext.into();
assert_eq!(block.block_id(), Some(42));
assert_eq!(block.sequence_hash(), Some(hash));
assert!(block.is_external());
}
}