1use derive_more::Deref;
2use std::fmt;
3
4#[derive(Default, Clone, Copy, Deref, PartialEq, Eq)]
5pub struct FourCC(pub(crate) [u8; 4]);
6
7impl FourCC {
8 pub const fn new(typ: &[u8; 4]) -> Self {
9 Self(*typ)
10 }
11
12 pub fn into_bytes(self) -> [u8; 4] {
13 self.0
14 }
15
16 pub fn as_bytes(&self) -> &[u8; 4] {
17 &self.0
18 }
19
20 pub fn as_str(&self) -> &str {
21 std::str::from_utf8(&self.0).unwrap_or("????")
22 }
23}
24
25impl From<[u8; 4]> for FourCC {
26 fn from(value: [u8; 4]) -> Self {
27 FourCC(value)
28 }
29}
30
31impl PartialEq<&[u8; 4]> for FourCC {
32 fn eq(&self, other: &&[u8; 4]) -> bool {
33 &self.0 == *other
34 }
35}
36
37impl PartialEq<[u8; 4]> for FourCC {
38 fn eq(&self, other: &[u8; 4]) -> bool {
39 &self.0 == other
40 }
41}
42
43impl fmt::Display for FourCC {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 let fourcc = std::str::from_utf8(&self.0)
46 .map_or_else(|_| convert_mac_roman_to_utf8(&self.0), ToOwned::to_owned);
47 if fourcc
48 .trim_matches(|c| !char::is_ascii_alphanumeric(&c))
49 .is_empty()
50 {
51 fmt::Debug::fmt(&self.0, f)
53 } else {
54 write!(f, "{fourcc}")
55 }
56 }
57}
58
59impl fmt::Debug for FourCC {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(f, "FourCC({self})")
62 }
63}
64
65fn convert_mac_roman_to_utf8(bytes: &[u8]) -> String {
66 let mut result = String::new();
67 for &byte in bytes {
68 match byte {
69 0xA9 => result.push('©'), 0xAE => result.push('®'), 0x99 => result.push('™'), b if b.is_ascii() => result.push(b as char),
74 _ => result.push('�'),
75 }
76 }
77 result
78}