1extern crate proc_macro2;
3use quote::quote;
4use syn::Lit;
5
6#[proc_macro]
7pub fn fourcc(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
12 if let Ok(literal) = syn::parse::<Lit>(item) {
13 match literal {
14 Lit::Bool(_) => {
15 return quote! { compile_error!("Can not create FourCC code from bool") }.into();
16 }
17 Lit::Byte(_) => {
18 return quote! { compile_error!("Can not create FourCC code from a single byte") }
19 .into();
20 }
21 Lit::Char(_) => {
22 return quote! { compile_error!("Can not create FourCC code from a single char") }
23 .into();
24 }
25 Lit::Float(_) => {
26 return quote! { compile_error!("Can not create FourCC code from a float") }.into();
27 }
28 Lit::Str(lit_str) => {
29 let value = lit_str.value();
30 match value.len() {
31 0..4 => {
32 let error = format!("String {value:?} is too short for FourCC");
33 return quote! { compile_error!(#error) }.into();
34 }
35 4 => {
36 let mut fourcc: u32 = 0;
37 for (idx, char) in value.chars().enumerate() {
38 if !char.is_ascii() {
39 return quote! { compile_error!("Character '{}' at index {} can not be converted to ASCII for FourCC", char, idx) }.into();
40 };
41 let mut buf = vec![0u8];
42 char.encode_utf8(&mut buf);
43 fourcc |= (buf[0] as u32) << (24 - 8 * idx);
44 }
45 return quote! { ::fourcc::FourCC(#fourcc) }.into();
46 }
47 5.. => {
48 return quote! { compile_error!("String is too long for FourCC") }.into();
49 }
50 }
51 }
52 Lit::ByteStr(lit_byte_str) => {
53 let value = lit_byte_str.value();
54 match value.len() {
55 0..4 => {
56 return quote! { compile_error!("Byte string is too short for FourCC") }
57 .into();
58 }
59 4 => {
60 let fourcc: u32 = comb_u8(value[0], value[1], value[2], value[3]);
61 return quote! { ::fourcc::FourCC(#fourcc) }.into();
62 }
63 5.. => {
64 return quote! { compile_error!("String is too long for FourCC") }.into();
65 }
66 }
67 }
68
69 Lit::Int(lit) => match lit.base10_parse::<u32>() {
70 Ok(v) => {
71 return quote! { ::fourcc::FourCC(#v) }.into();
72 }
73 Err(_) => {
74 return quote! { compile_error!("FourCC can't be built from unparseable int") }
75 .into();
76 }
77 },
78
79 Lit::CStr(_) => {
80 return quote! { compile_error!("CStr can not be converted to FourCC code") }
81 .into();
82 }
83
84 Lit::Verbatim(_) | _ => {
85 return quote! { compile_error!("Verbatim can not be converted to FourCC code") }
86 .into();
87 }
88 }
89 }
90
91 quote! { compile_error!("Can not convert to FourCC code") }.into()
92}
93
94#[inline]
95const fn comb_u8(v1: u8, v2: u8, v3: u8, v4: u8) -> u32 {
96 ((v1 as u32) << 24) | ((v2 as u32) << 16) | ((v3 as u32) << 8) | (v4 as u32)
97}