four_char_code_macros_impl/
lib.rs

1extern crate proc_macro;
2extern crate syn;
3
4use std::{cmp::Ordering, str::FromStr};
5
6use proc_macro::TokenStream;
7use proc_macro_hack::proc_macro_hack;
8use syn::{parse_macro_input, LitStr};
9
10#[proc_macro_hack]
11pub fn four_char_code(input: TokenStream) -> TokenStream {
12    let input = &parse_macro_input!(input as LitStr);
13    let mut bytes = {
14        let value = input.value();
15
16        match value.len().cmp(&4) {
17            Ordering::Less => {
18                return syn::Error::new_spanned(input, "four char code is too short")
19                    .into_compile_error()
20                    .into();
21            }
22            Ordering::Greater => {
23                return syn::Error::new_spanned(input, "four char code is too long")
24                    .into_compile_error()
25                    .into();
26            }
27            _ => (),
28        }
29        let value = value.as_bytes();
30        unsafe {
31            [
32                *value.get_unchecked(0),
33                *value.get_unchecked(1),
34                *value.get_unchecked(2),
35                *value.get_unchecked(3),
36            ]
37        }
38    };
39
40    {
41        let mut null_streak = true;
42
43        let mut i = 3usize;
44        loop {
45            let mut c = bytes[i];
46            if c == 0 {
47                if null_streak {
48                    c = 0x20;
49                    bytes[i] = c;
50                } else {
51                    return syn::Error::new_spanned(input, "invalid char in four char code")
52                        .into_compile_error()
53                        .into();
54                }
55            } else {
56                null_streak = false;
57            }
58
59            if c <= b'\x1f' || c >= b'\x7f' {
60                return syn::Error::new_spanned(input, "invalid char in four char code")
61                    .into_compile_error()
62                    .into();
63            }
64
65            if i == 0 {
66                break;
67            }
68            i -= 1;
69        }
70    }
71
72    proc_macro::TokenStream::from_str(
73        &proc_macro::Literal::u32_suffixed(u32::from_be_bytes(bytes)).to_string(),
74    )
75    .unwrap()
76}