1extern crate alloc;
5
6use alloc::string::String;
7use alloc::vec::Vec;
8
9use broadcast_common::{Parse, Serialize};
10
11use crate::error::{Error, Result};
12use crate::local_set::{LocalSet, StructuralSetKind};
13use crate::sets::{
14 InterchangeObjectFields, LocalSetOwnedItem, collect_dark, finish_owned_set, get_optional_raw,
15 get_required_fixed, get_required_raw, owned_set_serialized_len, serialize_owned_set,
16};
17use crate::types::{MxfTimestamp, ProductVersion, UlBytes, decode_utf16_be, encode_utf16_be};
18
19pub const TAG_THIS_GENERATION_UID: u16 = 0x3C09;
21pub const TAG_COMPANY_NAME: u16 = 0x3C01;
23pub const TAG_PRODUCT_NAME: u16 = 0x3C02;
25pub const TAG_PRODUCT_VERSION: u16 = 0x3C03;
27pub const TAG_VERSION_STRING: u16 = 0x3C04;
29pub const TAG_PRODUCT_UID: u16 = 0x3C05;
31pub const TAG_MODIFICATION_DATE: u16 = 0x3C06;
33pub const TAG_TOOLKIT_VERSION: u16 = 0x3C07;
35pub const TAG_PLATFORM: u16 = 0x3C08;
37
38const KNOWN_TAGS: [u16; 12] = [
39 crate::sets::TAG_INSTANCE_UID,
40 crate::sets::TAG_GENERATION_UID,
41 crate::sets::TAG_OBJECT_CLASS,
42 TAG_THIS_GENERATION_UID,
43 TAG_COMPANY_NAME,
44 TAG_PRODUCT_NAME,
45 TAG_PRODUCT_VERSION,
46 TAG_VERSION_STRING,
47 TAG_PRODUCT_UID,
48 TAG_MODIFICATION_DATE,
49 TAG_TOOLKIT_VERSION,
50 TAG_PLATFORM,
51];
52
53#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct Identification {
65 pub interchange: InterchangeObjectFields,
67 pub this_generation_uid: UlBytes,
70 pub company_name: String,
72 pub product_name: String,
74 pub product_version: Option<ProductVersion>,
76 pub version_string: String,
78 pub product_uid: UlBytes,
80 pub modification_date: MxfTimestamp,
82 pub toolkit_version: Option<ProductVersion>,
84 pub platform: Option<String>,
86 pub dark: Vec<(u16, Vec<u8>)>,
88}
89
90impl<'a> Parse<'a> for Identification {
91 type Error = Error;
92
93 fn parse(bytes: &'a [u8]) -> Result<Self> {
94 let set = LocalSet::parse(bytes)?;
95 if set.kind() != StructuralSetKind::Identification {
96 return Err(Error::KeyPrefixMismatch {
97 what: "Identification (Table 17)",
98 });
99 }
100 let items = &set.items;
101 let interchange = InterchangeObjectFields::decode(items, "Identification")?;
102 let this_generation_uid = get_required_fixed::<16>(
103 items,
104 TAG_THIS_GENERATION_UID,
105 "This Generation UID",
106 "Identification",
107 )?;
108 let company_name = decode_utf16_be(get_required_raw(
109 items,
110 TAG_COMPANY_NAME,
111 "Company Name",
112 "Identification",
113 )?)
114 .map_err(|_| Error::InvalidUtf16 {
115 tag: TAG_COMPANY_NAME,
116 name: "Company Name",
117 })?;
118 let product_name = decode_utf16_be(get_required_raw(
119 items,
120 TAG_PRODUCT_NAME,
121 "Product Name",
122 "Identification",
123 )?)
124 .map_err(|_| Error::InvalidUtf16 {
125 tag: TAG_PRODUCT_NAME,
126 name: "Product Name",
127 })?;
128 let product_version = get_optional_raw(items, TAG_PRODUCT_VERSION)
129 .map(ProductVersion::parse)
130 .transpose()?;
131 let version_string = decode_utf16_be(get_required_raw(
132 items,
133 TAG_VERSION_STRING,
134 "Version String",
135 "Identification",
136 )?)
137 .map_err(|_| Error::InvalidUtf16 {
138 tag: TAG_VERSION_STRING,
139 name: "Version String",
140 })?;
141 let product_uid =
142 get_required_fixed::<16>(items, TAG_PRODUCT_UID, "Product UID", "Identification")?;
143 let modification_date = MxfTimestamp::parse(get_required_raw(
144 items,
145 TAG_MODIFICATION_DATE,
146 "Modification Date",
147 "Identification",
148 )?)?;
149 let toolkit_version = get_optional_raw(items, TAG_TOOLKIT_VERSION)
150 .map(ProductVersion::parse)
151 .transpose()?;
152 let platform = get_optional_raw(items, TAG_PLATFORM)
153 .map(decode_utf16_be)
154 .transpose()
155 .map_err(|_| Error::InvalidUtf16 {
156 tag: TAG_PLATFORM,
157 name: "Platform",
158 })?;
159 let dark = collect_dark(items, &KNOWN_TAGS);
160
161 Ok(Identification {
162 interchange,
163 this_generation_uid,
164 company_name,
165 product_name,
166 product_version,
167 version_string,
168 product_uid,
169 modification_date,
170 toolkit_version,
171 platform,
172 dark,
173 })
174 }
175}
176
177impl Identification {
178 fn owned_items(&self) -> Vec<LocalSetOwnedItem> {
179 let mut out = Vec::new();
180 self.interchange.encode_into(&mut out);
181 out.push(LocalSetOwnedItem::fixed(
182 TAG_THIS_GENERATION_UID,
183 self.this_generation_uid,
184 ));
185 out.push(LocalSetOwnedItem::owned(
186 TAG_COMPANY_NAME,
187 encode_utf16_be(&self.company_name),
188 ));
189 out.push(LocalSetOwnedItem::owned(
190 TAG_PRODUCT_NAME,
191 encode_utf16_be(&self.product_name),
192 ));
193 if let Some(pv) = self.product_version {
194 let mut buf = [0u8; crate::types::PRODUCT_VERSION_LEN];
195 pv.serialize_into(&mut buf).expect("fixed-size buffer");
196 out.push(LocalSetOwnedItem::owned(TAG_PRODUCT_VERSION, buf.to_vec()));
197 }
198 out.push(LocalSetOwnedItem::owned(
199 TAG_VERSION_STRING,
200 encode_utf16_be(&self.version_string),
201 ));
202 out.push(LocalSetOwnedItem::fixed(TAG_PRODUCT_UID, self.product_uid));
203 {
204 let mut buf = [0u8; crate::types::TIMESTAMP_LEN];
205 self.modification_date
206 .serialize_into(&mut buf)
207 .expect("fixed-size buffer");
208 out.push(LocalSetOwnedItem::owned(
209 TAG_MODIFICATION_DATE,
210 buf.to_vec(),
211 ));
212 }
213 if let Some(tv) = self.toolkit_version {
214 let mut buf = [0u8; crate::types::PRODUCT_VERSION_LEN];
215 tv.serialize_into(&mut buf).expect("fixed-size buffer");
216 out.push(LocalSetOwnedItem::owned(TAG_TOOLKIT_VERSION, buf.to_vec()));
217 }
218 if let Some(platform) = &self.platform {
219 out.push(LocalSetOwnedItem::owned(
220 TAG_PLATFORM,
221 encode_utf16_be(platform),
222 ));
223 }
224 out
225 }
226}
227
228impl Serialize for Identification {
229 type Error = Error;
230
231 fn serialized_len(&self) -> usize {
232 let (key, items) = finish_owned_set(
233 StructuralSetKind::Identification,
234 self.owned_items(),
235 &self.dark,
236 );
237 owned_set_serialized_len(key, &items)
238 }
239
240 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
241 let (key, items) = finish_owned_set(
242 StructuralSetKind::Identification,
243 self.owned_items(),
244 &self.dark,
245 );
246 serialize_owned_set(key, &items, buf)
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253 use crate::types::ReleaseType;
254
255 fn sample() -> Identification {
256 Identification {
257 interchange: InterchangeObjectFields {
258 instance_uid: [0x11; 16],
259 generation_uid: None,
260 object_class: None,
261 },
262 this_generation_uid: [0x22; 16],
263 company_name: String::from("Acme Broadcast"),
264 product_name: String::from("st377-1"),
265 product_version: Some(ProductVersion {
266 major: 0,
267 minor: 1,
268 tertiary: 0,
269 patch: 0,
270 release: ReleaseType::Development,
271 }),
272 version_string: String::from("0.1.0-dev"),
273 product_uid: [0x33; 16],
274 modification_date: MxfTimestamp {
275 year: 2019,
276 month: 11,
277 day: 28,
278 hour: 9,
279 minute: 30,
280 second: 0,
281 msec_div4: 0,
282 },
283 toolkit_version: None,
284 platform: Some(String::from("rustc (linux)")),
285 dark: Vec::new(),
286 }
287 }
288
289 #[test]
290 fn construct_serialize_parse_round_trip() {
291 let id = sample();
292 let bytes = id.to_bytes();
293 let parsed = Identification::parse(&bytes).unwrap();
294 assert_eq!(parsed, id);
295 assert_eq!(parsed.to_bytes(), bytes);
296 }
297
298 #[test]
299 fn mutation_changes_serialized_bytes() {
300 let mut id = sample();
301 let before = id.to_bytes();
302 id.company_name = String::from("Changed Inc.");
303 let after = id.to_bytes();
304 assert_ne!(before, after);
305 assert_eq!(
306 Identification::parse(&after).unwrap().company_name,
307 "Changed Inc."
308 );
309 }
310}