scuffle_mp4/boxes/
macros.rs

1macro_rules! match_helper {
2    ([size] $expr:expr, $($name:tt,)*) => {
3        match $expr {
4            $(
5                Self::$name(box_) => box_.size(),
6            )*
7            Self::Unknown((_, data)) => {
8                let size = data.len() as u64 + 8;
9                if size > u32::MAX as u64 {
10                    size + 8
11                } else {
12                    size
13                }
14            }
15        }
16    };
17    ([write] $expr:expr, $writer:expr, $($name:tt,)*) => {
18        match $expr {
19            $(
20                Self::$name(box_) => box_.mux($writer)?,
21            )*
22            Self::Unknown((header, data)) => {
23                let size = data.len() as u64 + 8;
24                if size > u32::MAX as u64 {
25                    $writer.write_u32::<byteorder::BigEndian>(1)?;
26                } else {
27                    $writer.write_u32::<byteorder::BigEndian>(size as u32)?;
28                }
29                $writer.write_all(&header.box_type)?;
30                if size > u32::MAX as u64 {
31                    $writer.write_u64::<byteorder::BigEndian>(size)?;
32                }
33                $writer.write_all(data)?;
34            }
35        }
36    };
37    ([parse] $expr:expr, $header:expr, $data:expr, $($name:tt,)*) => {
38        match $expr {
39            $(
40                &$name::NAME => Ok(Self::$name(Box::new(<$name>::demux($header, $data)?))),
41            )*
42            _ => Ok(Self::Unknown(($header, $data))),
43        }
44    };
45}
46
47macro_rules! as_fn {
48    ($($type:tt,)*) => {
49        $(
50            paste! {
51                #[allow(dead_code)]
52                pub fn [<as_ $type:lower>](&self) -> Option<&$type> {
53                    match self {
54                        Self::$type(box_) => Some(box_),
55                        _ => None,
56                    }
57                }
58            }
59        )*
60    };
61}
62
63macro_rules! impl_from {
64    ($($type:tt,)*) => {
65        $(
66            impl From<$type> for DynBox {
67                fn from(box_: $type) -> Self {
68                    Self::$type(Box::new(box_))
69                }
70            }
71        )*
72    };
73}
74
75macro_rules! impl_box {
76    ($($type:tt,)*) => {
77        #[derive(Debug, Clone, PartialEq)]
78        pub enum DynBox {
79            $(
80                $type(Box<$type>),
81            )*
82            Unknown((BoxHeader, Bytes)),
83        }
84
85        impl DynBox {
86            pub fn size(&self) -> u64 {
87                match_helper!(
88                    [size] self,
89                    $($type,)*
90                )
91            }
92
93            pub fn mux<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
94                match_helper!(
95                    [write] self, writer,
96                    $($type,)*
97                );
98
99                Ok(())
100            }
101
102            pub fn demux(reader: &mut io::Cursor<Bytes>) -> io::Result<Self> {
103                let (header, data) = BoxHeader::demux(reader)?;
104
105                match_helper!(
106                    [parse] & header.box_type,
107                    header,
108                    data,
109                    $($type,)*
110                )
111            }
112
113            pub fn name(&self) -> &str {
114                match self {
115                    $(
116                        Self::$type(_) => std::str::from_utf8(&$type::NAME).expect("invalid utf8"),
117                    )*
118                    Self::Unknown((header, _)) => std::str::from_utf8(&header.box_type).unwrap_or("unknown"),
119                }
120            }
121
122            as_fn!(
123                $($type,)*
124            );
125        }
126
127
128        impl_from!(
129            $($type,)*
130        );
131    };
132}