use std::io::Result;
use crate::{
box_type::{DESCRIPTION_BOX_TYPE, SUPER_BOX_TYPE},
builder::{
to_box::{jumbf_size, write_jumbf},
ToBox, WriteAndSeek,
},
BoxType,
};
pub struct SuperBoxBuilder<'a> {
desc: DescriptionBoxBuilder,
child_boxes: Vec<OwnedOrBorrowedBox<'a>>,
}
impl<'a> SuperBoxBuilder<'a> {
pub fn new(uuid: &[u8; 16]) -> Self {
Self {
desc: DescriptionBoxBuilder::new(uuid),
child_boxes: vec![],
}
}
pub fn set_label<S: AsRef<str>>(mut self, label: S) -> Self {
self.desc.label = Some(label.as_ref().to_owned());
self.desc.requestable = true;
self
}
pub fn set_non_requestable_label<S: AsRef<str>>(mut self, label: S) -> Self {
self.desc.label = Some(label.as_ref().to_owned());
self.desc.requestable = false;
self
}
pub fn set_id(mut self, id: u32) -> Self {
self.desc.id = Some(id);
self
}
pub fn set_sha256_hash(mut self, hash: &[u8; 32]) -> Self {
self.desc.hash = Some(*hash);
self
}
pub fn set_private_box(mut self, private: impl ToBox + 'static) -> Self {
self.desc.private = Some(Box::new(private));
self
}
pub fn add_child_box(mut self, boxx: impl ToBox + 'static) -> Self {
self.child_boxes
.push(OwnedOrBorrowedBox::OwnedBox(Box::new(boxx)));
self
}
pub fn add_borrowed_child_box<B: ToBox>(mut self, boxx: &'a B) -> Self {
self.child_boxes.push(OwnedOrBorrowedBox::BorrowedBox(boxx));
self
}
pub fn write_jumbf(&self, to_stream: &mut dyn WriteAndSeek) -> Result<()> {
write_jumbf(self, to_stream)
}
}
impl<'a> ToBox for SuperBoxBuilder<'a> {
fn box_type(&self) -> BoxType {
SUPER_BOX_TYPE
}
fn payload_size(&self) -> Result<usize> {
let mut size: usize = jumbf_size(&self.desc)?;
for child in &self.child_boxes {
size += jumbf_size(child.as_ref())?;
}
Ok(size)
}
fn write_payload(&self, to_stream: &mut dyn WriteAndSeek) -> Result<()> {
write_jumbf(&self.desc, to_stream)?;
for child in &self.child_boxes {
write_jumbf(child.as_ref(), to_stream)?;
}
Ok(())
}
}
struct DescriptionBoxBuilder {
uuid: [u8; 16],
label: Option<String>,
requestable: bool,
id: Option<u32>,
hash: Option<[u8; 32]>,
private: Option<Box<dyn ToBox>>,
}
impl DescriptionBoxBuilder {
fn new(uuid: &[u8; 16]) -> Self {
Self {
uuid: *uuid,
label: None,
requestable: false,
id: None,
hash: None,
private: None,
}
}
}
impl ToBox for DescriptionBoxBuilder {
fn box_type(&self) -> BoxType {
DESCRIPTION_BOX_TYPE
}
fn write_payload(&self, to_stream: &mut dyn WriteAndSeek) -> Result<()> {
use crate::toggles;
to_stream.write_all(&self.uuid)?;
let mut toggles = 0u8;
if self.requestable {
toggles |= toggles::REQUESTABLE;
}
if self.label.is_some() {
toggles |= toggles::HAS_LABEL;
}
if self.id.is_some() {
toggles |= toggles::HAS_ID;
}
if self.hash.is_some() {
toggles |= toggles::HAS_HASH;
}
if self.private.is_some() {
toggles |= toggles::HAS_PRIVATE_BOX;
}
let toggles_slice = [toggles];
to_stream.write_all(&toggles_slice)?;
if let Some(label) = self.label.as_ref() {
to_stream.write_all(label.as_bytes())?;
to_stream.write_all(&[0u8])?;
}
if let Some(id) = self.id {
write_be_u32(to_stream, id)?;
}
if let Some(hash) = self.hash {
to_stream.write_all(&hash)?;
}
if let Some(private) = self.private.as_ref() {
write_jumbf(private.as_ref(), to_stream)?;
}
Ok(())
}
}
enum OwnedOrBorrowedBox<'a> {
OwnedBox(Box<dyn ToBox>),
BorrowedBox(&'a dyn ToBox),
}
impl<'a> OwnedOrBorrowedBox<'a> {
fn as_ref(&self) -> &dyn ToBox {
match self {
OwnedOrBorrowedBox::OwnedBox(boxx) => boxx.as_ref(),
OwnedOrBorrowedBox::BorrowedBox(boxx) => *boxx,
}
}
}
fn write_be_u32(to_stream: &mut dyn WriteAndSeek, v: u32) -> Result<()> {
let v_slice: [u8; 4] = [(v >> 24) as u8, (v >> 16) as u8, (v >> 8) as u8, v as u8];
to_stream.write_all(&v_slice)
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)]
#![allow(clippy::panic)]
#![allow(clippy::unwrap_used)]
use std::io::Cursor;
use hex_literal::hex;
use crate::{
builder::{DataBoxBuilder, PlaceholderDataBox, SuperBoxBuilder},
BoxType,
};
const JSON_BOX_TYPE: BoxType = BoxType(*b"json");
const RANDOM_BOX_TYPE: BoxType = BoxType(*b"abcd");
#[test]
fn basic_case() {
let expected_jumbf = hex!(
"0000002e" "6a756d62" "00000026" "6a756d64" "00000000000000000000000000000000" "03" "746573742e64657363626f7800" );
let sbox = SuperBoxBuilder::new(&hex!("00000000000000000000000000000000"))
.set_label("test.descbox");
let mut jumbf = Cursor::new(Vec::<u8>::new());
sbox.write_jumbf(&mut jumbf).unwrap();
assert_eq!(*jumbf.into_inner(), expected_jumbf);
}
#[test]
fn non_requestable_label() {
let expected_jumbf = hex!(
"0000002e" "6a756d62" "00000026" "6a756d64" "00000000000000000000000000000000" "02" "746573742e64657363626f7800" );
let sbox = SuperBoxBuilder::new(&hex!("00000000000000000000000000000000"))
.set_non_requestable_label("test.descbox");
let mut jumbf = Cursor::new(Vec::<u8>::new());
sbox.write_jumbf(&mut jumbf).unwrap();
assert_eq!(*jumbf.into_inner(), expected_jumbf);
}
#[test]
fn with_id() {
let expected_jumbf = hex!(
"00000025" "6a756d62" "0000001d" "6a756d64" "00000000000000000000000000000000" "04" "00001000" );
let sbox = SuperBoxBuilder::new(&hex!("00000000000000000000000000000000")).set_id(4096);
let mut jumbf = Cursor::new(Vec::<u8>::new());
sbox.write_jumbf(&mut jumbf).unwrap();
assert_eq!(*jumbf.into_inner(), expected_jumbf);
}
#[test]
fn with_hash() {
let expected_jumbf = hex!(
"0000004e" "6a756d62" "00000046" "6a756d64" "00000000000000000000000000000000" "0b" "746573742e64657363626f7800" "54686973206973206120626f67757320"
"686173682e2e2e2e2e2e2e2e2e2e2e2e" );
let sbox = SuperBoxBuilder::new(&hex!("00000000000000000000000000000000"))
.set_label("test.descbox")
.set_sha256_hash(b"This is a bogus hash............" as &[u8; 32]);
let mut jumbf = Cursor::new(Vec::<u8>::new());
sbox.write_jumbf(&mut jumbf).unwrap();
assert_eq!(*jumbf.into_inner(), expected_jumbf);
}
#[test]
fn with_private_box() {
let expected_jumbf = hex!(
"00000057" "6a756d62" "0000004f" "6a756d64" "00000000000000000000000000000000" "13" "746573742e64657363626f7800" "00000029" "6a736f6e" "7b20226c6f636174696f6e223a20224d61726761"
"746520436974792c204e4a227d" );
let private = DataBoxBuilder::from_owned(
JSON_BOX_TYPE,
hex!("7b20226c6f636174696f6e223a20224d61726761"
"746520436974792c204e4a227d")
.to_vec(),
);
let sbox = SuperBoxBuilder::new(&hex!("00000000000000000000000000000000"))
.set_label("test.descbox")
.set_private_box(private);
let mut jumbf = Cursor::new(Vec::<u8>::new());
sbox.write_jumbf(&mut jumbf).unwrap();
assert_eq!(*jumbf.into_inner(), expected_jumbf);
}
#[test]
fn no_label() {
let expected_jumbf = hex!(
"00000021" "6a756d62" "00000019" "6a756d64" "00000000000000000000000000000000" "00" );
let sbox = SuperBoxBuilder::new(&hex!("00000000000000000000000000000000"));
let mut jumbf = Cursor::new(Vec::<u8>::new());
sbox.write_jumbf(&mut jumbf).unwrap();
assert_eq!(*jumbf.into_inner(), expected_jumbf);
}
#[test]
fn with_child_boxes() {
let expected_jumbf = hex!(
"00000056" "6a756d62" "00000019" "6a756d64" "00000000000000000000000000000000" "00" "00000029" "6a736f6e" "7b20226c6f636174696f6e223a20224d61726761"
"746520436974792c204e4a227d" "0000000c" "61626364" "41424344" );
let cbox1 = DataBoxBuilder::from_owned(
JSON_BOX_TYPE,
hex!("7b20226c6f636174696f6e223a20224d61726761"
"746520436974792c204e4a227d")
.to_vec(),
);
let cbox2 = DataBoxBuilder::from_borrowed(RANDOM_BOX_TYPE, b"ABCD");
let sbox = SuperBoxBuilder::new(&hex!("00000000000000000000000000000000"))
.add_child_box(cbox1)
.add_child_box(cbox2);
let mut jumbf = Cursor::new(Vec::<u8>::new());
sbox.write_jumbf(&mut jumbf).unwrap();
assert_eq!(*jumbf.into_inner(), expected_jumbf);
}
#[test]
fn with_placeholder() {
let expected_jumbf = hex!(
"00000062" "6a756d62" "00000019" "6a756d64" "00000000000000000000000000000000" "00" "00000029" "6a736f6e" "7b20226c6f636174696f6e223a20224d61726761"
"746520436974792c204e4a227d" "00000018" "61626364" "00000000000000000000000000000000" );
let cbox = DataBoxBuilder::from_owned(
JSON_BOX_TYPE,
hex!("7b20226c6f636174696f6e223a20224d61726761"
"746520436974792c204e4a227d")
.to_vec(),
);
let pbox = PlaceholderDataBox::new(RANDOM_BOX_TYPE, 16);
let sbox = SuperBoxBuilder::new(&hex!("00000000000000000000000000000000"))
.add_child_box(cbox)
.add_borrowed_child_box(&pbox);
let mut jumbf = Cursor::new(Vec::<u8>::new());
sbox.write_jumbf(&mut jumbf).unwrap();
assert_eq!(*jumbf.get_ref(), expected_jumbf);
pbox.replace_payload(&mut jumbf, b"0123456789abcdef")
.unwrap();
let expected_jumbf = hex!(
"00000062" "6a756d62" "00000019" "6a756d64" "00000000000000000000000000000000" "00" "00000029" "6a736f6e" "7b20226c6f636174696f6e223a20224d61726761"
"746520436974792c204e4a227d" "00000018" "61626364" "30313233343536373839616263646566" );
assert_eq!(*jumbf.get_ref(), expected_jumbf);
}
}