1#![feature(proc_macro_diagnostic)]
18#![feature(downcast_unchecked)]
19#![feature(cfg_eval)]
20#![feature(cfg_match)]
21#![feature(assert_matches)]
22
23mod builder;
24mod enums;
25
26use proc_macro::TokenStream;
27use proc_macro2::TokenStream as TokenStream2;
28use quote::quote;
29use std::any::{type_name, type_name_of_val};
30use syn::spanned::Spanned;
31use syn::{parse_macro_input, Data, DeriveInput, Ident, Path, TypePath};
32
33pub(crate) fn error(span: proc_macro2::Span, message: &str) -> TokenStream2 {
34 syn::Error::new(span, message).into_compile_error()
36}
37
38fn error_input<E, O>(span: proc_macro2::Span, other: O) -> TokenStream2 {
39 error(
40 span,
41 &format!(
42 "Derive macro can only be applied to {}, got {}",
43 type_name::<E>(),
44 type_name_of_val(&other),
45 ),
46 )
47}
48
49#[proc_macro_derive(EnumVariants)]
50pub fn enum_variants(input: TokenStream) -> TokenStream {
51 let input = parse_macro_input!(input as DeriveInput);
52
53 enums::variants::variants(input).into()
54}
55
56#[proc_macro_derive(EnumNames)]
57pub fn enum_names(input: TokenStream) -> TokenStream {
58 let input = parse_macro_input!(input as DeriveInput);
59
60 enums::names::names(input).into()
61}
62
63#[proc_macro_derive(EnumRegex)]
64pub fn enum_regex(input: TokenStream) -> TokenStream {
65 let input = parse_macro_input!(input as DeriveInput);
66
67 enums::regex::regex(input).into()
68}
69
70#[proc_macro_derive(Delegation, attributes(delegate))]
71pub fn delegate_trait(input: TokenStream) -> TokenStream {
72 let input = parse_macro_input!(input as DeriveInput);
74
75 let item_enum = match &input.data {
77 Data::Enum(item_enum) => item_enum,
78 _ => return error(input.span(), "Delegate can only be derived for enums").into(),
79 };
80
81 let enum_name = &input.ident;
83
84 let delegate_attr = input
85 .attrs
86 .iter()
87 .find(|attr| attr.path().is_ident("delegate"))
88 .unwrap_or_else(|| panic!("Missing `delegate` attribute on enum root: {enum_name}"));
89
90 let mut delegate_type = None;
91 delegate_attr
92 .parse_nested_meta(|meta| {
93 if meta.path.is_ident("trait") {
94 let value = meta.value()?;
95 let path: TypePath = value.parse()?;
96 delegate_type = Some(path);
97 Ok(())
98 } else {
99 Err(meta.error("Expected `trait = crate::path::to::class`"))
100 }
101 })
102 .unwrap();
103
104 let mut consts = Vec::new();
106 let mut delegation_arms = Vec::new();
107 for variant in &item_enum.variants {
108 let ident = &variant.ident;
109 let attr = match variant.attrs.iter().find(|a| a.path().is_ident("delegate")) {
110 Some(attr) => attr,
111 None => return error(variant.span(), "Missing `delegate` attribute on variant").into(),
112 };
113
114 let nested = attr.parse_nested_meta(|meta| {
115 if !meta.path.is_ident("path") {
116 return Err(meta.error("Expected `delegate(path = crate::path::to::class)`"));
117 }
118
119 let path = meta.value()?.parse::<Path>()?;
120 let const_ident = Ident::new(&format!("{}_INSTANCE", ident), ident.span());
121 let r#const = quote! {
122 #[allow(non_upper_case_globals)]
123 static #const_ident: _LazyLock<#enum_name::Delegate> = _LazyLock::new(|| Box::new(#path::new()));
124 };
125 let arm = quote! { #enum_name::#ident => &*#const_ident };
126 consts.push(r#const);
127 delegation_arms.push(arm);
128 Ok(())
129 });
130
131 if let Err(err) = nested {
132 return err.into_compile_error().into();
133 }
134 }
135
136 let expanded = quote! {
138 #[automatically_derived]
139 impl #enum_name {
140 #[automatically_derived]
141 pub type Delegate = Box<(dyn #delegate_type)>;
142 }
143
144 const _: () = {
145 use std::sync::LazyLock as _LazyLock;
146 use std::ops::Deref as _Deref;
147
148 #(#consts)*
149
150 #[automatically_derived]
151 impl std::ops::Deref for #enum_name {
152 type Target = #enum_name::Delegate;
153
154 #[automatically_derived]
155 fn deref(&self) -> &Self::Target {
156 match self {
157 #(#delegation_arms),*
158 }
159 }
160 }
161 };
162
163 };
164
165 TokenStream::from(expanded)
167}
168
169#[proc_macro_derive(CommonFields)]
170pub fn conditional_fields_macro(input: TokenStream) -> TokenStream {
171 let input = parse_macro_input!(input as DeriveInput);
172 enums::common_fields::common_fields(input).into()
173}
174
175#[proc_macro_derive(Builder, attributes(builder))]
176pub fn builder_macro(input: TokenStream) -> TokenStream {
177 let input = parse_macro_input!(input as DeriveInput);
178 builder::builder(input)
179}
180
181