use bytes::{Buf, BufMut, Bytes, BytesMut};
use crate::primitives::varint::{get_uvarint, put_uvarint, uvarint_len};
pub const FRAME_VERSION: u32 = 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ValueHeader {
pub frame_version: u32,
pub api_key: u32,
pub api_version: u32,
}
#[derive(Debug, thiserror::Error)]
pub enum EnvelopeError {
#[error("truncated metadata record value envelope")]
Truncated,
}
#[must_use]
pub fn encode_value(api_key: u32, api_version: u32, body: &[u8]) -> Bytes {
let mut out = BytesMut::with_capacity(
uvarint_len(FRAME_VERSION) + uvarint_len(api_key) + uvarint_len(api_version) + body.len(),
);
put_uvarint(&mut out, FRAME_VERSION);
put_uvarint(&mut out, api_key);
put_uvarint(&mut out, api_version);
out.put_slice(body);
out.freeze()
}
pub fn decode_value_header<B: Buf>(buf: &mut B) -> Result<ValueHeader, EnvelopeError> {
let frame_version = get_uvarint(buf).map_err(|_| EnvelopeError::Truncated)?;
let api_key = get_uvarint(buf).map_err(|_| EnvelopeError::Truncated)?;
let api_version = get_uvarint(buf).map_err(|_| EnvelopeError::Truncated)?;
Ok(ValueHeader {
frame_version,
api_key,
api_version,
})
}
#[cfg(test)]
mod tests {
use super::*;
use assert2::assert;
#[test]
fn frame_zero_apikey_apiversion_roundtrip() {
let body: &[u8] = &[0x01, 0x02];
let value = encode_value(12, 0, body);
assert!(value.len() == 3 + body.len());
let mut cur: &[u8] = &value;
let hdr = decode_value_header(&mut cur).expect("decode header");
assert!(hdr.frame_version == 1);
assert!(hdr.api_key == 12);
assert!(hdr.api_version == 0);
assert!(cur == body);
}
#[test]
fn truncated_value_errors() {
let mut cur: &[u8] = &[]; assert!(decode_value_header(&mut cur).is_err());
}
}