pub const PROTOCOL_VERSION: u16 = 4;
pub const TRANSFER_FILE: u8 = 0x00;
pub const TRANSFER_DIR: u8 = 0x01;
pub const FRAME_RAW: u8 = 0x00;
pub const FRAME_ZSTD: u8 = 0x01;
pub const MAX_FILENAME_BYTES: usize = 4096;
pub const MAX_METADATA_ENCRYPTED: usize = 4 + MAX_FILENAME_BYTES + 8 + 1 + 12 + 16 + 16;
pub const CHUNK_SIZE: usize = 1024 * 1024;
#[derive(Debug, Clone)]
pub struct Metadata {
pub filename: String,
pub total_size: u64,
pub transfer_type: u8,
}
impl Metadata {
pub fn encode(&self) -> Vec<u8> {
let name_bytes = self.filename.as_bytes();
let mut buf = Vec::with_capacity(2 + name_bytes.len() + 8 + 1);
buf.extend_from_slice(&(name_bytes.len() as u16).to_be_bytes());
buf.extend_from_slice(name_bytes);
buf.extend_from_slice(&self.total_size.to_be_bytes());
buf.push(self.transfer_type);
buf
}
pub fn decode(raw: &[u8]) -> Result<Self, crate::EngineError> {
if raw.len() < 11 {
return Err(crate::EngineError::InvalidFrame(
"metadata too short".into(),
));
}
let name_len = u16::from_be_bytes([raw[0], raw[1]]) as usize;
if name_len == 0 || name_len > MAX_FILENAME_BYTES {
return Err(crate::EngineError::InvalidFrame(format!(
"invalid filename length: {name_len}"
)));
}
if raw.len() < 2 + name_len + 8 + 1 {
return Err(crate::EngineError::InvalidFrame(
"metadata truncated".into(),
));
}
let filename = std::str::from_utf8(&raw[2..2 + name_len])
.map_err(|_| crate::EngineError::InvalidFrame("filename not UTF-8".into()))?
.to_owned();
let total_size = u64::from_be_bytes(
raw[2 + name_len..2 + name_len + 8]
.try_into()
.expect("slice len == 8"),
);
let transfer_type = raw[2 + name_len + 8];
Ok(Self {
filename,
total_size,
transfer_type,
})
}
}