1#![doc = include_str!("../README.md")]
2
3pub use fourcc_rs_macros::fourcc;
4use std::{char, fmt, primitive};
5
6#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
7#[cfg_attr(feature = "binrw", derive(binrw::BinRead, binrw::BinWrite))]
8#[cfg_attr(feature = "binrw", br(big))]
9pub struct FourCC(#[doc(hidden)] pub u32);
15
16impl FourCC {
17 pub const fn new(value: u32) -> Self {
19 Self(value)
20 }
21
22 fn components(&self) -> [u8; 4] {
24 [
25 ((self.0 >> 24) & 0xff) as u8,
26 ((self.0 >> 16) & 0xff) as u8,
27 ((self.0 >> 8) & 0xff) as u8,
28 (self.0 & 0xff) as u8,
29 ]
30 }
31
32 pub fn value(&self) -> u32 {
33 self.0
34 }
35
36 fn chars(&self) -> [primitive::char; 4] {
41 self.components()
42 .map(|f| char::from_u32(f as u32).unwrap_or(char::from_u32(0xFFFD).unwrap()))
43 }
44
45 pub fn name(&self) -> String {
49 self.chars().into_iter().collect::<String>()
50 }
51}
52
53#[cfg(feature = "serde")]
54impl serde::Serialize for FourCC {
55 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
56 where
57 S: serde::Serializer,
58 {
59 serializer.collect_str(&self.name())
60 }
61}
62
63#[cfg(feature = "serde")]
64impl<'de> serde::Deserialize<'de> for FourCC {
65 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
66 where
67 D: serde::Deserializer<'de>,
68 {
69 struct FourCCVisitor {}
70 impl<'de> serde::de::Visitor<'de> for FourCCVisitor {
71 type Value = FourCC;
72
73 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
74 where
75 E: serde::de::Error,
76 {
77 Err(serde::de::Error::invalid_type(
78 serde::de::Unexpected::Bool(v),
79 &self,
80 ))
81 }
82
83 fn visit_i8<E>(self, v: i8) -> Result<Self::Value, E>
84 where
85 E: serde::de::Error,
86 {
87 self.visit_i64(v as i64)
88 }
89
90 fn visit_i16<E>(self, v: i16) -> Result<Self::Value, E>
91 where
92 E: serde::de::Error,
93 {
94 self.visit_i64(v as i64)
95 }
96
97 fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
98 where
99 E: serde::de::Error,
100 {
101 self.visit_i64(v as i64)
102 }
103
104 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
105 where
106 E: serde::de::Error,
107 {
108 Err(serde::de::Error::invalid_type(
109 serde::de::Unexpected::Signed(v),
110 &self,
111 ))
112 }
113
114 fn visit_i128<E>(self, v: i128) -> Result<Self::Value, E>
115 where
116 E: serde::de::Error,
117 {
118 let mut writer = String::new();
119 fmt::Write::write_fmt(&mut writer, format_args!("integer `{v}` as i128")).unwrap();
120 Err(serde::de::Error::invalid_type(
121 serde::de::Unexpected::Other(writer.as_str()),
122 &self,
123 ))
124 }
125
126 fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
127 where
128 E: serde::de::Error,
129 {
130 self.visit_u64(v as u64)
131 }
132
133 fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
134 where
135 E: serde::de::Error,
136 {
137 self.visit_u64(v as u64)
138 }
139
140 fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
141 where
142 E: serde::de::Error,
143 {
144 self.visit_u64(v as u64)
145 }
146
147 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
148 where
149 E: serde::de::Error,
150 {
151 Err(serde::de::Error::invalid_type(
152 serde::de::Unexpected::Unsigned(v),
153 &self,
154 ))
155 }
156
157 fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
158 where
159 E: serde::de::Error,
160 {
161 let mut writer = String::new();
162 fmt::Write::write_fmt(&mut writer, format_args!("integer `{v}` as u128")).unwrap();
163 Err(serde::de::Error::invalid_type(
164 serde::de::Unexpected::Other(writer.as_str()),
165 &self,
166 ))
167 }
168
169 fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
170 where
171 E: serde::de::Error,
172 {
173 self.visit_f64(v as f64)
174 }
175
176 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
177 where
178 E: serde::de::Error,
179 {
180 Err(serde::de::Error::invalid_type(
181 serde::de::Unexpected::Float(v),
182 &self,
183 ))
184 }
185
186 fn visit_char<E>(self, v: char) -> Result<Self::Value, E>
187 where
188 E: serde::de::Error,
189 {
190 self.visit_str(v.encode_utf8(&mut [0u8; 4]))
191 }
192
193 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
194 where
195 E: serde::de::Error,
196 {
197 FourCC::try_from(v).map_err(|_| {
198 serde::de::Error::invalid_type(serde::de::Unexpected::Str(v), &self)
199 })
200 }
201
202 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
203 where
204 E: serde::de::Error,
205 {
206 self.visit_str(v)
207 }
208
209 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
210 where
211 E: serde::de::Error,
212 {
213 self.visit_str(&v)
214 }
215
216 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
217 where
218 E: serde::de::Error,
219 {
220 Err(serde::de::Error::invalid_type(
221 serde::de::Unexpected::Bytes(v),
222 &self,
223 ))
224 }
225
226 fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
227 where
228 E: serde::de::Error,
229 {
230 self.visit_bytes(v)
231 }
232
233 fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
234 where
235 E: serde::de::Error,
236 {
237 self.visit_bytes(&v)
238 }
239
240 fn visit_none<E>(self) -> Result<Self::Value, E>
241 where
242 E: serde::de::Error,
243 {
244 Err(serde::de::Error::invalid_type(
245 serde::de::Unexpected::Option,
246 &self,
247 ))
248 }
249
250 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
251 where
252 D: serde::Deserializer<'de>,
253 {
254 let _ = deserializer;
255 Err(serde::de::Error::invalid_type(
256 serde::de::Unexpected::Option,
257 &self,
258 ))
259 }
260
261 fn visit_unit<E>(self) -> Result<Self::Value, E>
262 where
263 E: serde::de::Error,
264 {
265 Err(serde::de::Error::invalid_type(
266 serde::de::Unexpected::Unit,
267 &self,
268 ))
269 }
270
271 fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
272 where
273 D: serde::Deserializer<'de>,
274 {
275 let _ = deserializer;
276 Err(serde::de::Error::invalid_type(
277 serde::de::Unexpected::NewtypeStruct,
278 &self,
279 ))
280 }
281
282 fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
283 where
284 A: serde::de::SeqAccess<'de>,
285 {
286 let _ = seq;
287 Err(serde::de::Error::invalid_type(
288 serde::de::Unexpected::Seq,
289 &self,
290 ))
291 }
292
293 fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
294 where
295 A: serde::de::MapAccess<'de>,
296 {
297 let _ = map;
298 Err(serde::de::Error::invalid_type(
299 serde::de::Unexpected::Map,
300 &self,
301 ))
302 }
303
304 fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
305 where
306 A: serde::de::EnumAccess<'de>,
307 {
308 let _ = data;
309 Err(serde::de::Error::invalid_type(
310 serde::de::Unexpected::Enum,
311 &self,
312 ))
313 }
314
315 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
316 f.write_str("string of four ASCII characters")
317 }
318 }
319 deserializer.deserialize_str(FourCCVisitor {})
320 }
321}
322
323impl fmt::Display for FourCC {
324 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325 let [c1, c2, c3, c4] = self.chars();
326 format!("'{c1}{c2}{c3}{c4}'").fmt(f)
327 }
328}
329
330impl fmt::Debug for FourCC {
331 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332 let [c1, c2, c3, c4] = self.chars();
333 format!("'{c1}{c2}{c3}{c4}'").fmt(f)
334 }
335}
336
337impl From<FourCC> for u32 {
338 fn from(val: FourCC) -> Self {
339 val.0
340 }
341}
342
343impl From<u32> for FourCC {
344 fn from(value: u32) -> Self {
345 Self(value)
346 }
347}
348
349#[derive(Debug)]
350pub enum Error {
352 InvalidInputLength,
354 InvalidInputChar,
356}
357
358impl TryFrom<String> for FourCC {
359 type Error = Error;
360
361 fn try_from(value: String) -> Result<Self, Self::Error> {
362 if value.len() != 4 {
363 return Err(Error::InvalidInputLength);
364 }
365
366 if !value.is_ascii() {
367 return Err(Error::InvalidInputChar);
368 };
369
370 Ok(Self(value.chars().enumerate().fold(
371 0u32,
372 |acc: u32, (idx, value): (usize, char)| -> u32 {
373 let mut buf = vec![0u8];
374 value.encode_utf8(&mut buf);
375
376 acc | ((buf[0] as u32) << (24 - 8 * idx))
377 },
378 )))
379 }
380}
381
382impl TryFrom<&str> for FourCC {
383 type Error = Error;
384
385 fn try_from(value: &str) -> Result<Self, Self::Error> {
386 if value.len() != 4 {
387 return Err(Error::InvalidInputLength);
388 }
389
390 if !value.is_ascii() {
391 return Err(Error::InvalidInputChar);
392 };
393
394 fn to_u8(c: char) -> u8 {
395 let mut buf = vec![0u8];
396 c.encode_utf8(&mut buf);
397 buf[0]
398 }
399
400 let chars: Vec<_> = value.chars().collect();
401
402 Ok(Self(u32_from_u8s(
403 to_u8(chars[0]),
404 to_u8(chars[1]),
405 to_u8(chars[2]),
406 to_u8(chars[3]),
407 )))
408 }
409}
410
411impl From<[u8; 4]> for FourCC {
412 fn from(value: [u8; 4]) -> Self {
413 Self(u32_from_u8s(value[0], value[1], value[2], value[3]))
414 }
415}
416
417impl From<&[u8; 4]> for FourCC {
418 fn from(value: &[u8; 4]) -> Self {
419 Self(u32_from_u8s(value[0], value[1], value[2], value[3]))
420 }
421}
422
423#[inline]
424const fn u32_from_u8s(v1: u8, v2: u8, v3: u8, v4: u8) -> u32 {
425 ((v1 as u32) << 24) | ((v2 as u32) << 16) | ((v3 as u32) << 8) | (v4 as u32)
426}
427
428#[cfg(test)]
429mod test {
430 use crate::{Error, FourCC};
431
432 #[test]
433 fn creation() {
434 assert_eq!(FourCC::new(0), FourCC(0));
435 assert_eq!(FourCC::new(0xabcdef01), FourCC(0xabcdef01));
436 }
437
438 #[test]
439 fn display() {
440 assert_eq!(&format!("{}", FourCC::new(0x41424344)), "'ABCD'");
441 }
442
443 #[test]
444 fn display_options() {
445 assert_eq!(&format!("{:8}", FourCC::new(0x41424344)), "'ABCD' ");
446 assert_eq!(&format!("{:8}", FourCC::new(0x00000000)), "'\0\0\0\0' ");
447 assert_eq!(&format!("{:8}", FourCC::new(0x00010000)), "'\0\u{1}\0\0' ");
448 }
449
450 #[test]
451 fn debug() {
452 assert_eq!(&format!("{:?}", FourCC::new(0x41424344)), "\"'ABCD'\"");
453 }
454
455 #[test]
456 fn components() {
457 let code = FourCC::new(0xabcdef01);
458 assert_eq!(code.components(), [0xab, 0xcd, 0xef, 0x01]);
459 }
460
461 #[test]
462 fn chars() {
463 let code: FourCC = b"ABCD".into();
464 assert_eq!(code.chars(), ['A', 'B', 'C', 'D']);
465 }
466
467 #[test]
468 fn name() {
469 let code: FourCC = b"ABCD".into();
470 assert_eq!(code.name(), "ABCD".to_string());
471 }
472
473 #[test]
474 fn from_u32() {
475 let code: FourCC = FourCC::from(0xabcdef01);
476 assert_eq!(code, FourCC(0xabcdef01));
477 }
478
479 #[test]
480 fn into_u32() {
481 let code: FourCC = FourCC::new(0xabcdef01);
482 let code: u32 = code.into();
483 assert_eq!(code, 0xabcdef01u32);
484 }
485
486 #[test]
487 fn try_from_string() {
488 let code: FourCC = String::from("ABCD").try_into().unwrap();
489 assert_eq!(code, FourCC::new(0x41424344));
490
491 assert!(matches!(
492 String::from("ABCDE").try_into() as Result<FourCC, _>,
493 Err(Error::InvalidInputLength)
494 ));
495
496 assert!(matches!(
497 String::from("ABC").try_into() as Result<FourCC, _>,
498 Err(Error::InvalidInputLength)
499 ));
500
501 assert!(matches!(
502 String::from("AB©").try_into() as Result<FourCC, _>,
503 Err(Error::InvalidInputChar)
504 ));
505 }
506
507 #[test]
508 fn try_from_str() {
509 let code: FourCC = "ABCD".try_into().unwrap();
510 assert_eq!(code, FourCC::new(0x41424344));
511
512 assert!(matches!(
513 "ABCDE".try_into() as Result<FourCC, _>,
514 Err(Error::InvalidInputLength)
515 ));
516
517 assert!(matches!(
518 "ABC".try_into() as Result<FourCC, _>,
519 Err(Error::InvalidInputLength)
520 ));
521
522 assert!(matches!(
523 "AB©".try_into() as Result<FourCC, _>,
524 Err(Error::InvalidInputChar)
525 ));
526 }
527
528 #[test]
529 fn from_u8_array() {
530 let code: FourCC = b"ABCD".to_owned().into();
531 assert_eq!(code, FourCC::new(0x41424344));
532 }
533
534 #[test]
535 fn from_u8_array_ref() {
536 let code: FourCC = b"ABCD".into();
537 assert_eq!(code, FourCC::new(0x41424344));
538 }
539}
540
541#[cfg(all(test, feature = "binrw"))]
542mod feature_binrw {
543 use binrw::BinReaderExt as _;
544 use std::io;
545
546 use crate::FourCC;
547
548 #[test]
549 fn read() {
550 let mut reader = io::Cursor::new(b"\x41\x42\x43\x44");
551 let fourcc: FourCC = reader.read_be().unwrap();
552 let expected: FourCC = "ABCD".try_into().unwrap();
553
554 assert_eq!(fourcc, expected);
555 }
556}
557
558#[cfg(all(test, feature = "serde"))]
559mod feature_serde {
560 use crate::FourCC;
561
562 #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
563 struct MySample {
564 a_code: FourCC,
565 }
566
567 #[test]
568 fn read() {
569 let sample = MySample {
570 a_code: FourCC::new(0x41424344),
571 };
572 let json = serde_json::to_string(&sample).unwrap();
573 assert_eq!(&json, "{\"a_code\":\"ABCD\"}");
574
575 let restored_sample: MySample = serde_json::from_str(&json).unwrap();
576 assert_eq!(sample, restored_sample);
577 }
578}