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::fmt;
104use core::fmt::Write;
105use core::result::Result;
106
107#[derive(Clone, Copy, PartialEq, Eq, Hash)]
111#[cfg_attr(feature = "zerocopy", derive(zerocopy::FromBytes, zerocopy::AsBytes))]
112#[repr(C, packed)]
113pub struct FourCC(pub [u8; 4]);
114impl FourCC {
115 const fn from_u32(self: Self) -> u32 {
116 ((self.0[0] as u32) << 24 & 0xff000000)
117 | ((self.0[1] as u32) << 16 & 0x00ff0000)
118 | ((self.0[2] as u32) << 8 & 0x0000ff00)
119 | ((self.0[3] as u32) & 0x000000ff)
120 }
121}
122impl<'a> From<&'a [u8; 4]> for FourCC {
123 fn from(buf: &[u8; 4]) -> FourCC {
124 FourCC([buf[0], buf[1], buf[2], buf[3]])
125 }
126}
127impl<'a> From<&'a [u8]> for FourCC {
128 fn from(buf: &[u8]) -> FourCC {
129 FourCC([buf[0], buf[1], buf[2], buf[3]])
130 }
131}
132impl From<u32> for FourCC {
133 fn from(val: u32) -> FourCC {
134 FourCC([
135 (val >> 24 & 0xff) as u8,
136 (val >> 16 & 0xff) as u8,
137 (val >> 8 & 0xff) as u8,
138 (val & 0xff) as u8,
139 ])
140 }
141}
142#[cfg(not(feature = "nightly"))]
144macro_rules! from_fourcc_for_u32 {
145 () => {
146 impl From<FourCC> for u32 {
147 fn from(val: FourCC) -> Self {
148 val.from_u32()
149 }
150 }
151 };
152}
153#[cfg(feature = "nightly")]
154macro_rules! from_fourcc_for_u32 {
155 ($($t:tt)*) => {
156 impl const From<FourCC> for u32 {
157 fn from(val: FourCC) -> Self {
158 val.from_u32()
159 }
160 }
161 };
162}
163from_fourcc_for_u32!();
164
165impl fmt::Display for FourCC {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
167 let b = &self.0;
168 let iter = core::ascii::escape_default(b[0])
169 .chain(core::ascii::escape_default(b[1]))
170 .chain(core::ascii::escape_default(b[2]))
171 .chain(core::ascii::escape_default(b[3]));
172 for c in iter {
173 f.write_char(c as char)?;
174 }
175 Ok(())
176 }
177}
178
179impl fmt::Debug for FourCC {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
181 f.debug_tuple("FourCC")
182 .field(&format_args!("{}", self))
183 .finish()
184 }
185}
186
187#[cfg(feature = "schemars")]
188impl schemars::JsonSchema for FourCC {
189 fn schema_name() -> String {
190 "FourCC".to_string()
191 }
192 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
193 gen.subschema_for::<&str>()
194 }
195 fn is_referenceable() -> bool {
196 false
197 }
198}
199
200#[cfg(feature = "serde")]
201impl serde::ser::Serialize for FourCC {
202 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
203 serializer.collect_str(self)
204 }
205}
206
207#[cfg(feature = "serde")]
208struct FromStrVisitor<T> {
209 expecting: &'static str,
210 ty: core::marker::PhantomData<T>,
211}
212
213#[cfg(feature = "serde")]
214impl<T> FromStrVisitor<T> {
215 fn new(expecting: &'static str) -> Self {
216 FromStrVisitor {
217 expecting: expecting,
218 ty: core::marker::PhantomData,
219 }
220 }
221}
222
223#[cfg(feature = "serde")]
224impl core::str::FromStr for FourCC {
225 type Err = u32;
226 fn from_str(s: &str) -> Result<Self, Self::Err> {
227 Ok(s.as_bytes().into())
228 }
229}
230
231#[cfg(feature = "serde")]
232impl<'de, T> serde::de::Visitor<'de> for FromStrVisitor<T>
233where
234 T: core::str::FromStr,
235 T::Err: fmt::Display,
236{
237 type Value = T;
238
239 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
240 formatter.write_str(self.expecting)
241 }
242
243 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
244 where
245 E: serde::de::Error,
246 {
247 s.parse().map_err(serde::de::Error::custom)
248 }
249}
250
251#[cfg(feature = "serde")]
252impl<'de> serde::de::Deserialize<'de> for FourCC {
253 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
254 deserializer.deserialize_str(FromStrVisitor::new("FourCC"))
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn eq() {
264 assert_eq!(FourCC(*b"uuid"), b"uuid".into());
265 assert_ne!(FourCC(*b"uuid"), b"diuu".into());
266 }
267
268 #[test]
269 fn int_conversions() {
270 let val: u32 = FourCC(*b"ABCD").into();
271 assert_eq!(0x41424344_u32, val);
272 assert_eq!(FourCC(*b"ABCD"), 0x41424344u32.into());
273 }
274
275 #[cfg(feature = "std")]
276 #[test]
277 fn display() {
278 assert_eq!("uuid", format!("{}", FourCC(*b"uuid")));
279 assert_eq!("\\x00uid", format!("{}", FourCC(*b"\x00uid")));
280 }
281
282 #[cfg(feature = "serde")]
283 #[test]
284 fn serialize() {
285 use serde_test::{assert_tokens, Token};
286
287 let code = FourCC(*b"uuid");
288 assert_tokens(&code, &[Token::Str("uuid")]);
289 }
290
291 #[cfg(feature = "serde")]
292 #[test]
293 fn deserialize() {
294 use std::str::FromStr;
295 let data = "uuid";
296 let code = FourCC::from_str(data).unwrap();
297 assert_eq!(code, FourCC(*b"uuid"));
298 }
299
300 #[cfg(feature = "schemars")]
301 #[test]
302 fn schema() {
303 let schema = schemars::schema_for!(FourCC);
304 let expected_type = schemars::schema::InstanceType::String;
305 assert_eq!(
306 schema.schema.instance_type,
307 Some(schemars::schema::SingleOrVec::Single(Box::from(
308 expected_type
309 )))
310 );
311 }
312}