commonlibsse_ng_proc_macro_common\to_bitflags/
mod.rs

1//! generate bitflags from enum
2use core::str::FromStr as _;
3
4use proc_macro2::TokenStream;
5use quote::quote;
6use syn::ItemEnum;
7
8use crate::{
9    enum_parser::{
10        filter_default_attr, filter_repr_default_attr, parse_discriminant, select_bitflags_type,
11    },
12    ffi_enum::attr_args,
13};
14
15pub fn to_bitflags(
16    attrs: TokenStream,
17    item_enum: ItemEnum,
18    crate_root_name: TokenStream,
19) -> TokenStream {
20    let args = {
21        let attr_args = match darling::ast::NestedMeta::parse_meta_list(attrs) {
22            Ok(v) => v,
23            Err(e) => {
24                return darling::Error::from(e).write_errors();
25            }
26        };
27
28        match <attr_args::MacroArgs as darling::FromMeta>::from_list(&attr_args) {
29            Ok(v) => v,
30            Err(e) => {
31                return e.write_errors();
32            }
33        }
34    };
35    to_bitflags_inner(args, item_enum, crate_root_name)
36        .unwrap_or_else(syn::Error::into_compile_error)
37}
38
39fn to_bitflags_inner(
40    _args: attr_args::MacroArgs,
41    item_enum: ItemEnum,
42    crate_root_name: TokenStream,
43) -> syn::Result<TokenStream> {
44    let enum_ident = &item_enum.ident;
45    let vis = &item_enum.vis;
46
47    let (others_attr, repr_attr) = filter_repr_default_attr(&item_enum.attrs);
48    let bitflags_type = match repr_attr {
49        Some(repr_attr) => select_bitflags_type(repr_attr)?,
50        None => quote! { usize },
51    };
52
53    // Generate bitflags and match arms
54    let DiscriminantData { bitflags, default_value, .. } =
55        DiscriminantData::from_item_enum(&item_enum)?;
56    let docs = format!("- size type: [`{bitflags_type}`]");
57
58    let expanded = quote! {
59        #crate_root_name::__private::bitflags::bitflags! {
60            #(#others_attr)*
61            ///
62            #[doc = #docs]
63            #[repr(transparent)]
64            #vis struct #enum_ident: #bitflags_type {
65                #(#bitflags;)*
66            }
67        }
68
69        impl Default for #enum_ident {
70            #[inline]
71            fn default() -> Self {
72                Self::from_bits_retain(#default_value)
73            }
74        }
75    };
76
77    Ok(expanded)
78}
79
80/// Struct to store discriminant information and current value
81pub(crate) struct DiscriminantData {
82    /// .e.g `pub const #var_name: Self = Self(#value)`
83    pub(crate) bitflags: Vec<TokenStream>,
84    #[allow(unused)]
85    pub(crate) default_value: TokenStream,
86}
87
88macro_rules! to_non_suffix_num_token {
89    ($value:expr) => {
90        TokenStream::from_str(&format!("{}", $value)).unwrap()
91    };
92}
93
94/// Generates the discriminants for the enum and prepares the corresponding quote items
95impl DiscriminantData {
96    pub(crate) fn from_item_enum(item_enum: &ItemEnum) -> syn::Result<Self> {
97        let mut current_value = 0;
98        let mut bitflags = Vec::new();
99        let mut default_value = quote! { 0 };
100
101        for variant in &item_enum.variants {
102            let var_name = &variant.ident;
103            let (variant_attrs, found_default) = filter_default_attr(&variant.attrs);
104
105            // If use explicit discriminant, change from current discriminant.
106            let value = if let Some((_, expr)) = &variant.discriminant {
107                let parsed = parse_discriminant(expr)?;
108                current_value = parsed; // Set the current value
109                if found_default {
110                    default_value = quote! { #expr };
111                };
112
113                quote! { #expr }
114            } else {
115                to_non_suffix_num_token!(current_value)
116            };
117
118            // Add bitflags constant
119            bitflags.push(quote! {
120                #(#variant_attrs)*
121
122                #[allow(non_upper_case_globals)]
123                const #var_name = #value
124            });
125
126            current_value += 1;
127        }
128
129        Ok(Self { bitflags, default_value })
130    }
131}