1use std::string::FromUtf8Error;
2
3use bon::Builder;
4
5use crate::{atom::FourCC, parser::ParseAtomData, writer::SerializeAtom, ParseError};
6
7pub const DREF: FourCC = FourCC::new(b"dref");
8
9pub mod entry_types {
11 pub const URL: &[u8; 4] = b"url ";
13 pub const URN: &[u8; 4] = b"urn ";
15 pub const ALIS: &[u8; 4] = b"alis";
17}
18
19pub mod flags {
21 pub const SELF_CONTAINED: u32 = 0x000001;
23}
24
25#[derive(Debug, Clone)]
26pub enum DataReferenceEntryInner {
27 Url(String),
28 Urn(String),
29 Alias(Vec<u8>),
30 Unknown(FourCC, Vec<u8>),
31}
32
33impl DataReferenceEntryInner {
34 fn new(entry_type: FourCC, data: Vec<u8>) -> Result<Self, FromUtf8Error> {
35 Ok(match &entry_type.0 {
36 entry_types::URL => DataReferenceEntryInner::Url(String::from_utf8(data)?),
37 entry_types::URN => DataReferenceEntryInner::Urn(String::from_utf8(data)?),
38 entry_types::ALIS => DataReferenceEntryInner::Alias(data),
39 _ => DataReferenceEntryInner::Unknown(entry_type, data),
40 })
41 }
42}
43
44#[derive(Debug, Clone, Builder)]
46pub struct DataReferenceEntry {
47 #[builder(setters(vis = ""))]
49 pub inner: DataReferenceEntryInner,
50 #[builder(default)]
52 pub version: u8,
53 #[builder(default)]
55 pub flags: [u8; 3],
56}
57
58impl<S: data_reference_entry_builder::State> DataReferenceEntryBuilder<S> {
59 pub fn url(
60 self,
61 url: impl Into<String>,
62 ) -> DataReferenceEntryBuilder<data_reference_entry_builder::SetInner<S>>
63 where
64 S::Inner: data_reference_entry_builder::IsUnset,
65 {
66 self.inner(DataReferenceEntryInner::Url(url.into()))
67 }
68
69 pub fn urn(
70 self,
71 urn: impl Into<String>,
72 ) -> DataReferenceEntryBuilder<data_reference_entry_builder::SetInner<S>>
73 where
74 S::Inner: data_reference_entry_builder::IsUnset,
75 {
76 self.inner(DataReferenceEntryInner::Urn(urn.into()))
77 }
78}
79
80impl DataReferenceEntry {
81 pub fn is_self_contained(&self) -> bool {
83 let flags_u32 = u32::from_be_bytes([0, self.flags[0], self.flags[1], self.flags[2]]);
84 (flags_u32 & flags::SELF_CONTAINED) != 0
85 }
86}
87
88#[derive(Debug, Clone, Builder)]
91pub struct DataReferenceAtom {
92 #[builder(default = 0)]
94 pub version: u8,
95 #[builder(default = [0u8; 3])]
97 pub flags: [u8; 3],
98 #[builder(with = FromIterator::from_iter)]
100 pub entries: Vec<DataReferenceEntry>,
101}
102
103impl<S: data_reference_atom_builder::State> DataReferenceAtomBuilder<S> {
104 pub fn entry(
105 self,
106 entry: DataReferenceEntry,
107 ) -> DataReferenceAtomBuilder<data_reference_atom_builder::SetEntries<S>>
108 where
109 S::Entries: data_reference_atom_builder::IsUnset,
110 {
111 self.entries(vec![entry])
112 }
113}
114
115impl ParseAtomData for DataReferenceAtom {
116 fn parse_atom_data(atom_type: FourCC, input: &[u8]) -> Result<Self, ParseError> {
117 crate::atom::util::parser::assert_atom_type!(atom_type, DREF);
118 use crate::atom::util::parser::stream;
119 use winnow::Parser;
120 Ok(parser::parse_dref_data.parse(stream(input))?)
121 }
122}
123
124impl SerializeAtom for DataReferenceAtom {
125 fn atom_type(&self) -> FourCC {
126 DREF
127 }
128
129 fn into_body_bytes(self) -> Vec<u8> {
130 serializer::serialize_dref_data(self)
131 }
132}
133
134mod serializer {
135 use crate::atom::{
136 dref::{entry_types, DataReferenceEntry, DataReferenceEntryInner},
137 DataReferenceAtom,
138 };
139
140 pub fn serialize_dref_data(data: DataReferenceAtom) -> Vec<u8> {
141 let entries = data.entries;
142 vec![
143 version(data.version),
144 flags(data.flags),
145 entry_count(entries.len()),
146 entries.into_iter().flat_map(entry).collect(),
147 ]
148 .into_iter()
149 .flatten()
150 .collect()
151 }
152
153 fn version(version: u8) -> Vec<u8> {
154 vec![version]
155 }
156
157 fn flags(flags: [u8; 3]) -> Vec<u8> {
158 flags.to_vec()
159 }
160
161 fn entry_count(n: usize) -> Vec<u8> {
162 u32::try_from(n)
163 .expect("entries len must fit in a u32")
164 .to_be_bytes()
165 .to_vec()
166 }
167
168 fn entry(e: DataReferenceEntry) -> Vec<u8> {
169 let e = raw_entry(e);
170 let data: Vec<u8> = vec![version(e.version), flags(e.flags), e.data]
171 .into_iter()
172 .flatten()
173 .collect();
174 let header_size = 4 + 4; vec![
176 entry_size(header_size + data.len()).to_vec(),
177 e.typ.to_vec(),
178 data,
179 ]
180 .into_iter()
181 .flatten()
182 .collect()
183 }
184
185 fn entry_size(n: usize) -> [u8; 4] {
186 u32::try_from(n)
187 .expect("entry size len must fit in a u32")
188 .to_be_bytes()
189 }
190
191 struct RawEntry {
192 version: u8,
193 flags: [u8; 3],
194 typ: [u8; 4],
195 data: Vec<u8>,
196 }
197
198 fn raw_entry(e: DataReferenceEntry) -> RawEntry {
199 let (typ, data) = match e.inner {
200 DataReferenceEntryInner::Url(url) => (*entry_types::URL, url.into_bytes()),
201 DataReferenceEntryInner::Urn(urn) => (*entry_types::URN, urn.into_bytes()),
202 DataReferenceEntryInner::Alias(alias_data) => (*entry_types::ALIS, alias_data),
203 DataReferenceEntryInner::Unknown(typ, unknown_data) => (typ.0, unknown_data),
204 };
205 RawEntry {
206 version: e.version,
207 flags: e.flags,
208 typ,
209 data,
210 }
211 }
212}
213
214mod parser {
215 use winnow::{
216 binary::length_repeat,
217 combinator::{seq, trace},
218 error::StrContext,
219 Parser,
220 };
221
222 use super::{DataReferenceAtom, DataReferenceEntry, DataReferenceEntryInner};
223 use crate::atom::util::parser::{
224 atom_size, be_u32_as_usize, combinators::inclusive_length_and_then, flags3, fourcc,
225 rest_vec, version, Stream,
226 };
227
228 pub fn parse_dref_data(input: &mut Stream<'_>) -> winnow::ModalResult<DataReferenceAtom> {
229 trace(
230 "dref",
231 (version, flags3, entries)
232 .map(|(version, flags, entries)| DataReferenceAtom {
233 version,
234 flags,
235 entries,
236 })
237 .context(StrContext::Label("dref")),
238 )
239 .parse_next(input)
240 }
241
242 fn entries(input: &mut Stream<'_>) -> winnow::ModalResult<Vec<DataReferenceEntry>> {
243 trace(
244 "entries",
245 length_repeat(
246 be_u32_as_usize.context(StrContext::Label("entry_count")),
247 entry.context(StrContext::Label("entry")),
248 ),
249 )
250 .parse_next(input)
251 }
252
253 fn entry(input: &mut Stream<'_>) -> winnow::ModalResult<DataReferenceEntry> {
254 trace(
255 "entry",
256 inclusive_length_and_then(atom_size, move |input: &mut Stream<'_>| {
257 let typ = fourcc.parse_next(input)?;
258 seq!(DataReferenceEntry {
259 version: version,
260 flags: flags3,
261 inner: rest_vec
262 .try_map(|data| DataReferenceEntryInner::new(typ, data))
263 .context(StrContext::Label("data")),
264 })
265 .parse_next(input)
266 }),
267 )
268 .parse_next(input)
269 }
270}
271
272#[cfg(test)]
273mod tests {
274 use super::*;
275 use crate::atom::test_utils::test_atom_roundtrip;
276
277 #[test]
279 fn test_dref_roundtrip() {
280 test_atom_roundtrip::<DataReferenceAtom>(DREF);
281 }
282}