Skip to main content

fourcc_rs_macros/
lib.rs

1//! Macro implementations for the FourCC crate. See [fourcc-rs](../fourcc-rs) for more details.
2extern crate proc_macro2;
3use quote::quote;
4use syn::Lit;
5
6#[proc_macro]
7/// Macro to create `fourcc::FourCC`s from literals at compile time.
8///
9/// The macro accepts a single argument of a _string_ literal, _byte string_, or _integer_. Other types are rejected. It will also emit a compilation error if the given value is out of
10/// range or contains invalid characters.
11pub 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}