1#![doc = include_str!("../README.md")]
2
3pub use fourcc_rs_macros::fourcc;
4#[doc(hidden)]
5pub use fourcc_rs_macros::fourcc_rexport;
6use std::{char, fmt, primitive};
7
8#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
9#[cfg_attr(feature = "binrw", derive(binrw::BinRead, binrw::BinWrite))]
10#[cfg_attr(feature = "binrw", br(big))]
11pub struct FourCC(#[doc(hidden)] pub u32);
17
18impl FourCC {
19 pub const fn new(value: u32) -> Self {
21 Self(value)
22 }
23
24 fn components(&self) -> [u8; 4] {
26 [
27 ((self.0 >> 24) & 0xff) as u8,
28 ((self.0 >> 16) & 0xff) as u8,
29 ((self.0 >> 8) & 0xff) as u8,
30 (self.0 & 0xff) as u8,
31 ]
32 }
33
34 pub fn value(&self) -> u32 {
36 self.0
37 }
38
39 fn chars(&self) -> [primitive::char; 4] {
44 self.components()
45 .map(|f| char::from_u32(f as u32).unwrap_or(char::from_u32(0xFFFD).unwrap()))
46 }
47
48 pub fn name(&self) -> String {
52 self.chars().into_iter().collect::<String>()
53 }
54}
55
56#[cfg(feature = "serde")]
57impl serde::Serialize for FourCC {
58 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
59 where
60 S: serde::Serializer,
61 {
62 serializer.collect_str(&self.name())
63 }
64}
65
66#[cfg(feature = "serde")]
67impl<'de> serde::Deserialize<'de> for FourCC {
68 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
69 where
70 D: serde::Deserializer<'de>,
71 {
72 struct FourCCVisitor {}
73 impl<'de> serde::de::Visitor<'de> for FourCCVisitor {
74 type Value = FourCC;
75
76 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
77 where
78 E: serde::de::Error,
79 {
80 Err(serde::de::Error::invalid_type(
81 serde::de::Unexpected::Bool(v),
82 &self,
83 ))
84 }
85
86 fn visit_i8<E>(self, v: i8) -> Result<Self::Value, E>
87 where
88 E: serde::de::Error,
89 {
90 self.visit_i64(v as i64)
91 }
92
93 fn visit_i16<E>(self, v: i16) -> Result<Self::Value, E>
94 where
95 E: serde::de::Error,
96 {
97 self.visit_i64(v as i64)
98 }
99
100 fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
101 where
102 E: serde::de::Error,
103 {
104 self.visit_i64(v as i64)
105 }
106
107 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
108 where
109 E: serde::de::Error,
110 {
111 Err(serde::de::Error::invalid_type(
112 serde::de::Unexpected::Signed(v),
113 &self,
114 ))
115 }
116
117 fn visit_i128<E>(self, v: i128) -> Result<Self::Value, E>
118 where
119 E: serde::de::Error,
120 {
121 let mut writer = String::new();
122 fmt::Write::write_fmt(&mut writer, format_args!("integer `{v}` as i128")).unwrap();
123 Err(serde::de::Error::invalid_type(
124 serde::de::Unexpected::Other(writer.as_str()),
125 &self,
126 ))
127 }
128
129 fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
130 where
131 E: serde::de::Error,
132 {
133 self.visit_u64(v as u64)
134 }
135
136 fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
137 where
138 E: serde::de::Error,
139 {
140 self.visit_u64(v as u64)
141 }
142
143 fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
144 where
145 E: serde::de::Error,
146 {
147 self.visit_u64(v as u64)
148 }
149
150 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
151 where
152 E: serde::de::Error,
153 {
154 Err(serde::de::Error::invalid_type(
155 serde::de::Unexpected::Unsigned(v),
156 &self,
157 ))
158 }
159
160 fn visit_u128<E>(self, v: u128) -> Result<Self::Value, E>
161 where
162 E: serde::de::Error,
163 {
164 let mut writer = String::new();
165 fmt::Write::write_fmt(&mut writer, format_args!("integer `{v}` as u128")).unwrap();
166 Err(serde::de::Error::invalid_type(
167 serde::de::Unexpected::Other(writer.as_str()),
168 &self,
169 ))
170 }
171
172 fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
173 where
174 E: serde::de::Error,
175 {
176 self.visit_f64(v as f64)
177 }
178
179 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
180 where
181 E: serde::de::Error,
182 {
183 Err(serde::de::Error::invalid_type(
184 serde::de::Unexpected::Float(v),
185 &self,
186 ))
187 }
188
189 fn visit_char<E>(self, v: char) -> Result<Self::Value, E>
190 where
191 E: serde::de::Error,
192 {
193 self.visit_str(v.encode_utf8(&mut [0u8; 4]))
194 }
195
196 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
197 where
198 E: serde::de::Error,
199 {
200 FourCC::try_from(v).map_err(|_| {
201 serde::de::Error::invalid_type(serde::de::Unexpected::Str(v), &self)
202 })
203 }
204
205 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
206 where
207 E: serde::de::Error,
208 {
209 self.visit_str(v)
210 }
211
212 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
213 where
214 E: serde::de::Error,
215 {
216 self.visit_str(&v)
217 }
218
219 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
220 where
221 E: serde::de::Error,
222 {
223 Err(serde::de::Error::invalid_type(
224 serde::de::Unexpected::Bytes(v),
225 &self,
226 ))
227 }
228
229 fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
230 where
231 E: serde::de::Error,
232 {
233 self.visit_bytes(v)
234 }
235
236 fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
237 where
238 E: serde::de::Error,
239 {
240 self.visit_bytes(&v)
241 }
242
243 fn visit_none<E>(self) -> Result<Self::Value, E>
244 where
245 E: serde::de::Error,
246 {
247 Err(serde::de::Error::invalid_type(
248 serde::de::Unexpected::Option,
249 &self,
250 ))
251 }
252
253 fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
254 where
255 D: serde::Deserializer<'de>,
256 {
257 let _ = deserializer;
258 Err(serde::de::Error::invalid_type(
259 serde::de::Unexpected::Option,
260 &self,
261 ))
262 }
263
264 fn visit_unit<E>(self) -> Result<Self::Value, E>
265 where
266 E: serde::de::Error,
267 {
268 Err(serde::de::Error::invalid_type(
269 serde::de::Unexpected::Unit,
270 &self,
271 ))
272 }
273
274 fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
275 where
276 D: serde::Deserializer<'de>,
277 {
278 let _ = deserializer;
279 Err(serde::de::Error::invalid_type(
280 serde::de::Unexpected::NewtypeStruct,
281 &self,
282 ))
283 }
284
285 fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
286 where
287 A: serde::de::SeqAccess<'de>,
288 {
289 let _ = seq;
290 Err(serde::de::Error::invalid_type(
291 serde::de::Unexpected::Seq,
292 &self,
293 ))
294 }
295
296 fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
297 where
298 A: serde::de::MapAccess<'de>,
299 {
300 let _ = map;
301 Err(serde::de::Error::invalid_type(
302 serde::de::Unexpected::Map,
303 &self,
304 ))
305 }
306
307 fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
308 where
309 A: serde::de::EnumAccess<'de>,
310 {
311 let _ = data;
312 Err(serde::de::Error::invalid_type(
313 serde::de::Unexpected::Enum,
314 &self,
315 ))
316 }
317
318 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
319 f.write_str("string of four ASCII characters")
320 }
321 }
322 deserializer.deserialize_str(FourCCVisitor {})
323 }
324}
325
326impl fmt::Display for FourCC {
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 let [c1, c2, c3, c4] = self.chars();
329 format!("'{c1}{c2}{c3}{c4}'").fmt(f)
330 }
331}
332
333impl fmt::Debug for FourCC {
334 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335 let [c1, c2, c3, c4] = self.chars();
336 format!("'{c1}{c2}{c3}{c4}'").fmt(f)
337 }
338}
339
340impl From<FourCC> for u32 {
341 fn from(val: FourCC) -> Self {
342 val.0
343 }
344}
345
346impl From<u32> for FourCC {
347 fn from(value: u32) -> Self {
348 Self(value)
349 }
350}
351
352#[derive(Debug, PartialEq, Eq)]
353pub enum Error {
364 InvalidInputLength,
366 InvalidInputChar,
368}
369
370impl TryFrom<String> for FourCC {
371 type Error = Error;
372
373 fn try_from(value: String) -> Result<Self, Self::Error> {
374 if value.len() != 4 {
375 return Err(Error::InvalidInputLength);
376 }
377
378 if !value.is_ascii() {
379 return Err(Error::InvalidInputChar);
380 };
381
382 Ok(Self(value.chars().enumerate().fold(
383 0u32,
384 |acc: u32, (idx, value): (usize, char)| -> u32 {
385 let mut buf = vec![0u8];
386 value.encode_utf8(&mut buf);
387
388 acc | ((buf[0] as u32) << (24 - 8 * idx))
389 },
390 )))
391 }
392}
393
394impl TryFrom<&str> for FourCC {
395 type Error = Error;
396
397 fn try_from(value: &str) -> Result<Self, Self::Error> {
398 if value.len() != 4 {
399 return Err(Error::InvalidInputLength);
400 }
401
402 if !value.is_ascii() {
403 return Err(Error::InvalidInputChar);
404 };
405
406 fn to_u8(c: char) -> u8 {
407 let mut buf = vec![0u8];
408 c.encode_utf8(&mut buf);
409 buf[0]
410 }
411
412 let chars: Vec<_> = value.chars().collect();
413
414 Ok(Self(u32_from_u8s(
415 to_u8(chars[0]),
416 to_u8(chars[1]),
417 to_u8(chars[2]),
418 to_u8(chars[3]),
419 )))
420 }
421}
422
423impl From<[u8; 4]> for FourCC {
424 fn from(value: [u8; 4]) -> Self {
425 Self(u32_from_u8s(value[0], value[1], value[2], value[3]))
426 }
427}
428
429impl From<&[u8; 4]> for FourCC {
430 fn from(value: &[u8; 4]) -> Self {
431 Self(u32_from_u8s(value[0], value[1], value[2], value[3]))
432 }
433}
434
435#[inline]
436const fn u32_from_u8s(v1: u8, v2: u8, v3: u8, v4: u8) -> u32 {
437 ((v1 as u32) << 24) | ((v2 as u32) << 16) | ((v3 as u32) << 8) | (v4 as u32)
438}
439
440#[cfg(test)]
441mod test {
442 use crate::{Error, FourCC};
443
444 #[test]
445 fn creation() {
446 assert_eq!(FourCC::new(0), FourCC(0));
447 assert_eq!(FourCC::new(0xabcdef01), FourCC(0xabcdef01));
448 }
449
450 #[test]
451 fn display() {
452 assert_eq!(&format!("{}", FourCC::new(0x41424344)), "'ABCD'");
453 }
454
455 #[test]
456 fn display_options() {
457 assert_eq!(&format!("{:8}", FourCC::new(0x41424344)), "'ABCD' ");
458 assert_eq!(&format!("{:8}", FourCC::new(0x00000000)), "'\0\0\0\0' ");
459 assert_eq!(&format!("{:8}", FourCC::new(0x00010000)), "'\0\u{1}\0\0' ");
460 }
461
462 #[test]
463 fn debug() {
464 assert_eq!(&format!("{:?}", FourCC::new(0x41424344)), "\"'ABCD'\"");
465 }
466
467 #[test]
468 fn components() {
469 let code = FourCC::new(0xabcdef01);
470 assert_eq!(code.components(), [0xab, 0xcd, 0xef, 0x01]);
471 }
472
473 #[test]
474 fn chars() {
475 let code: FourCC = b"ABCD".into();
476 assert_eq!(code.chars(), ['A', 'B', 'C', 'D']);
477 }
478
479 #[test]
480 fn name() {
481 let code: FourCC = b"ABCD".into();
482 assert_eq!(code.name(), "ABCD".to_string());
483 }
484
485 #[test]
486 fn from_u32() {
487 let code: FourCC = FourCC::from(0xabcdef01);
488 assert_eq!(code, FourCC(0xabcdef01));
489 }
490
491 #[test]
492 fn into_u32() {
493 let code: FourCC = FourCC::new(0xabcdef01);
494 let code: u32 = code.into();
495 assert_eq!(code, 0xabcdef01u32);
496 }
497
498 #[test]
499 fn try_from_string() {
500 let code: FourCC = String::from("ABCD").try_into().unwrap();
501 assert_eq!(code, FourCC::new(0x41424344));
502
503 assert!(matches!(
504 String::from("ABCDE").try_into() as Result<FourCC, _>,
505 Err(Error::InvalidInputLength)
506 ));
507
508 assert!(matches!(
509 String::from("ABC").try_into() as Result<FourCC, _>,
510 Err(Error::InvalidInputLength)
511 ));
512
513 assert!(matches!(
514 String::from("AB©").try_into() as Result<FourCC, _>,
515 Err(Error::InvalidInputChar)
516 ));
517 }
518
519 #[test]
520 fn try_from_str() {
521 let code: FourCC = "ABCD".try_into().unwrap();
522 assert_eq!(code, FourCC::new(0x41424344));
523
524 assert!(matches!(
525 "ABCDE".try_into() as Result<FourCC, _>,
526 Err(Error::InvalidInputLength)
527 ));
528
529 assert!(matches!(
530 "ABC".try_into() as Result<FourCC, _>,
531 Err(Error::InvalidInputLength)
532 ));
533
534 assert!(matches!(
535 "AB©".try_into() as Result<FourCC, _>,
536 Err(Error::InvalidInputChar)
537 ));
538 }
539
540 #[test]
541 fn from_u8_array() {
542 let code: FourCC = b"ABCD".to_owned().into();
543 assert_eq!(code, FourCC::new(0x41424344));
544 }
545
546 #[test]
547 fn from_u8_array_ref() {
548 let code: FourCC = b"ABCD".into();
549 assert_eq!(code, FourCC::new(0x41424344));
550 }
551}
552
553#[cfg(all(test, feature = "binrw"))]
554mod feature_binrw {
555 use binrw::BinReaderExt as _;
556 use std::io;
557
558 use crate::FourCC;
559
560 #[test]
561 fn read() {
562 let mut reader = io::Cursor::new(b"\x41\x42\x43\x44");
563 let fourcc: FourCC = reader.read_be().unwrap();
564 let expected: FourCC = "ABCD".try_into().unwrap();
565
566 assert_eq!(fourcc, expected);
567 }
568}
569
570#[cfg(all(test, feature = "serde"))]
571mod feature_serde {
572 use crate::FourCC;
573
574 #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
575 struct MySample {
576 a_code: FourCC,
577 }
578
579 #[test]
580 fn read() {
581 let sample = MySample {
582 a_code: FourCC::new(0x41424344),
583 };
584 let json = serde_json::to_string(&sample).unwrap();
585 assert_eq!(&json, "{\"a_code\":\"ABCD\"}");
586
587 let restored_sample: MySample = serde_json::from_str(&json).unwrap();
588 assert_eq!(sample, restored_sample);
589 }
590}