mp4san 0.3.0

An MP4 file sanitizer.
Documentation
#![allow(missing_docs)]

use bytes::{BufMut, BytesMut};

use crate::error::Result;

use super::error::{ParseResultExt, WhileParsingField};
use super::mp4box::Boxes;
use super::{BoxType, ParseBox, ParseError, ParsedBox, TrakBox};

#[derive(Clone, Debug)]
pub struct MoovBox {
    children: Boxes,
}

const NAME: BoxType = BoxType::MOOV;

impl MoovBox {
    #[cfg(test)]
    pub(crate) fn with_children<C: Into<Boxes>>(children: C) -> Self {
        Self { children: children.into() }
    }

    pub fn traks(&mut self) -> impl Iterator<Item = Result<&mut TrakBox, ParseError>> + '_ {
        self.children
            .get_mut()
            .map(|result| result.while_parsing_child(NAME, BoxType::TRAK))
    }

    pub fn encoded_len(&self) -> u64 {
        self.children.encoded_len()
    }
}

impl ParseBox for MoovBox {
    fn parse(buf: &mut BytesMut) -> Result<Self, ParseError> {
        let children = Boxes::parse(buf).while_parsing_field(NAME, "children")?;
        ensure_attach!(
            children.box_types().any(|box_type| box_type == BoxType::TRAK),
            ParseError::MissingRequiredBox(BoxType::TRAK),
            WhileParsingField(NAME, "children"),
        );
        Ok(Self { children })
    }

    fn box_type() -> BoxType {
        NAME
    }
}
impl ParsedBox for MoovBox {
    fn encoded_len(&self) -> u64 {
        self.children.encoded_len()
    }

    fn put_buf(&self, out: &mut dyn BufMut) {
        self.children.put_buf(out);
    }
}

#[cfg(test)]
mod test {
    use crate::parse::Mp4Box;

    use super::*;

    fn test_trak() -> Mp4Box<TrakBox> {
        Mp4Box::with_data(TrakBox::with_children(vec![]).into()).unwrap()
    }

    #[test]
    fn roundtrip() {
        let mut data = BytesMut::new();
        MoovBox::with_children(vec![test_trak().into()]).put_buf(&mut data);
        MoovBox::parse(&mut data).unwrap();
    }

    #[test]
    fn no_traks() {
        let mut data = BytesMut::new();
        MoovBox::with_children(vec![]).put_buf(&mut data);
        let err = MoovBox::parse(&mut data).unwrap_err();
        assert!(
            matches!(err.get_ref(), ParseError::MissingRequiredBox(BoxType::TRAK)),
            "{err}",
        );
    }
}