use bytes::{BufMut, Bytes, BytesMut};
const T_NULL: u32 = 0x0000000A;
const T_UINT32: u32 = 0x00000003;
const T_INT64: u32 = 0x00000006;
const T_DOUBLE: u32 = 0x00000009;
const T_BYTEARRAY: u32 = 0x00000002;
#[derive(Debug, Clone)]
pub enum PrimArg {
Int32(i32),
Int64(i64),
Double(f64),
Bytes(Bytes),
}
pub fn encode_primitive_dict(args: &[PrimArg]) -> Bytes {
let mut out = BytesMut::new();
for arg in args {
out.put_u32_le(T_NULL);
match arg {
PrimArg::Int32(v) => {
out.put_u32_le(T_UINT32);
out.put_u32_le(*v as u32);
}
PrimArg::Int64(v) => {
out.put_u32_le(T_INT64);
out.put_i64_le(*v);
}
PrimArg::Double(v) => {
out.put_u32_le(T_DOUBLE);
out.put_f64_le(*v);
}
PrimArg::Bytes(b) => {
out.put_u32_le(T_BYTEARRAY);
out.put_u32_le(b.len() as u32);
out.put_slice(b);
}
}
}
out.freeze()
}
pub fn archived_object(data: impl Into<Bytes>) -> PrimArg {
PrimArg::Bytes(data.into())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_args() {
let b = encode_primitive_dict(&[]);
assert!(b.is_empty());
}
#[test]
fn test_int32_arg() {
let b = encode_primitive_dict(&[PrimArg::Int32(42)]);
assert_eq!(b.len(), 12);
assert_eq!(u32::from_le_bytes(b[0..4].try_into().unwrap()), T_NULL);
assert_eq!(u32::from_le_bytes(b[4..8].try_into().unwrap()), T_UINT32);
assert_eq!(u32::from_le_bytes(b[8..12].try_into().unwrap()), 42);
}
#[test]
fn test_bytes_arg() {
let payload = b"hello";
let b = encode_primitive_dict(&[PrimArg::Bytes(Bytes::from_static(payload))]);
assert_eq!(b.len(), 17);
}
#[test]
fn test_int64_arg_omits_length_field() {
let b = encode_primitive_dict(&[PrimArg::Int64(36)]);
assert_eq!(b.len(), 16);
assert_eq!(u32::from_le_bytes(b[0..4].try_into().unwrap()), T_NULL);
assert_eq!(u32::from_le_bytes(b[4..8].try_into().unwrap()), T_INT64);
assert_eq!(i64::from_le_bytes(b[8..16].try_into().unwrap()), 36);
}
}