commonlibsse_ng_proc_macro_common/
enum_parser.rs

1use proc_macro2::TokenStream;
2use quote::{ToTokens as _, quote};
3use syn::{Attribute, Expr, Lit, Meta};
4
5/// Parses the discriminant value into integer
6pub(crate) fn parse_discriminant(expr: &Expr) -> syn::Result<i64> {
7    match expr {
8        Expr::Lit(lit) => {
9            match &lit.lit {
10                Lit::Int(int) => int.base10_parse::<i64>(),
11                Lit::Byte(byte) => Ok(byte.value() as i64), // e.g. `b'l'`
12                _ => Err(syn::Error::new_spanned(
13                    lit,
14                    format!("Failed to parse as integer: {}", lit.to_token_stream()),
15                )),
16            }
17        }
18        Expr::Binary(binary) => {
19            let left = parse_discriminant(&binary.left)?;
20            let right = parse_discriminant(&binary.right)?;
21
22            match binary.op {
23                syn::BinOp::Add(_) => Ok(left + right), // Addition
24                syn::BinOp::Sub(_) => Ok(left - right), // Subtraction
25                syn::BinOp::Mul(_) => Ok(left * right), // Multiplication
26                syn::BinOp::Div(_) => {
27                    if right == 0 {
28                        Err(syn::Error::new_spanned(
29                            binary,
30                            "Division by zero is not allowed".to_string(),
31                        ))
32                    } else {
33                        Ok(left / right) // Division
34                    }
35                }
36                syn::BinOp::Rem(_) => {
37                    if right == 0 {
38                        Err(syn::Error::new_spanned(
39                            binary,
40                            "Modulo by zero is not allowed".to_string(),
41                        ))
42                    } else {
43                        Ok(left % right) // Modulus (remainder)
44                    }
45                }
46                syn::BinOp::Shl(_) => Ok(left << right), // Left shift
47                syn::BinOp::Shr(_) => Ok(left >> right), // Right shift
48                syn::BinOp::BitAnd(_) => Ok(left & right), // Bitwise AND
49                syn::BinOp::BitOr(_) => Ok(left | right), // Bitwise OR
50                syn::BinOp::BitXor(_) => Ok(left ^ right), // Bitwise XOR
51                _ => Err(syn::Error::new_spanned(
52                    binary,
53                    format!("Unsupported operator: {}", binary.to_token_stream()),
54                )),
55            }
56        }
57        unknown => Err(syn::Error::new_spanned(
58            unknown,
59            format!("Unsupported expression type: {}", unknown.to_token_stream()),
60        )),
61    }
62}
63
64/// Select the appropriate bitflags type based on the `repr` attribute.
65pub(crate) fn select_bitflags_type(repr_attr: &Attribute) -> syn::Result<TokenStream> {
66    let mut repr = quote! { usize };
67    if let Meta::List(meta) = &repr_attr.meta {
68        meta.parse_nested_meta(|nested_meta| {
69            let path = &nested_meta.path;
70
71            if path.is_ident("u32") {
72                repr = quote! { u32 };
73            } else if path.is_ident("i32") {
74                repr = quote! { i32 };
75            } else if path.is_ident("u64") {
76                repr = quote! { u64 };
77            } else if path.is_ident("i64") {
78                repr = quote! { i64 };
79            } else if path.is_ident("u8") {
80                repr = quote! { u8 };
81            } else if path.is_ident("i8") {
82                repr = quote! { i8 };
83            } else if path.is_ident("u16") {
84                repr = quote! { u16 };
85            } else if path.is_ident("i16") {
86                repr = quote! { i16 };
87            } else if path.is_ident("usize") {
88                repr = quote! { usize };
89            } else if path.is_ident("isize") {
90                repr = quote! { isize };
91            } else if path.is_ident("C") {
92                repr = quote! { i32 }; // c_int
93            } else {
94                return Err(syn::Error::new_spanned(
95                    path,
96                    format!(
97                        "Unsupported repr type: {}",
98                        path.get_ident().map(|i| i.to_string()).unwrap_or_default()
99                    ),
100                ));
101            }
102
103            Ok(())
104        })?;
105    }
106
107    Ok(repr)
108}
109
110pub(crate) fn filter_default_attr(attrs: &[Attribute]) -> (Vec<&Attribute>, bool) {
111    let mut not_default = false;
112    let v = attrs
113        .iter()
114        .filter(|attr| {
115            // Remove `#[default]`
116            not_default =
117                if let Meta::Path(path) = &attr.meta { !path.is_ident("default") } else { true };
118            not_default
119        })
120        .collect();
121
122    (v, not_default)
123}
124
125pub(crate) fn filter_repr_default_attr(
126    attrs: &[Attribute],
127) -> (Vec<TokenStream>, Option<&Attribute>) {
128    let mut others_attr = vec![];
129    let mut repr_attr = None;
130
131    for attr in attrs {
132        // Exclude `repr` & `derive(Default)`
133        let path = attr.meta.path();
134
135        if path.is_ident("repr") {
136            repr_attr = Some(attr);
137            continue;
138        }
139
140        if path.is_ident("derive") {
141            let mut derived = vec![];
142            if let Meta::List(meta) = &attr.meta {
143                let _ = meta.parse_nested_meta(|nested_meta| {
144                    let path = nested_meta.path;
145                    if !path.is_ident("Default") {
146                        derived.push(path);
147                    };
148                    Ok(())
149                });
150            }
151            others_attr.push(quote! { #[derive(#(#derived,)*)] });
152            continue;
153        }
154
155        others_attr.push(attr.to_token_stream());
156    }
157
158    (others_attr, repr_attr)
159}