use crate::bundle::{
Bundle, FileDescriptor, Version, FILE_DESCRIPTORS_SIZE_BYTES_LENGTH, HEADER_MAGIC_BYTES,
VERSION_BYTES_LENGTH,
};
use bincode::{config, decode_from_slice};
use std::io::{Cursor, Read};
pub fn decode(buf: impl AsRef<[u8]>) -> crate::Result<Bundle> {
Decoder::new(&buf).decode()
}
struct Decoder<T> {
c: Cursor<T>,
}
impl<T> Decoder<T> {
fn new(buf: T) -> Self {
Self {
c: Cursor::new(buf),
}
}
}
impl<T: AsRef<[u8]>> Decoder<T> {
fn decode(&mut self) -> crate::Result<Bundle> {
self.read_magic_bytes()?;
let version = self.read_version()?;
let descriptors = self.read_file_descriptors()?;
let mut data = Vec::new();
self.c.read_to_end(&mut data)?;
let bundle = Bundle {
version,
descriptors,
data,
};
Ok(bundle)
}
fn read_magic_bytes(&mut self) -> crate::Result<()> {
let mut buf = [0; HEADER_MAGIC_BYTES.len()];
self.c.read_exact(&mut buf)?;
if buf != HEADER_MAGIC_BYTES {
return Err(crate::Error::InvalidMagic);
}
Ok(())
}
fn read_version(&mut self) -> crate::Result<Version> {
let mut buf = [0; VERSION_BYTES_LENGTH];
self.c.read_exact(&mut buf)?;
if &buf == Version::Version1.bytes() {
return Ok(Version::Version1);
}
Err(crate::Error::InvalidVersion)
}
fn read_file_descriptors(&mut self) -> crate::Result<Vec<FileDescriptor>> {
let mut size_buf = [0; FILE_DESCRIPTORS_SIZE_BYTES_LENGTH];
self.c.read_exact(&mut size_buf)?;
let size = u32::from_be_bytes(AsRef::<[u8]>::as_ref(&size_buf).try_into().unwrap());
let mut descriptors_buf = vec![0; size as usize];
self.c.read_exact(&mut descriptors_buf)?;
let config = config::standard().with_big_endian();
let (file_descriptors, _): (Vec<FileDescriptor>, _) =
decode_from_slice(&descriptors_buf, config)?;
Ok(file_descriptors)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::encoder::encode_bytes;
use std::path::Path;
#[test]
fn encode_and_decode() {
let path = Path::new("index.js");
let file = r#"const a = 10;"#;
let bundle = Bundle::builder().add_file(path, file.as_bytes()).build();
let encoded = encode_bytes(&bundle).unwrap();
let decoded = decode(encoded).unwrap();
assert_eq!(bundle, decoded);
}
#[test]
fn invalid_magic() {
assert!(matches!(
decode(vec![0, 0, 0, 0, 0, 0, 0, 0]).unwrap_err(),
crate::Error::InvalidMagic,
));
}
}