four_cc_nokhwa/
lib.rs

1//! Newtype wrapper providing a convenient representation of _four-character-code_ values.
2//!
3//! Using this type in a public APIs as an alternative to simply passing the equivalent `u32`
4//! makes the value's expected use explicit.
5//!
6//!
7//! ## Creating a FourCC value
8//!
9//! ```rust
10//! use four_cc::FourCC;
11//!
12//! let uuid = FourCC(*b"uuid");
13//!
14//! // using Into
15//! let code: FourCC = b"uuid".into();
16//! assert_eq!(uuid, code);
17//! ```
18//!
19//! ## From a slice
20//!
21//! ```rust
22//! # use four_cc::FourCC;
23//! let data = b"moofftyp";
24//! let code = FourCC::from(&data[0..4]);  // would panic if fewer than 4 bytes
25//! assert_eq!(FourCC(*b"moof"), code);
26//! ```
27//!
28//! ## From a u32
29//!
30//! ```rust
31//! # use four_cc::FourCC;
32//! let data: u32 = 0x6d6f6f66;
33//! let code = FourCC::from(data);
34//! assert_eq!(FourCC(*b"moof"), code);
35//! // conversion back into a u32
36//! let converted: u32 = code.into();
37//! assert_eq!(data, converted);
38//! ```
39//!
40//! ## Constants
41//!
42//! FourCC values can be used in const expressions
43//!
44//! ```rust
45//! # use four_cc::FourCC;
46//! const UUID: FourCC = FourCC(*b"uuid");
47//! ```
48//!
49//! ## Matching
50//!
51//! You can use FourCC values in match patterns as long as you define constants to match against,
52//!
53//! ```rust
54//! # use four_cc::FourCC;
55//! const UUID: FourCC = FourCC(*b"uuid");
56//! const MOOV: FourCC = FourCC(*b"moov");
57//! # let other_value = UUID;
58//! match other_value {
59//!     MOOV => println!("movie"),
60//!     UUID => println!("unique identifier"),
61//!     // compiler will not accept: FourCC(*b"trun") => println!("track fragment run"),
62//!     _ => println!("Other value; scary stuff")
63//! }
64//! ```
65//!
66//! ## Invalid literal values
67//!
68//! If the literal has other than four bytes, compilation will fail
69//!
70//! ```compile_fail
71//! # use four_cc::FourCC;
72//! let bad_fourcc = FourCC(*b"uuid123");
73//! // -> expected an array with a fixed size of 4 elements, found one with 7 elements
74//! ```
75//! **Note** the FourCC value _may_ contain non-printable byte values, including the byte-value zero.
76//!
77//! ## Debug display
78//!
79//! ```rust
80//! # use four_cc::FourCC;
81//! # use std::fmt::Debug;
82//! let uuid = FourCC(*b"uuid");
83//! # assert_eq!("FourCC(uuid)", format!("{:?}", &uuid));
84//! println!("it's {:?}", uuid);  // produces: it's FourCC{uuid}
85//! ```
86//!
87//! Note that if the FourCC bytes are not able to be converted to UTF8, then a fallback
88//! representation will be used (as it would be surprising for `format!()` to panic).
89//!
90//! ```rust
91//! # use four_cc::FourCC;
92//! # use std::fmt::Debug;
93//! let uuid = FourCC(*b"u\xFFi\0");
94//! # assert_eq!("FourCC(u\\xffi\\x00)", format!("{:?}", &uuid));
95//! println!("it's {:?}", uuid);  // produces: it's FourCC{u\xffi\x00}
96//! ```
97
98#![forbid(unsafe_code)]
99#![deny(rust_2018_idioms, future_incompatible, missing_docs)]
100#![cfg_attr(feature = "nightly", feature(const_trait_impl))]
101#![cfg_attr(not(feature = "std"), no_std)]
102
103use core::cmp::Ordering;
104use core::fmt;
105use core::fmt::Write;
106use core::result::Result;
107use core::str::FromStr;
108
109/// A _four-character-code_ value.
110///
111/// See the [module level documentation](index.html).
112#[derive(Clone, Copy, PartialEq, Eq, Hash)]
113#[cfg_attr(feature = "zerocopy", derive(zerocopy::FromBytes, zerocopy::AsBytes))]
114#[repr(C, packed)]
115pub struct FourCC(pub [u8; 4]);
116impl FourCC {
117    const fn from_u32(self: Self) -> u32 {
118        ((self.0[0] as u32) << 24 & 0xff000000)
119            | ((self.0[1] as u32) << 16 & 0x00ff0000)
120            | ((self.0[2] as u32) << 8 & 0x0000ff00)
121            | ((self.0[3] as u32) & 0x000000ff)
122    }
123}
124impl<'a> From<&'a [u8; 4]> for FourCC {
125    fn from(buf: &[u8; 4]) -> FourCC {
126        FourCC([buf[0], buf[1], buf[2], buf[3]])
127    }
128}
129impl<'a> From<&'a [u8]> for FourCC {
130    fn from(buf: &[u8]) -> FourCC {
131        FourCC([buf[0], buf[1], buf[2], buf[3]])
132    }
133}
134impl From<u32> for FourCC {
135    fn from(val: u32) -> FourCC {
136        FourCC([
137            (val >> 24 & 0xff) as u8,
138            (val >> 16 & 0xff) as u8,
139            (val >> 8 & 0xff) as u8,
140            (val & 0xff) as u8,
141        ])
142    }
143}
144impl PartialOrd for FourCC {
145    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
146        // Implement comparison logic here, possibly using the inner FourCC value
147        // For example, if FourCC can be converted to something comparable:
148        self.to_string().partial_cmp(&other.to_string())
149    }
150}
151impl Ord for FourCC {
152    fn cmp(&self, other: &Self) -> Ordering {
153        self.to_string().cmp(&other.to_string())
154    }
155}
156impl FromStr for FourCC {
157    type Err = u32;
158    fn from_str(s: &str) -> Result<Self, Self::Err> {
159        if s.len() != 4 {
160            return Err(s.len() as u32);
161        }
162        let mut buf = [0u8; 4];
163        buf.copy_from_slice(s.as_bytes());
164        Ok(FourCC(buf))
165    }
166}
167
168// The macro is needed, because the `impl const` syntax doesn't exists on `stable`.
169#[cfg(not(feature = "nightly"))]
170macro_rules! from_fourcc_for_u32 {
171    () => {
172        impl From<FourCC> for u32 {
173            fn from(val: FourCC) -> Self {
174                val.from_u32()
175            }
176        }
177    };
178}
179#[cfg(feature = "nightly")]
180macro_rules! from_fourcc_for_u32 {
181    ($($t:tt)*) => {
182        impl const From<FourCC> for u32 {
183            fn from(val: FourCC) -> Self {
184                val.from_u32()
185            }
186        }
187    };
188}
189from_fourcc_for_u32!();
190
191impl fmt::Display for FourCC {
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
193        let b = &self.0;
194        let iter = core::ascii::escape_default(b[0])
195            .chain(core::ascii::escape_default(b[1]))
196            .chain(core::ascii::escape_default(b[2]))
197            .chain(core::ascii::escape_default(b[3]));
198        for c in iter {
199            f.write_char(c as char)?;
200        }
201        Ok(())
202    }
203}
204
205impl fmt::Debug for FourCC {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
207        f.debug_tuple("FourCC")
208            .field(&format_args!("{}", self))
209            .finish()
210    }
211}
212
213#[cfg(feature = "schemars")]
214impl schemars::JsonSchema for FourCC {
215    fn schema_name() -> String {
216        "FourCC".to_string()
217    }
218    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
219        gen.subschema_for::<&str>()
220    }
221    fn is_referenceable() -> bool {
222        false
223    }
224}
225
226#[cfg(feature = "serde")]
227impl serde::ser::Serialize for FourCC {
228    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
229        serializer.collect_str(self)
230    }
231}
232
233#[cfg(feature = "serde")]
234struct FromStrVisitor<T> {
235    expecting: &'static str,
236    ty: core::marker::PhantomData<T>,
237}
238
239#[cfg(feature = "serde")]
240impl<T> FromStrVisitor<T> {
241    fn new(expecting: &'static str) -> Self {
242        FromStrVisitor {
243            expecting: expecting,
244            ty: core::marker::PhantomData,
245        }
246    }
247}
248
249#[cfg(feature = "serde")]
250impl<'de, T> serde::de::Visitor<'de> for FromStrVisitor<T>
251where
252    T: core::str::FromStr,
253    T::Err: fmt::Display,
254{
255    type Value = T;
256
257    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
258        formatter.write_str(self.expecting)
259    }
260
261    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
262    where
263        E: serde::de::Error,
264    {
265        s.parse().map_err(serde::de::Error::custom)
266    }
267}
268
269#[cfg(feature = "serde")]
270impl<'de> serde::de::Deserialize<'de> for FourCC {
271    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
272        deserializer.deserialize_str(FromStrVisitor::new("FourCC"))
273    }
274}
275
276#[cfg(test)]
277mod tests {
278    use super::*;
279
280    #[test]
281    fn eq() {
282        assert_eq!(FourCC(*b"uuid"), b"uuid".into());
283        assert_ne!(FourCC(*b"uuid"), b"diuu".into());
284    }
285
286    #[test]
287    fn int_conversions() {
288        let val: u32 = FourCC(*b"ABCD").into();
289        assert_eq!(0x41424344_u32, val);
290        assert_eq!(FourCC(*b"ABCD"), 0x41424344u32.into());
291    }
292
293    #[cfg(feature = "std")]
294    #[test]
295    fn display() {
296        assert_eq!("uuid", format!("{}", FourCC(*b"uuid")));
297        assert_eq!("\\x00uid", format!("{}", FourCC(*b"\x00uid")));
298    }
299
300    #[cfg(feature = "serde")]
301    #[test]
302    fn serialize() {
303        use serde_test::{assert_tokens, Token};
304
305        let code = FourCC(*b"uuid");
306        assert_tokens(&code, &[Token::Str("uuid")]);
307    }
308
309    #[cfg(feature = "serde")]
310    #[test]
311    fn deserialize() {
312        use std::str::FromStr;
313        let data = "uuid";
314        let code = FourCC::from_str(data).unwrap();
315        assert_eq!(code, FourCC(*b"uuid"));
316    }
317
318    #[cfg(feature = "schemars")]
319    #[test]
320    fn schema() {
321        let schema = schemars::schema_for!(FourCC);
322        let expected_type = schemars::schema::InstanceType::String;
323        assert_eq!(
324            schema.schema.instance_type,
325            Some(schemars::schema::SingleOrVec::Single(Box::from(
326                expected_type
327            )))
328        );
329    }
330}