use std::str::Utf8Error;
use crate::core::{
lsn::InvalidLSN, page::PageSizeErr, pageidx::ConvertToPageIdxErr, zerocopy_ext::ZerocopyErr,
};
use culprit::Result;
use fjall::Slice;
use crate::core::gid::GidParseErr;
#[derive(Debug, thiserror::Error)]
pub enum DecodeErr {
#[error("Invalid LSN: {0}")]
InvalidLSN(#[from] InvalidLSN),
#[error("Zerocopy error: {0}")]
Zerocopy(#[from] ZerocopyErr),
#[error("Invalid page index: {0}")]
InvalidPageIdx(#[from] ConvertToPageIdxErr),
#[error(transparent)]
InvalidUtf8(#[from] Utf8Error),
#[error("Invalid ID: {0}")]
GidParseErr(#[from] GidParseErr),
#[error("Bilrost error: {0}")]
BilrostErr(#[from] bilrost::DecodeError),
#[error(transparent)]
PageSizeErr(#[from] PageSizeErr),
}
pub trait FjallReprRef {
fn as_slice(&self) -> impl AsRef<[u8]>;
#[inline]
fn into_slice(self) -> Slice
where
Self: Sized,
{
Slice::new(self.as_slice().as_ref())
}
}
pub trait FjallRepr: FjallReprRef + Clone {
fn try_from_slice(slice: Slice) -> Result<Self, DecodeErr>;
}
impl FjallReprRef for str {
fn as_slice(&self) -> impl AsRef<[u8]> {
self
}
}
#[macro_export]
macro_rules! proxy_to_fjall_repr {
(
encode ($ty:ty) using proxy ($proxy:ty)
into_proxy($me:ident) $into_proxy:block
from_proxy($iproxy:ident) $from_proxy:block
) => {
impl FjallReprRef for $ty {
#[inline]
fn as_slice(&self) -> impl AsRef<[u8]> {
let $me = self.clone();
$into_proxy
}
#[inline]
fn into_slice(self) -> Slice
where
Self: Sized,
{
let $me = self;
Slice::new($into_proxy.as_ref())
}
}
impl FjallRepr for $ty {
#[inline]
fn try_from_slice(
slice: Slice,
) -> Result<Self, $crate::local::fjall_storage::fjall_repr::DecodeErr> {
let $iproxy: &$proxy =
<$proxy>::try_ref_from_unaligned_bytes(&slice).or_into_ctx()?;
$from_proxy
}
}
};
}
#[cfg(test)]
pub mod testutil {
use super::*;
use std::fmt::Debug;
#[track_caller]
pub fn test_roundtrip<T>(value: T)
where
T: FjallRepr + PartialEq + Clone + Debug,
{
let slice = value.clone().into_slice();
let decoded = T::try_from_slice(slice).expect("Failed to decode");
assert_eq!(value, decoded, "Roundtrip failed");
}
#[track_caller]
pub fn test_invalid<T: FjallRepr>(slice: &[u8]) {
assert!(
T::try_from_slice(Slice::from(slice)).is_err(),
"Expected error for invalid slice"
);
}
#[track_caller]
pub fn test_empty_default<T: FjallRepr + Default + Debug + PartialEq>() {
assert_eq!(
T::try_from_slice(Slice::new(b"")).expect("failed to decode"),
T::default(),
"Expected empty slice to decode to default value"
);
}
#[track_caller]
pub fn test_serialized_order<T>(values: &[T])
where
T: FjallRepr + PartialEq + Clone + Debug,
{
let mut slices: Vec<Slice> = values.iter().cloned().map(|v| v.into_slice()).collect();
slices.sort();
for (i, slice) in slices.into_iter().enumerate() {
let decoded = T::try_from_slice(slice).expect("Failed to decode");
assert_eq!(decoded, values[i], "Order mismatch at index {i}");
}
}
}