* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This software is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
use heck::ToShoutySnakeCase;
use proc_macro::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Fields};
pub fn derive_enum_consts_impl(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let enum_name = &input.ident;
let variants = match input.data {
Data::Enum(ref data) => &data.variants,
_ => {
return syn::Error::new_spanned(input, "EnumConsts can only be used on enums")
.to_compile_error()
.into();
}
};
let mut repr_type = None;
for attr in &input.attrs {
if attr.path().is_ident("repr") {
let parse_result = attr.parse_nested_meta(|meta| {
if let Some(ident) = meta.path.get_ident() {
let s = ident.to_string();
if matches!(
s.as_str(),
"u8" | "u16"
| "u32"
| "u64"
| "u128"
| "usize"
| "i8"
| "i16"
| "i32"
| "i64"
| "i128"
| "isize"
) {
repr_type = Some(ident.clone());
}
}
Ok(())
});
if let Err(e) = parse_result {
return e.to_compile_error().into();
}
}
}
let const_type = repr_type.map(|t| quote! { #t }).unwrap_or(quote! { isize });
let consts = variants.iter().map(|variant| {
if !matches!(variant.fields, Fields::Unit) {
return syn::Error::new_spanned(
variant,
"EnumConsts only supports unit variants (no fields)",
)
.to_compile_error();
}
let variant_name = &variant.ident;
let const_name = variant_name.to_string().to_shouty_snake_case();
let const_ident = format_ident!("{}", const_name);
let doc_attrs = variant
.attrs
.iter()
.filter(|attr| attr.path().is_ident("doc"));
quote_spanned! {variant.span()=>
#(#doc_attrs)*
pub const #const_ident: #const_type = Self::#variant_name as #const_type;
}
});
let expanded = quote! {
impl #enum_name {
#(#consts)*
}
};
TokenStream::from(expanded)
}