use crate::codec::Codec;
use crate::error::{DbError, DbResult};
use super::Wire;
#[derive(Clone, Copy, Default)]
pub(crate) struct CodecWire<C>(pub C);
impl<V, C: Codec<V> + Send + Sync + 'static> Wire<V> for CodecWire<C> {
const PASSTHROUGH: bool = true;
fn encode_to(&self, value: &V, out: &mut Vec<u8>) -> DbResult<()> {
self.0.encode_to(value, out)
}
fn encode_append(&self, value: &V, out: &mut Vec<u8>) -> DbResult<()> {
self.0.encode_append(value, out)
}
fn decode(&self, bytes: &[u8]) -> DbResult<V> {
self.0.decode_from(bytes)
}
fn size(&self, value: &V) -> DbResult<u32> {
Ok(self.0.size(value) as u32)
}
fn validate_stored(&self, raw: &[u8]) -> DbResult<()> {
self.0.decode_from(raw).map(|_| ())
}
}
pub(crate) struct ZeroWire<const V: usize>;
impl<T, const V: usize> Wire<T> for ZeroWire<V>
where
T: Copy
+ zerocopy::IntoBytes
+ zerocopy::FromBytes
+ zerocopy::Immutable
+ Send
+ Sync
+ 'static,
{
fn encode_to(&self, value: &T, out: &mut Vec<u8>) -> DbResult<()> {
out.clear();
out.extend_from_slice(crate::zero_tree::to_bytes::<V, T>(value).as_slice());
Ok(())
}
fn encode_append(&self, value: &T, out: &mut Vec<u8>) -> DbResult<()> {
out.extend_from_slice(crate::zero_tree::to_bytes::<V, T>(value).as_slice());
Ok(())
}
fn decode(&self, bytes: &[u8]) -> DbResult<T> {
if bytes.len() != V {
return Err(DbError::Client("invalid value length for zero collection"));
}
let arr: &[u8; V] = bytes
.try_into()
.map_err(|_| DbError::Client("invalid value length for zero collection"))?;
Ok(crate::zero_tree::from_value_bytes(arr))
}
fn size(&self, _value: &T) -> DbResult<u32> {
Ok(V as u32)
}
fn decode_batch_item(&self, bytes: &[u8]) -> DbResult<T> {
if bytes.len() != V {
return Err(DbError::Client("invalid value length in batch"));
}
self.decode(bytes)
.map_err(|_| DbError::Client("invalid value length in batch"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::DbError;
#[cfg(feature = "rapira-codec")]
mod rapira_codec {
use super::*;
use crate::codec::RapiraCodec;
#[test]
fn codec_wire_round_trips_and_is_passthrough() {
let w = CodecWire(RapiraCodec);
assert!(<CodecWire<RapiraCodec> as Wire<u64>>::PASSTHROUGH);
let mut out = Vec::new();
Wire::<u64>::encode_to(&w, &7u64, &mut out).unwrap();
assert_eq!(Wire::<u64>::decode(&w, &out).unwrap(), 7u64);
assert_eq!(Wire::<u64>::size(&w, &7u64).unwrap(), out.len() as u32);
let err = Wire::<u64>::decode_batch_item(&w, &[0xff, 0xff]).unwrap_err();
assert!(matches!(
err,
DbError::Client("invalid value bytes in batch")
));
}
#[test]
fn codec_wire_encode_append_preserves_existing_bytes() {
let w = CodecWire(RapiraCodec);
let mut out = vec![0xAA];
Wire::<u64>::encode_append(&w, &7u64, &mut out).expect("append");
assert_eq!(out[0], 0xAA);
assert_eq!(Wire::<u64>::decode(&w, &out[1..]).expect("decode"), 7);
}
#[test]
fn validate_stored_default_errors_and_codec_checks_decode() {
let w = CodecWire(RapiraCodec);
let bytes = Wire::<u64>::encode(&w, &1u64).unwrap();
Wire::<u64>::validate_stored(&w, &bytes).unwrap();
assert!(Wire::<u64>::validate_stored(&w, &[0xff; 3]).is_err());
}
}
#[test]
fn zero_wire_rejects_bad_length_with_upsert_message() {
let w = ZeroWire::<8>;
assert!(!<ZeroWire<8> as Wire<u64>>::PASSTHROUGH);
let err = Wire::<u64>::decode(&w, &[1, 2, 3]).unwrap_err();
assert!(matches!(
err,
DbError::Client("invalid value length for zero collection")
));
let err = Wire::<u64>::decode_batch_item(&w, &[1, 2, 3]).unwrap_err();
assert!(matches!(
err,
DbError::Client("invalid value length in batch")
));
}
#[test]
fn zero_wire_encode_append_preserves_existing_bytes() {
let w = ZeroWire::<8>;
let mut out = vec![0xAA];
Wire::<u64>::encode_append(&w, &7u64, &mut out).expect("append");
assert_eq!(out[0], 0xAA);
assert_eq!(Wire::<u64>::decode(&w, &out[1..]).expect("decode"), 7);
}
}