Skip to main content

mp4_atom/
uuid.rs

1use crate::*;
2
3pub trait UuidAtom: Sized {
4    const EXTENDED_TYPE: ExtendedType;
5
6    fn decode_uuid_body<B: Buf>(buf: &mut B) -> Result<Self>;
7    fn encode_uuid_body<B: BufMut>(&self, buf: &mut B) -> Result<()>;
8}
9
10pub(crate) trait UuidAtomExt: Sized {
11    const EXTENDED_TYPE_EXT: ExtendedType;
12    type Ext: Ext;
13
14    fn decode_uuid_body_ext<B: Buf>(buf: &mut B, ext: Self::Ext) -> Result<Self>;
15    fn encode_uuid_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<Self::Ext>;
16}
17
18impl<T: UuidAtomExt> UuidAtom for T {
19    const EXTENDED_TYPE: ExtendedType = Self::EXTENDED_TYPE_EXT;
20
21    // logic borrowed from ./atom_ext.rs
22    fn decode_uuid_body<B: Buf>(buf: &mut B) -> Result<Self> {
23        let ext = Ext::decode(u32::decode(buf)?)?;
24        UuidAtomExt::decode_uuid_body_ext(buf, ext)
25    }
26
27    fn encode_uuid_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
28        // Here's the magic, we reserve space for the version/flags first
29        let start = buf.len();
30        0u32.encode(buf)?;
31
32        // That way we can return them as part of the trait, avoiding boilerplate
33        let ext = self.encode_uuid_body_ext(buf)?;
34
35        // Go back and update the version/flags
36        let header = ext.encode()?;
37        buf.set_slice(start, &header.to_be_bytes());
38
39        Ok(())
40    }
41}
42
43// This can be encapuslated in a macro to allow new uuid boxes to be made
44// without adding new match arms. In the same way as in ./any.rs
45#[derive(Clone, Eq, PartialEq, Debug)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47#[non_exhaustive]
48pub enum Uuid {
49    C2pa(C2pa),
50    Unknown(ExtendedType, Vec<u8>),
51}
52
53impl Atom for Uuid {
54    const KIND: FourCC = FourCC::new(b"uuid");
55
56    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
57        let et = ExtendedType::decode(buf)?;
58
59        match et {
60            C2pa::EXTENDED_TYPE => Ok(Uuid::C2pa(C2pa::decode_uuid_body(buf)?)),
61            _ => {
62                let payload = Vec::<u8>::decode(buf)?;
63                Ok(Uuid::Unknown(et, payload))
64            }
65        }
66    }
67
68    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
69        match self {
70            Uuid::C2pa(c2pa) => {
71                C2pa::EXTENDED_TYPE.encode(buf)?;
72                c2pa.encode_uuid_body(buf)?;
73            }
74            Uuid::Unknown(et, payload) => {
75                et.encode(buf)?;
76                payload.encode(buf)?;
77            }
78        }
79
80        Ok(())
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_c2pa_round_trip() -> Result<()> {
90        let original = Uuid::C2pa(C2pa {
91            box_purpose: "urn:uuid:...".to_string(),
92            data: vec![1, 2, 3, 4],
93        });
94
95        // 1. Encode into a byte buffer
96        let mut buf = Vec::new();
97        original.encode_body(&mut buf)?;
98
99        // 2. Decode back from the buffer
100        let mut cursor = &buf[..];
101        let decoded = Uuid::decode_body(&mut cursor)?;
102
103        // 3. Assert equality
104        assert_eq!(original, decoded, "They werent equal");
105        Ok(())
106    }
107
108    #[test]
109    fn test_c2pa_uuid_golden_vector() -> Result<()> {
110        let mut bytes = Vec::new();
111
112        // 1. C2pa Extended Type bytes (16 bytes from the specification)
113        bytes.extend_from_slice(&[
114            0xD8, 0xFE, 0xC3, 0xD6, 0x1B, 0x0E, 0x48, 0x3C, 0x92, 0x97, 0x58, 0x28, 0x87, 0x7E,
115            0xC4, 0x81,
116        ]);
117
118        // 2. FullBox version and flags (version = 0, flags = 0 -> 4 bytes of 0x00)
119        bytes.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]);
120
121        // 3. box_purpose string bytes (null-terminated UTF-8 string)
122        bytes.extend_from_slice(b"urn:uuid:test\0");
123
124        // 4. Data payload bytes (0xDEADBEEF)
125        bytes.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]);
126
127        let mut cursor = &bytes[..];
128        let decoded = Uuid::decode_body(&mut cursor)?;
129
130        let expected = Uuid::C2pa(C2pa {
131            box_purpose: "urn:uuid:test".to_string(),
132            data: vec![0xDE, 0xAD, 0xBE, 0xEF],
133        });
134
135        assert_eq!(decoded, expected);
136        Ok(())
137    }
138}