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