use std::error::Error;
use std::fmt;
use std::fmt::Write as _;
use std::str::FromStr;
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct FourCc([u8; 4]);
impl FourCc {
pub const ANY: Self = Self([0x00, 0x00, 0x00, 0x00]);
pub const fn from_bytes(bytes: [u8; 4]) -> Self {
Self(bytes)
}
pub const fn from_u32(value: u32) -> Self {
Self(value.to_be_bytes())
}
pub const fn as_bytes(&self) -> &[u8; 4] {
&self.0
}
pub const fn into_bytes(self) -> [u8; 4] {
self.0
}
pub fn matches(self, other: Self) -> bool {
self == Self::ANY || other == Self::ANY || self.0 == other.0
}
fn is_printable(self) -> bool {
self.0.iter().all(|byte| matches!(byte, 0x20..=0x7e | 0xa9))
}
}
impl From<[u8; 4]> for FourCc {
fn from(value: [u8; 4]) -> Self {
Self::from_bytes(value)
}
}
impl From<u32> for FourCc {
fn from(value: u32) -> Self {
Self::from_u32(value)
}
}
impl TryFrom<&str> for FourCc {
type Error = ParseFourCcError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let bytes = value.as_bytes();
if bytes.len() != 4 {
return Err(ParseFourCcError { len: bytes.len() });
}
Ok(Self([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
}
impl FromStr for FourCc {
type Err = ParseFourCcError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s)
}
}
impl fmt::Display for FourCc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_printable() {
for byte in self.0 {
if byte == 0xa9 {
f.write_str("(c)")?;
} else {
f.write_char(char::from(byte))?;
}
}
return Ok(());
}
write!(
f,
"0x{:02x}{:02x}{:02x}{:02x}",
self.0[0], self.0[1], self.0[2], self.0[3]
)
}
}
impl fmt::Debug for FourCc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FourCc(\"{self}\")")
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for FourCc {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.serde_string())
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for FourCc {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = <String as serde::Deserialize>::deserialize(deserializer)?;
Self::from_serde_str(&value).map_err(serde::de::Error::custom)
}
}
#[cfg(feature = "serde")]
impl FourCc {
fn serde_string(self) -> String {
if self.0.iter().all(|byte| matches!(byte, 0x20..=0x7e)) {
self.0.iter().map(|byte| char::from(*byte)).collect()
} else {
format!(
"0x{:02x}{:02x}{:02x}{:02x}",
self.0[0], self.0[1], self.0[2], self.0[3]
)
}
}
fn from_serde_str(value: &str) -> Result<Self, String> {
if let Some(hex) = value
.strip_prefix("0x")
.or_else(|| value.strip_prefix("0X"))
{
if hex.len() != 8 {
return Err(format!(
"hex fourcc values must contain exactly 8 digits, got {}",
hex.len()
));
}
let parsed = u32::from_str_radix(hex, 16)
.map_err(|error| format!("invalid hex fourcc value: {error}"))?;
return Ok(Self::from_u32(parsed));
}
Self::try_from(value).map_err(|error| error.to_string())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ParseFourCcError {
len: usize,
}
impl ParseFourCcError {
pub const fn len(&self) -> usize {
self.len
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
}
impl fmt::Display for ParseFourCcError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "fourcc values must be exactly 4 bytes, got {}", self.len)
}
}
impl Error for ParseFourCcError {}