1#![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#[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 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#[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}