concrete_type/
lib.rs

1#![doc(html_root_url = "https://docs.rs/concrete-type")]
2
3extern crate proc_macro;
4
5use convert_case::{Boundary, Case, Casing};
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::{
9    Attribute, DeriveInput, Expr, Fields, Ident, Lit, Meta, Token,
10    parse::{Parse, ParseStream},
11    parse_macro_input,
12};
13
14#[proc_macro_derive(DeExchange)]
15pub fn de_exchange_derive(input: TokenStream) -> TokenStream {
16    // Parse Rust code abstract syntax tree with Syn from TokenStream -> DeriveInput
17    let ast: DeriveInput =
18        syn::parse(input).expect("de_exchange_derive() failed to parse input TokenStream");
19
20    // Determine execution name
21    let exchange = &ast.ident;
22
23    let generated = quote! {
24        impl<'de> serde::Deserialize<'de> for #exchange {
25            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
26            where
27                D: serde::de::Deserializer<'de>
28            {
29                let input = <String as serde::Deserialize>::deserialize(deserializer)?;
30                let exchange = #exchange::ID;
31                let expected = exchange.as_str();
32
33                if input.as_str() == expected {
34                    Ok(Self::default())
35                } else {
36                    Err(serde::de::Error::invalid_value(
37                        serde::de::Unexpected::Str(input.as_str()),
38                        &expected
39                    ))
40                }
41            }
42        }
43    };
44
45    TokenStream::from(generated)
46}
47
48#[proc_macro_derive(SerExchange)]
49pub fn ser_exchange_derive(input: TokenStream) -> TokenStream {
50    // Parse Rust code abstract syntax tree with Syn from TokenStream -> DeriveInput
51    let ast: DeriveInput =
52        syn::parse(input).expect("ser_exchange_derive() failed to parse input TokenStream");
53
54    // Determine Exchange
55    let exchange = &ast.ident;
56
57    let generated = quote! {
58        impl serde::Serialize for #exchange {
59            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
60            where
61                S: serde::ser::Serializer,
62            {
63                serializer.serialize_str(#exchange::ID.as_str())
64            }
65        }
66    };
67
68    TokenStream::from(generated)
69}
70
71#[proc_macro_derive(DeSubKind)]
72pub fn de_sub_kind_derive(input: TokenStream) -> TokenStream {
73    // Parse Rust code abstract syntax tree with Syn from TokenStream -> DeriveInput
74    let ast: DeriveInput =
75        syn::parse(input).expect("de_sub_kind_derive() failed to parse input TokenStream");
76
77    // Determine SubKind name
78    let sub_kind = &ast.ident;
79
80    let expected_sub_kind = sub_kind
81        .to_string()
82        .from_case(Case::Pascal)
83        .without_boundaries(&Boundary::letter_digit())
84        .to_case(Case::Snake);
85
86    let generated = quote! {
87        impl<'de> serde::Deserialize<'de> for #sub_kind {
88            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
89            where
90                D: serde::de::Deserializer<'de>
91            {
92                let input = <String as serde::Deserialize>::deserialize(deserializer)?;
93
94                if input == #expected_sub_kind {
95                    Ok(Self)
96                } else {
97                    Err(serde::de::Error::invalid_value(
98                        serde::de::Unexpected::Str(input.as_str()),
99                        &#expected_sub_kind
100                    ))
101                }
102            }
103        }
104    };
105
106    TokenStream::from(generated)
107}
108
109#[proc_macro_derive(SerSubKind)]
110pub fn ser_sub_kind_derive(input: TokenStream) -> TokenStream {
111    // Parse Rust code abstract syntax tree with Syn from TokenStream -> DeriveInput
112    let ast: DeriveInput =
113        syn::parse(input).expect("ser_sub_kind_derive() failed to parse input TokenStream");
114
115    // Determine SubKind name
116    let sub_kind = &ast.ident;
117    let sub_kind_string = sub_kind.to_string().to_case(Case::Snake);
118    let sub_kind_str = sub_kind_string.as_str();
119
120    let generated = quote! {
121        impl serde::Serialize for #sub_kind {
122            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
123            where
124                S: serde::ser::Serializer,
125            {
126                serializer.serialize_str(#sub_kind_str)
127            }
128        }
129    };
130
131    TokenStream::from(generated)
132}
133
134/// Helper function to extract concrete type path from an attribute
135fn extract_concrete_type_path(attrs: &[Attribute]) -> Option<syn::Path> {
136    for attr in attrs {
137        if attr.path().is_ident("concrete") {
138            if let Meta::NameValue(meta) = &attr.meta {
139                if let Expr::Lit(expr_lit) = &meta.value {
140                    if let Lit::Str(lit_str) = &expr_lit.lit {
141                        return syn::parse_str::<syn::Path>(&lit_str.value()).ok();
142                    }
143                }
144            }
145        }
146    }
147    None
148}
149
150/// A derive macro that implements the mapping between enum variants and concrete types.
151///
152/// This derive macro is designed for enums where each variant maps to a specific concrete type.
153/// Each variant must be annotated with the `#[concrete = "path::to::Type"]` attribute that
154/// specifies the concrete type that the variant represents.
155///
156/// The macro generates:
157/// 1. A `concrete_type_id` method that returns the `TypeId` of the concrete type for a variant
158/// 2. A `concrete_type_name` method that returns the name of the concrete type as a string
159/// 3. A `with_concrete_type` method that executes a function with knowledge of the concrete type
160/// 4. A macro with the snake_case name of the enum (e.g., `exchange!` for `Exchange`,
161///    `strategy!` for `Strategy`) that can be used to execute code with the concrete type
162///
163/// This enables type-level programming with enums, where you can define enum variants and
164/// map them to concrete type implementations.
165///
166/// # Example
167/// ```
168/// use concrete_type::Concrete;
169/// use std::marker::PhantomData;
170///
171/// // Define concrete types
172/// mod exchanges {
173///     pub struct Binance;
174///     pub struct Okx;
175///
176///     impl Binance {
177///         pub fn new() -> Self { Self }
178///         pub fn name(&self) -> &'static str { "binance" }
179///     }
180///
181///     impl Okx {
182///         pub fn new() -> Self { Self }
183///         pub fn name(&self) -> &'static str { "okx" }
184///     }
185/// }
186///
187/// // Define the exchange enum with concrete type mappings
188/// #[derive(Concrete, Clone, Copy)]
189/// enum Exchange {
190///     #[concrete = "exchanges::Binance"]
191///     Binance,
192///     #[concrete = "exchanges::Okx"]
193///     Okx,
194/// }
195///
196/// // Using the auto-generated exchange! macro:
197/// let exchange = Exchange::Binance;
198/// let name = exchange!(exchange; ExchangeImpl => {
199///     // Inside this block, ExchangeImpl is aliased to exchanges::Binance
200///     let instance = ExchangeImpl::new();
201///     instance.name()
202/// });
203/// assert_eq!(name, "binance");
204///
205/// // Using the Exchange enum's methods directly
206/// let exchange = Exchange::Okx;
207/// // Get the TypeId of the concrete type
208/// let type_id = exchange.concrete_type_id();
209/// // Get the name of the concrete type (as a string)
210/// let type_name = exchange.concrete_type_name();
211/// assert!(type_name.ends_with("exchanges::Okx"));
212/// ```
213#[proc_macro_derive(Concrete, attributes(concrete))]
214pub fn derive_concrete(input: TokenStream) -> TokenStream {
215    // Parse the input tokens into a syntax tree
216    let input = parse_macro_input!(input as DeriveInput);
217
218    // Extract the name of the type
219    let type_name = &input.ident;
220
221    // Create a snake_case version of the type name for the macro_rules! name
222    let type_name_str = type_name.to_string();
223    let macro_name_str = type_name_str.to_case(Case::Snake);
224    let macro_name = syn::Ident::new(&macro_name_str, type_name.span());
225
226    // Check if we're dealing with a struct that has type parameters
227    let is_struct_with_type_params = match &input.data {
228        syn::Data::Struct(_) => !input.generics.params.is_empty(),
229        _ => false,
230    };
231
232    // Handle differently based on whether we're dealing with an enum or a struct with type parameters
233    if is_struct_with_type_params {
234        // The macro name should be 'trading_system' based on the file content, not derived from the struct name
235        let macro_name = syn::Ident::new("trading_system", type_name.span());
236
237        // Generate a macro that accepts multiple arguments and type parameters
238        // This handles syntax like: trading_system!(exchange, strategy; (Exchange, Strategy) => { ... })
239        let trading_system_macro = quote! {
240            /// Generates a macro for working with a struct that has type parameters.
241            /// This macro composes multiple enum-based macros to provide concrete types for generic parameters.
242            ///
243            /// # Example
244            ///
245            /// ```
246            /// use concrete_type::Concrete;
247            /// use std::marker::PhantomData;
248            ///
249            /// // Define the concrete implementation types
250            /// mod exchanges {
251            ///     pub struct Binance;
252            ///     pub struct Okx;
253            /// }
254            ///
255            /// mod strategies {
256            ///     pub struct StrategyA;
257            ///     pub struct StrategyB;
258            ///
259            ///     impl StrategyA {
260            ///         pub fn name() -> &'static str { "strategy_a" }
261            ///     }
262            ///
263            ///     impl StrategyB {
264            ///         pub fn name() -> &'static str { "strategy_b" }
265            ///     }
266            /// }
267            ///
268            /// // Define enums that map to concrete types
269            /// #[derive(Concrete, Clone, Copy)]
270            /// enum Exchange {
271            ///     #[concrete = "exchanges::Binance"]
272            ///     Binance,
273            ///     #[concrete = "exchanges::Okx"]
274            ///     Okx,
275            /// }
276            ///
277            /// #[derive(Concrete)]
278            /// enum Strategy {
279            ///     #[concrete = "strategies::StrategyA"]
280            ///     StrategyA,
281            ///     #[concrete = "strategies::StrategyB"]
282            ///     StrategyB,
283            /// }
284            ///
285            /// // A struct with type parameters that will be resolved at runtime
286            /// #[derive(Concrete)]
287            /// struct TradingSystem<E, S> {
288            ///     phantom: PhantomData<(E, S)>,
289            /// }
290            ///
291            /// // Implement for concrete type combinations
292            /// impl TradingSystem<exchanges::Binance, strategies::StrategyA> {
293            ///     pub fn new() -> Self {
294            ///         Self { phantom: PhantomData }
295            ///     }
296            ///
297            ///     pub fn name(&self) -> &'static str { "binance_strategy_a" }
298            /// }
299            ///
300            /// impl TradingSystem<exchanges::Okx, strategies::StrategyB> {
301            ///     pub fn new() -> Self {
302            ///         Self { phantom: PhantomData }
303            ///     }
304            ///
305            ///     pub fn name(&self) -> &'static str { "okx_strategy_b" }
306            /// }
307            ///
308            /// // Use the trading_system macro
309            /// let exchange = Exchange::Okx;
310            /// let strategy = Strategy::StrategyB;
311            ///
312            /// let name = trading_system!(exchange, strategy; (E, S) => {
313            ///     // Here E is exchanges::Okx and S is strategies::StrategyB
314            ///     TradingSystem::<E, S>::new().name()
315            /// });
316            /// assert_eq!(name, "okx_strategy_b");
317            /// ```
318            #[macro_export]
319            macro_rules! #macro_name {
320                // Match the pattern with two enum instances and two type parameters
321                ($exchange_enum:expr, $strategy_enum:expr; ($exchange_type:ident, $strategy_type:ident) => $code_block:block) => {
322                    exchange!($exchange_enum; $exchange_type => {
323                        strategy!($strategy_enum; $strategy_type => {
324                            $code_block
325                        })
326                    })
327                };
328            }
329        };
330
331        TokenStream::from(trading_system_macro)
332    } else {
333        // Handle enum case (exactly as before)
334        // Ensure we're dealing with an enum
335        let data_enum = match &input.data {
336            syn::Data::Enum(data_enum) => data_enum,
337            _ => {
338                return syn::Error::new_spanned(
339                    type_name,
340                    "Concrete can only be derived for enums or structs with type parameters",
341                )
342                .to_compile_error()
343                .into();
344            }
345        };
346
347        // Extract variant names and their concrete types
348        let mut variant_mappings = Vec::new();
349
350        for variant in &data_enum.variants {
351            let variant_name = &variant.ident;
352
353            // Extract the concrete type path from the variant's attributes
354            if let Some(concrete_type) = extract_concrete_type_path(&variant.attrs) {
355                variant_mappings.push((variant_name, concrete_type));
356            } else {
357                // Variant is missing the #[concrete = "..."] attribute
358                return syn::Error::new_spanned(
359                    variant_name,
360                    format!(
361                        "Enum variant `{}` is missing the #[concrete = \"...\"] attribute",
362                        variant_name
363                    ),
364                )
365                .to_compile_error()
366                .into();
367            }
368        }
369
370        // Generate match arms for the concrete type mapping
371        let match_arms = variant_mappings
372            .iter()
373            .map(|(variant_name, concrete_type)| {
374                quote! {
375                    #type_name::#variant_name => {
376                        type_id::<#concrete_type>()
377                    }
378                }
379            });
380
381        // Generate match arms for the concrete type name
382        let type_name_arms = variant_mappings
383            .iter()
384            .map(|(variant_name, concrete_type)| {
385                quote! {
386                    #type_name::#variant_name => type_name_of::<#concrete_type>()
387                }
388            });
389
390        // Generate match arms for the concrete type aliases
391        let type_alias_arms = variant_mappings
392            .iter()
393            .map(|(variant_name, concrete_type)| {
394                quote! {
395                    #type_name::#variant_name => {
396                        type ConcreteType = #concrete_type;
397                        f()
398                    }
399                }
400            });
401
402        // Generate match arms for the macro_rules! version
403        let macro_match_arms = variant_mappings
404            .iter()
405            .map(|(variant_name, concrete_type)| {
406                quote! {
407                    #type_name::#variant_name => {
408                        type $type_param = #concrete_type;
409                        $code_block
410                    }
411                }
412            });
413
414        // Generate a top-level macro with the snake_case name of the enum
415        // This way it will be directly accessible in the crate
416        let macro_def = quote! {
417            #[macro_export]
418            macro_rules! #macro_name {
419                ($enum_instance:expr; $type_param:ident => $code_block:block) => {
420                    match $enum_instance {
421                        #(#macro_match_arms),*
422                    }
423                };
424            }
425        };
426
427        // Generate the methods implementation
428        let methods_impl = quote! {
429            impl #type_name {
430                /// Returns the TypeId of the concrete type associated with this enum variant
431                pub fn concrete_type_id(&self) -> std::any::TypeId {
432                    use std::any::TypeId;
433
434                    fn type_id<T: 'static>() -> TypeId {
435                        TypeId::of::<T>()
436                    }
437
438                    match self {
439                        #(#match_arms),*
440                    }
441                }
442
443                /// Returns the name of the concrete type associated with this enum variant
444                pub fn concrete_type_name(&self) -> &'static str {
445                    use std::any::type_name;
446
447                    fn type_name_of<T: 'static>() -> &'static str {
448                        type_name::<T>()
449                    }
450
451                    match self {
452                        #(#type_name_arms),*
453                    }
454                }
455
456                /// Executes a function with the concrete type associated with this enum variant
457                pub fn with_concrete_type<F, R>(&self, f: F) -> R
458                where
459                    F: for<'a> Fn() -> R,
460                {
461                    match self {
462                        #(#type_alias_arms),*
463                    }
464                }
465            }
466        };
467
468        // Combine the macro definition and methods implementation
469        let expanded = quote! {
470            // Define the macro outside any module to make it directly accessible
471            #macro_def
472
473            // Implement methods on the enum
474            #methods_impl
475        };
476
477        // Return the generated implementation
478        TokenStream::from(expanded)
479    }
480}
481
482/// Parser for the concrete macro
483/// Format: concrete!(enum_instance; TypeParam => { code_block })
484struct ConcreteMacroInput {
485    enum_instance: Expr,
486    type_param: Ident,
487    code_block: syn::Block,
488}
489
490impl Parse for ConcreteMacroInput {
491    fn parse(input: ParseStream) -> syn::Result<Self> {
492        let enum_instance = input.parse()?;
493        input.parse::<Token![;]>()?;
494        let type_param = input.parse()?;
495        input.parse::<Token![=>]>()?;
496        let code_block = input.parse()?;
497
498        Ok(ConcreteMacroInput {
499            enum_instance,
500            type_param,
501            code_block,
502        })
503    }
504}
505
506/// A procedural macro for executing code with knowledge of the concrete type mapped to an enum variant.
507///
508/// This macro takes an enum instance that has been derived with `#[derive(Concrete)]` and uses
509/// the concrete type information to execute a block of code with a type alias to the concrete type.
510/// This enables type-level programming based on runtime enum values.
511///
512/// # Usage
513/// ```ignore
514/// concrete!(enum_instance; TypeParam => { code_block })
515/// ```
516///
517/// # Parameters
518/// - `enum_instance`: An instance of an enum with `#[derive(Concrete)]`
519/// - `TypeParam`: The name you want to use as the type alias within the code block
520/// - `code_block`: The code to execute with knowledge of the concrete type
521///
522/// # Example
523/// ```
524/// use concrete_type::Concrete;
525/// use std::marker::PhantomData;
526///
527/// // Define concrete types and trait
528/// trait ExchangeApi {
529///     fn new() -> Self;
530///     fn name(&self) -> &'static str;
531/// }
532///
533/// mod exchanges {
534///     pub struct Binance;
535///     impl Binance {
536///         pub fn new() -> Self { Self }
537///         pub fn name(&self) -> &'static str { "binance" }
538///     }
539///
540///     pub struct Okx;
541///     impl Okx {
542///         pub fn new() -> Self { Self }
543///         pub fn name(&self) -> &'static str { "okx" }
544///     }
545/// }
546///
547/// // Define the exchange enum with concrete type mappings
548/// #[derive(Concrete, Clone, Copy)]
549/// enum Exchange {
550///     #[concrete = "exchanges::Binance"]
551///     Binance,
552///     #[concrete = "exchanges::Okx"]
553///     Okx,
554/// }
555///
556/// // This example uses the auto-generated `exchange!` macro instead of the `concrete!` macro
557/// let exchange = Exchange::Binance;
558/// let name = exchange!(exchange; ExchangeImpl => {
559///     // Here, ExchangeImpl is aliased to the concrete type (exchanges::Binance)
560///     let instance = ExchangeImpl::new();
561///     instance.name()
562/// });
563/// assert_eq!(name, "binance");
564///
565/// // You can also use with_concrete_type for a similar effect:
566/// let exchange = Exchange::Okx;
567/// let name = exchange.with_concrete_type(|| {
568///     // Inside this block, ConcreteType is aliased to exchanges::Okx
569///     type ConcreteType = exchanges::Okx;
570///     let instance = ConcreteType::new();
571///     instance.name()
572/// });
573/// assert_eq!(name, "okx");
574/// ```
575///
576/// This allows for executing code that requires the concrete type at compile time,
577/// even though the enum variant is only known at runtime.
578#[proc_macro]
579pub fn concrete(input: TokenStream) -> TokenStream {
580    let ConcreteMacroInput {
581        enum_instance: _,
582        type_param: _,
583        code_block: _,
584    } = parse_macro_input!(input as ConcreteMacroInput);
585
586    // This is a placeholder implementation
587    // In the examples we're using local macro_rules! macros instead
588    let expanded = quote! {
589        compile_error!("This concrete macro is used for documentation purposes only. The examples use a local macro_rules! for concrete instead.")
590    };
591
592    TokenStream::from(expanded)
593}
594
595/// A derive macro that implements the mapping between enum variants with associated data and concrete types.
596///
597/// This derive macro is designed for enums where each variant has associated configuration data and maps to a specific concrete type.
598/// Each variant must be annotated with the `#[concrete = "path::to::Type"]` attribute and contain a single tuple field
599/// that holds the configuration data for that concrete type.
600///
601/// The macro generates:
602/// 1. A `concrete_type_id` method that returns the `TypeId` of the concrete type for a variant
603/// 2. A `concrete_type_name` method that returns the name of the concrete type as a string
604/// 3. A `config` method that returns a reference to the configuration data
605/// 4. A macro with the snake_case name of the enum + "_config" (with "Config" suffix removed if present)
606///    that allows access to both the concrete type and configuration data
607///
608/// This enables type-level programming with configuration data, where enum variants map to concrete type
609/// implementations and carry the configuration needed by those types.
610///
611/// # Example
612/// ```
613/// use concrete_type::ConcreteConfig;
614///
615/// // Define concrete types and configuration types
616/// mod exchanges {
617///     pub trait ExchangeApi {
618///         type Config;
619///         fn new(config: Self::Config) -> Self;
620///         fn name(&self) -> &'static str;
621///     }
622///
623///     pub struct Binance;
624///     pub struct BinanceConfig;
625///
626///     impl ExchangeApi for Binance {
627///         type Config = BinanceConfig;
628///         fn new(_: Self::Config) -> Self { Self }
629///         fn name(&self) -> &'static str { "binance" }
630///     }
631///
632///     pub struct Okx;
633///     pub struct OkxConfig;
634///
635///     impl ExchangeApi for Okx {
636///         type Config = OkxConfig;
637///         fn new(_: Self::Config) -> Self { Self }
638///         fn name(&self) -> &'static str { "okx" }
639///     }
640/// }
641///
642/// // Define the exchange config enum with concrete type mappings and config data
643/// #[derive(ConcreteConfig)]
644/// enum ExchangeConfig {
645///     #[concrete = "exchanges::Binance"]
646///     Binance(exchanges::BinanceConfig),
647///     #[concrete = "exchanges::Okx"]
648///     Okx(exchanges::OkxConfig),
649/// }
650///
651/// // Import the trait for access to its methods
652/// use exchanges::ExchangeApi;
653///
654/// // Using the auto-generated exchange_config! macro:
655/// let config = ExchangeConfig::Binance(exchanges::BinanceConfig);
656/// let name = exchange_config!(config; (Exchange, config_param) => {
657///     // Inside this block:
658///     // - Exchange is aliased to exchanges::Binance
659///     // - config_param is the BinanceConfig instance
660///     Exchange::new(config_param).name()
661/// });
662/// assert_eq!(name, "binance");
663///
664/// // Create a different instance for demonstrating methods
665/// let config2 = ExchangeConfig::Binance(exchanges::BinanceConfig);
666///
667/// // Using the ExchangeConfig enum's methods directly
668/// // Get the TypeId of the concrete type
669/// let type_id = config2.concrete_type_id();
670/// // Get the name of the concrete type (as a string)
671/// let type_name = config2.concrete_type_name();
672/// assert!(type_name.ends_with("exchanges::Binance"));
673/// ```
674#[proc_macro_derive(ConcreteConfig, attributes(concrete))]
675pub fn derive_concrete_config(input: TokenStream) -> TokenStream {
676    // Parse the input tokens into a syntax tree
677    let input = parse_macro_input!(input as DeriveInput);
678
679    // Extract the name of the type
680    let type_name = &input.ident;
681
682    // Create a snake_case version of the type name for the macro_rules! name
683    let type_name_str = type_name.to_string();
684    // Strip "Config" suffix if present for cleaner macro names
685    let base_name = if type_name_str.ends_with("Config") {
686        &type_name_str[0..type_name_str.len() - 6]
687    } else {
688        &type_name_str
689    };
690    let macro_name_str = format!("{}_config", base_name.to_case(Case::Snake));
691    let macro_name = syn::Ident::new(&macro_name_str, type_name.span());
692
693    // Ensure we're dealing with an enum
694    let data_enum = match &input.data {
695        syn::Data::Enum(data_enum) => data_enum,
696        _ => {
697            return syn::Error::new_spanned(
698                type_name,
699                "ConcreteConfig can only be derived for enums with data",
700            )
701            .to_compile_error()
702            .into();
703        }
704    };
705
706    // Extract variant names, their concrete types, and field types
707    let mut variant_mappings = Vec::new();
708
709    for variant in &data_enum.variants {
710        let variant_name = &variant.ident;
711
712        // Extract the concrete type path from the variant's attributes
713        if let Some(concrete_type) = extract_concrete_type_path(&variant.attrs) {
714            // Verify the variant has a tuple field
715            match &variant.fields {
716                Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
717                    variant_mappings.push((variant_name, concrete_type));
718                }
719                _ => {
720                    return syn::Error::new_spanned(
721                        variant_name,
722                        format!(
723                            "Enum variant `{}` must have exactly one unnamed field for the config",
724                            variant_name
725                        ),
726                    )
727                    .to_compile_error()
728                    .into();
729                }
730            }
731        } else {
732            // Variant is missing the #[concrete = "..."] attribute
733            return syn::Error::new_spanned(
734                variant_name,
735                format!(
736                    "Enum variant `{}` is missing the #[concrete = \"...\"] attribute",
737                    variant_name
738                ),
739            )
740            .to_compile_error()
741            .into();
742        }
743    }
744
745    // Generate match arms for the concrete type ID
746    let match_arms = variant_mappings
747        .iter()
748        .map(|(variant_name, concrete_type)| {
749            quote! {
750                #type_name::#variant_name(_) => {
751                    type_id::<#concrete_type>()
752                }
753            }
754        });
755
756    // Generate match arms for the concrete type name
757    let type_name_arms = variant_mappings
758        .iter()
759        .map(|(variant_name, concrete_type)| {
760            quote! {
761                #type_name::#variant_name(_) => type_name_of::<#concrete_type>()
762            }
763        });
764
765    // Generate match arms for the config method
766    let config_arms = variant_mappings
767        .iter()
768        .map(|(variant_name, _concrete_type)| {
769            quote! {
770                #type_name::#variant_name(config) => config
771            }
772        });
773
774    // Generate match arms for the macro_rules! version
775    let macro_match_arms = variant_mappings
776        .iter()
777        .map(|(variant_name, concrete_type)| {
778            quote! {
779                #type_name::#variant_name(config) => {
780                    type $type_param = #concrete_type;
781                    let $config_param = config;
782                    $code_block
783                }
784            }
785        });
786
787    // Create the macro name
788
789    // Generate a top-level macro with the snake_case name of the enum + "_config"
790    let macro_def = quote! {
791        #[macro_export]
792        macro_rules! #macro_name {
793            ($enum_instance:expr; ($type_param:ident, $config_param:ident) => $code_block:block) => {
794                match $enum_instance {
795                    #(#macro_match_arms),*
796                }
797            };
798        }
799    };
800
801    // Generate the methods implementation
802    let methods_impl = quote! {
803        impl #type_name {
804            /// Returns the TypeId of the concrete type associated with this enum variant
805            pub fn concrete_type_id(&self) -> std::any::TypeId {
806                use std::any::TypeId;
807
808                fn type_id<T: 'static>() -> TypeId {
809                    TypeId::of::<T>()
810                }
811
812                match self {
813                    #(#match_arms),*
814                }
815            }
816
817            /// Returns the name of the concrete type associated with this enum variant
818            pub fn concrete_type_name(&self) -> &'static str {
819                use std::any::type_name;
820
821                fn type_name_of<T: 'static>() -> &'static str {
822                    type_name::<T>()
823                }
824
825                match self {
826                    #(#type_name_arms),*
827                }
828            }
829
830            // Get config data from the enum variant
831            pub fn config(&self) -> &dyn std::any::Any {
832                match self {
833                    #(#config_arms),*
834                }
835            }
836        }
837    };
838
839    // Combine the macro definition and methods implementation
840    let expanded = quote! {
841        // Define the macro
842        #macro_def
843
844        // Implement methods on the enum
845        #methods_impl
846    };
847
848    TokenStream::from(expanded)
849}