Skip to main content

doom_fish_utils/
four_char_code.rs

1//! Minimal `FourCharCode` implementation for pixel formats and color conversions
2//!
3//! A `FourCharCode` is a 4-byte code used in Core Video and Core Media to identify
4//! pixel formats, codecs, and other media types.
5
6use std::fmt;
7use std::str::FromStr;
8
9/// `FourCharCode` represents a 4-character code (used in Core Video/Media)
10///
11/// # Examples
12///
13/// ```
14/// use doom_fish_utils::FourCharCode;
15///
16/// // Create from string
17/// let code: FourCharCode = "BGRA".parse().unwrap();
18/// assert_eq!(code.display(), "BGRA");
19///
20/// // Create from bytes
21/// let code = FourCharCode::from_bytes(*b"420v");
22/// assert_eq!(code.as_u32(), 0x34323076);
23/// ```
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25pub struct FourCharCode(u32);
26
27impl FourCharCode {
28    /// Create a `FourCharCode` from exactly 4 bytes (infallible)
29    ///
30    /// # Examples
31    ///
32    /// ```
33    /// use doom_fish_utils::FourCharCode;
34    ///
35    /// let code = FourCharCode::from_bytes(*b"BGRA");
36    /// assert_eq!(code.display(), "BGRA");
37    /// ```
38    #[inline]
39    #[must_use]
40    pub const fn from_bytes(bytes: [u8; 4]) -> Self {
41        Self(u32::from_be_bytes(bytes))
42    }
43
44    /// Create a `FourCharCode` from a byte slice
45    #[must_use]
46    pub fn from_slice(bytes: &[u8]) -> Option<Self> {
47        if bytes.len() != 4 {
48            return None;
49        }
50
51        let code = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
52        Some(Self(code))
53    }
54
55    /// Get the u32 representation
56    ///
57    /// # Examples
58    ///
59    /// ```
60    /// use doom_fish_utils::FourCharCode;
61    ///
62    /// let code = FourCharCode::from_bytes(*b"BGRA");
63    /// let value: u32 = code.as_u32();
64    /// assert_eq!(value, 0x42475241);
65    /// ```
66    #[inline]
67    #[must_use]
68    pub const fn as_u32(self) -> u32 {
69        self.0
70    }
71
72    /// Get the bytes as an array
73    #[inline]
74    #[must_use]
75    pub const fn as_bytes(self) -> [u8; 4] {
76        self.0.to_be_bytes()
77    }
78
79    /// Create from a u32 value (const version of From trait)
80    #[inline]
81    #[must_use]
82    pub const fn from_u32(value: u32) -> Self {
83        Self(value)
84    }
85
86    /// Compare with another `FourCharCode` at compile time
87    #[inline]
88    #[must_use]
89    pub const fn equals(self, other: Self) -> bool {
90        self.0 == other.0
91    }
92
93    /// Display the code as a string
94    #[must_use]
95    pub fn display(self) -> String {
96        let bytes = self.0.to_be_bytes();
97        String::from_utf8_lossy(&bytes).to_string()
98    }
99}
100
101impl FromStr for FourCharCode {
102    type Err = &'static str;
103
104    fn from_str(s: &str) -> Result<Self, Self::Err> {
105        if s.len() != 4 {
106            return Err("FourCharCode must be exactly 4 characters");
107        }
108        if !s.is_ascii() {
109            return Err("FourCharCode must contain only ASCII characters");
110        }
111
112        let bytes = s.as_bytes();
113        let code = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
114        Ok(Self(code))
115    }
116}
117
118impl From<u32> for FourCharCode {
119    fn from(value: u32) -> Self {
120        Self(value)
121    }
122}
123
124impl From<FourCharCode> for u32 {
125    fn from(code: FourCharCode) -> Self {
126        code.0
127    }
128}
129
130impl fmt::Display for FourCharCode {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        write!(f, "{}", self.display())
133    }
134}