use bytes::Bytes;
use crate::bmt::DEFAULT_BODY_SIZE;
use crate::error::Result;
use super::chunk_type::ChunkType;
use super::content::ContentChunk;
use super::single_owner::SingleOwnerChunk;
use super::traits::{Chunk, ChunkAddress};
use super::type_id::ChunkTypeId;
#[derive(Debug, Clone)]
pub enum AnyChunk<const BODY_SIZE: usize = DEFAULT_BODY_SIZE> {
Content(ContentChunk<BODY_SIZE>),
SingleOwner(SingleOwnerChunk<BODY_SIZE>),
Custom {
type_id: ChunkTypeId,
address: ChunkAddress,
data: Bytes,
},
}
impl<const BODY_SIZE: usize> AnyChunk<BODY_SIZE> {
pub fn address(&self) -> &ChunkAddress {
match self {
Self::Content(c) => c.address(),
Self::SingleOwner(c) => c.address(),
Self::Custom { address, .. } => address,
}
}
pub fn data(&self) -> &Bytes {
match self {
Self::Content(c) => c.data(),
Self::SingleOwner(c) => c.data(),
Self::Custom { data, .. } => data,
}
}
pub const fn type_id(&self) -> ChunkTypeId {
match self {
Self::Content(_) => ChunkTypeId::CONTENT,
Self::SingleOwner(_) => ChunkTypeId::SINGLE_OWNER,
Self::Custom { type_id, .. } => *type_id,
}
}
pub fn size(&self) -> usize {
match self {
Self::Content(c) => c.size(),
Self::SingleOwner(c) => c.size(),
Self::Custom { data, .. } => data.len(),
}
}
pub fn span(&self) -> u64 {
match self {
Self::Content(c) => super::traits::BmtChunk::span(c),
Self::SingleOwner(c) => super::traits::BmtChunk::span(c),
Self::Custom { .. } => 0, }
}
pub fn verify(&self, expected: &ChunkAddress) -> Result<()> {
match self {
Self::Content(c) => c.verify(expected),
Self::SingleOwner(c) => c.verify(expected),
Self::Custom { address, .. } => {
if address != expected {
return Err(
super::error::ChunkError::verification_failed(*expected, *address).into(),
);
}
Ok(())
}
}
}
pub fn into_bytes(self) -> Bytes {
match self {
Self::Content(c) => c.into(),
Self::SingleOwner(c) => c.into(),
Self::Custom { data, .. } => data,
}
}
pub fn is<T: ChunkType>(&self) -> bool {
self.type_id() == T::TYPE_ID
}
pub const fn is_content(&self) -> bool {
matches!(self, Self::Content(_))
}
pub const fn is_single_owner(&self) -> bool {
matches!(self, Self::SingleOwner(_))
}
pub const fn is_custom(&self) -> bool {
matches!(self, Self::Custom { .. })
}
pub const fn as_content(&self) -> Option<&ContentChunk<BODY_SIZE>> {
match self {
Self::Content(c) => Some(c),
_ => None,
}
}
pub const fn as_single_owner(&self) -> Option<&SingleOwnerChunk<BODY_SIZE>> {
match self {
Self::SingleOwner(c) => Some(c),
_ => None,
}
}
pub fn into_content(self) -> Option<ContentChunk<BODY_SIZE>> {
match self {
Self::Content(c) => Some(c),
_ => None,
}
}
pub fn into_single_owner(self) -> Option<SingleOwnerChunk<BODY_SIZE>> {
match self {
Self::SingleOwner(c) => Some(c),
_ => None,
}
}
}
impl<const BODY_SIZE: usize> From<ContentChunk<BODY_SIZE>> for AnyChunk<BODY_SIZE> {
fn from(chunk: ContentChunk<BODY_SIZE>) -> Self {
Self::Content(chunk)
}
}
impl<const BODY_SIZE: usize> From<SingleOwnerChunk<BODY_SIZE>> for AnyChunk<BODY_SIZE> {
fn from(chunk: SingleOwnerChunk<BODY_SIZE>) -> Self {
Self::SingleOwner(chunk)
}
}
impl<const BODY_SIZE: usize> PartialEq for AnyChunk<BODY_SIZE> {
fn eq(&self, other: &Self) -> bool {
self.address() == other.address()
}
}
impl<const BODY_SIZE: usize> Eq for AnyChunk<BODY_SIZE> {}
#[cfg(test)]
mod tests {
use super::super::traits::Chunk;
use super::*;
type DefaultContentChunk = ContentChunk<DEFAULT_BODY_SIZE>;
type DefaultSingleOwnerChunk = SingleOwnerChunk<DEFAULT_BODY_SIZE>;
type DefaultAnyChunk = AnyChunk<DEFAULT_BODY_SIZE>;
#[test]
fn test_content_chunk_conversion() {
let content = DefaultContentChunk::new(&b"hello world"[..]).unwrap();
let address = *content.address();
let any: DefaultAnyChunk = content.into();
assert!(any.is_content());
assert!(!any.is_single_owner());
assert!(!any.is_custom());
assert_eq!(any.type_id(), ChunkTypeId::CONTENT);
assert_eq!(*any.address(), address);
}
#[test]
fn test_as_content() {
let content = DefaultContentChunk::new(&b"test data"[..]).unwrap();
let expected_addr = *content.address();
let any: DefaultAnyChunk = content.into();
let recovered = any.as_content().unwrap();
assert_eq!(*recovered.address(), expected_addr);
}
#[test]
fn test_into_content() {
let content = DefaultContentChunk::new(&b"test data"[..]).unwrap();
let expected_addr = *content.address();
let any: DefaultAnyChunk = content.into();
let recovered = any.into_content().unwrap();
assert_eq!(*recovered.address(), expected_addr);
}
#[test]
fn test_is_methods() {
let content: DefaultAnyChunk = DefaultContentChunk::new(&b"test"[..]).unwrap().into();
assert!(content.is::<DefaultContentChunk>());
assert!(!content.is::<DefaultSingleOwnerChunk>());
}
#[test]
fn test_clone() {
let content = DefaultContentChunk::new(&b"test"[..]).unwrap();
let any: DefaultAnyChunk = content.into();
let cloned = any.clone();
assert_eq!(any.address(), cloned.address());
assert_eq!(any.type_id(), cloned.type_id());
}
}