1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
use proc_macro::TokenStream;
use quote::ToTokens;
use syn::TypePath;
use syn::spanned::Spanned;
use crate::get_global_set;
pub(crate) fn register_help(input: TokenStream) -> TokenStream {
// Parse the input as a comma-separated list of arguments
let input_parsed = syn::parse_macro_input!(input with syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>::parse_terminated);
// Check if there are exactly two elements
if input_parsed.len() != 2 {
return syn::Error::new(
input_parsed.span(),
"Expected exactly two comma-separated arguments: `EntryType, StructName`",
)
.to_compile_error()
.into();
}
// Extract the two elements
let entry_type_expr = &input_parsed[0];
let struct_name_expr = &input_parsed[1];
// Convert expressions to TypePath and Ident
let entry_type = match syn::parse2::<TypePath>(entry_type_expr.to_token_stream()) {
Ok(ty) => ty,
Err(e) => return e.to_compile_error().into(),
};
let struct_name = match syn::parse2::<syn::Ident>(struct_name_expr.to_token_stream()) {
Ok(ident) => ident,
Err(e) => return e.to_compile_error().into(),
};
// Register the help request mapping
let help_entry = build_help_entry(&struct_name, &entry_type);
let entry_str = help_entry.to_string();
// Check if entry was already pre-inserted by `#[help]` attribute
let mut helps = get_global_set(&crate::HELP_REQUESTS).lock().unwrap();
if helps.contains(&entry_str) {
// Already registered by `#[help]`, no duplicate check needed
return quote::quote! {}.into();
}
// Check for duplicate variant (different struct, same type)
let variant_name = entry_type.path.segments.last().unwrap().ident.to_string();
if let Err(err) =
crate::check_duplicate_variant(&helps, &entry_str, &variant_name, "help", entry_type.span())
{
return err.into();
}
helps.insert(entry_str);
quote::quote! {}.into()
}
fn build_help_entry(struct_name: &syn::Ident, entry_type: &TypePath) -> proc_macro2::TokenStream {
let enum_variant = entry_type.path.segments.last().unwrap().ident.clone();
quote::quote! {
Self::#enum_variant => {
// SAFETY: The member_id check ensures that `any` contains a value of type `#entry_type`,
// so downcasting to `#entry_type` is safe.
let value = unsafe { any.downcast::<#entry_type>().unwrap_unchecked() };
<#struct_name as ::mingling::HelpRequest>::render_help(value)
}
}
}
|