power_of_two_impl/
lib.rs

1//! An implementation of procedural macro to generate compile-time constants for powers of two.
2
3extern crate proc_macro;
4
5use proc_macro::TokenStream;
6use proc_macro2::Span;
7use proc_macro_hack::proc_macro_hack;
8use quote::quote;
9use syn::{
10    parse::{Parse, ParseStream, Result},
11    parse_macro_input, Error, LitInt,
12};
13
14struct Power {
15    power: u32,
16    base_type: syn::Ident,
17}
18
19impl Parse for Power {
20    fn parse(input: ParseStream) -> Result<Self> {
21        let lit: LitInt = input.parse()?;
22        let power = lit.base10_parse::<u32>()?;
23
24        let mut base_type = syn::Ident::new("usize", Span::call_site());
25        if let Some(_comma) = input.parse::<Option<syn::Token![,]>>()? {
26            if let Some(type_) = input.parse::<Option<syn::Ident>>()? {
27                base_type = type_;
28            }
29        }
30
31        Ok(Power { power, base_type })
32    }
33}
34
35/// ```no_run
36/// const POW2: usize = power_of_two!(2);
37/// const POW3: isize = power_of_two!(2, isize);
38/// ```
39#[proc_macro_hack]
40pub fn power_of_two(input: TokenStream) -> TokenStream {
41    // Parse the input tokens into a syntax tree
42    let Power { power, base_type } = parse_macro_input!(input);
43
44    let expanded = if base_type == "usize" {
45        let value = 2usize.pow(power);
46        quote!(#value)
47    } else if base_type == "u8" {
48        let value = 2u8.pow(power);
49        quote!(#value)
50    } else if base_type == "i8" {
51        let value = 2i8.pow(power);
52        quote!(#value)
53    } else if base_type == "u16" {
54        let value = 2u16.pow(power);
55        quote!(#value)
56    } else if base_type == "i16" {
57        let value = 2i16.pow(power);
58        quote!(#value)
59    } else if base_type == "u32" {
60        let value = 2u32.pow(power);
61        quote!(#value)
62    } else if base_type == "i32" {
63        let value = 2i32.pow(power);
64        quote!(#value)
65    } else if base_type == "u128" {
66        let value = 2u128.pow(power);
67        quote!(#value)
68    } else if base_type == "i128" {
69        let value = 2i128.pow(power);
70        quote!(#value)
71    } else if base_type == "usize" {
72        let value = 2usize.pow(power);
73        quote!(#value)
74    } else if base_type == "isize" {
75        let value = 2isize.pow(power);
76        quote!(#value)
77    } else {
78        Error::new_spanned(base_type, "Unknown integer type").to_compile_error()
79    };
80
81    // Hand the output tokens back to the compiler
82    TokenStream::from(expanded)
83}