concrete_type_rules/
lib.rs

1#![doc(html_root_url = "https://docs.rs/concrete-type-rules")]
2#![warn(missing_docs)]
3
4//! # Concrete Type Rules
5//!
6//! Utilities and extensions for working with the `concrete-type` crate.
7//!
8//! This crate provides additional tools for composing multiple concrete types together,
9//! including macros for generating combined matchers that can handle multiple enum types
10//! at once.
11//!
12//! ## Features
13//!
14//! - `gen_match_concretes_macro!` - Generates macros for matching multiple enum instances
15//!   simultaneously, with support for 2-5 enum types.
16//!
17//! ## Examples
18//!
19//! ### Combined Matcher for Two Enum Types
20//!
21//! ```rust,ignore
22//! use concrete_type::Concrete;
23//! use concrete_type_rules::gen_match_concretes_macro;
24//!
25//! #[derive(Concrete)]
26//! enum Exchange {
27//!     #[concrete = "exchanges::Binance"]
28//!     Binance,
29//! }
30//!
31//! #[derive(Concrete)]
32//! enum Strategy {
33//!     #[concrete = "strategies::StrategyA"]
34//!     StrategyA,
35//! }
36//!
37//! mod exchanges {
38//!     pub struct Binance;
39//! }
40//!
41//! mod strategies {
42//!     pub struct StrategyA;
43//! }
44//!
45//! // Generate a combined matcher macro
46//! gen_match_concretes_macro!(Exchange, Strategy);
47//!
48//! // Now you can use the generated macro with both enum instances
49//! let exchange = Exchange::Binance;
50//! let strategy = Strategy::StrategyA;
51//!
52//! // This uses a single match expression for both enums
53//! let result = match_exchange_strategy!(exchange, strategy; E, S => {
54//!     // E is exchanges::Binance, S is strategies::StrategyA
55//!     format!("{} + {}", std::any::type_name::<E>(), std::any::type_name::<S>())
56//! });
57//! ```
58//!
59//! ### Using With More Enum Types
60//!
61//! ```rust,ignore
62//! // For 3 enum types:
63//! gen_match_concretes_macro!(Exchange, Strategy, Market);
64//!
65//! // Generated macro name combines all enum names in snake_case
66//! // E.g., match_exchange_strategy_market!
67//!
68//! // For 4 or 5 enum types:
69//! gen_match_concretes_macro!(Exchange, Strategy, Market, Asset, TimeFrame);
70//! ```
71
72/// A macro that generates a combined matcher macro for multiple concrete enums.
73///
74/// This macro creates a new macro that allows you to match multiple enum instances
75/// simultaneously, providing type parameters for each concrete type associated with
76/// the enum variants.
77///
78/// # Arguments
79///
80/// * First argument: First enum type name
81/// * Second argument: Second enum type name
82/// * Optionally: Third, fourth, and fifth enum type names
83///
84/// The generated macro will be named using the snake_case of all provided enum names,
85/// joined with underscores and prefixed with "match_".
86///
87/// # Generated Macro Usage
88///
89/// The generated macro accepts:
90///
91/// * Enum instances as positional parameters (one for each enum type)
92/// * Type parameters and a code block after a semicolon
93///
94/// Inside the code block, each type parameter is aliased to the concrete type
95/// associated with the corresponding enum variant.
96///
97/// # Examples
98///
99/// ```rust,ignore
100/// use concrete_type::Concrete;
101/// use concrete_type_rules::gen_match_concretes_macro;
102///
103/// #[derive(Concrete, Clone, Copy)]
104/// enum Exchange {
105///     #[concrete = "BinanceType"]
106///     Binance,
107/// }
108///
109/// #[derive(Concrete)]
110/// enum Strategy {
111///     #[concrete = "StrategyAType"]
112///     StrategyA,
113/// }
114///
115/// struct BinanceType;
116/// struct StrategyAType;
117///
118/// // Generate a combined matcher macro
119/// gen_match_concretes_macro!(Exchange, Strategy);
120///
121/// // Now you can use the generated macro
122/// let exchange = Exchange::Binance;
123/// let strategy = Strategy::StrategyA;
124///
125/// let result = match_exchange_strategy!(exchange, strategy; E, S => {
126///     // Here E is BinanceType and S is StrategyAType
127///     format!("{}", std::any::type_name::<(E, S)>())
128/// });
129/// ```
130#[macro_export]
131macro_rules! gen_match_concretes_macro {
132    // For 2 enum types
133    ($first_enum:ident, $second_enum:ident) => {
134        paste::paste! {
135            #[macro_export]
136            macro_rules! [<match_ $first_enum:snake _ $second_enum:snake>] {
137                ($first_var:expr, $second_var:expr; $first_type:ident, $second_type:ident => $code_block:block) => {
138                    [<$first_enum:snake>]!($first_var; $first_type => {
139                        [<$second_enum:snake>]!($second_var; $second_type => {
140                            $code_block
141                        })
142                    })
143                };
144            }
145        }
146    };
147
148    // For 3 enum types
149    ($first_enum:ident, $second_enum:ident, $third_enum:ident) => {
150        paste::paste! {
151            #[macro_export]
152            macro_rules! [<match_ $first_enum:snake _ $second_enum:snake _ $third_enum:snake>] {
153                ($first_var:expr, $second_var:expr, $third_var:expr; $first_type:ident, $second_type:ident, $third_type:ident => $code_block:block) => {
154                    [<$first_enum:snake>]!($first_var; $first_type => {
155                        [<$second_enum:snake>]!($second_var; $second_type => {
156                            [<$third_enum:snake>]!($third_var; $third_type => {
157                                $code_block
158                            })
159                        })
160                    })
161                };
162            }
163        }
164    };
165
166    // For 4 enum types
167    ($first_enum:ident, $second_enum:ident, $third_enum:ident, $fourth_enum:ident) => {
168        paste::paste! {
169            #[macro_export]
170            macro_rules! [<match_ $first_enum:snake _ $second_enum:snake _ $third_enum:snake _ $fourth_enum:snake>] {
171                ($first_var:expr, $second_var:expr, $third_var:expr, $fourth_var:expr;
172                 $first_type:ident, $second_type:ident, $third_type:ident, $fourth_type:ident => $code_block:block) => {
173                    [<$first_enum:snake>]!($first_var; $first_type => {
174                        [<$second_enum:snake>]!($second_var; $second_type => {
175                            [<$third_enum:snake>]!($third_var; $third_type => {
176                                [<$fourth_enum:snake>]!($fourth_var; $fourth_type => {
177                                    $code_block
178                                })
179                            })
180                        })
181                    })
182                };
183            }
184        }
185    };
186
187    // For 5 enum types
188    ($first_enum:ident, $second_enum:ident, $third_enum:ident, $fourth_enum:ident, $fifth_enum:ident) => {
189        paste::paste! {
190            #[macro_export]
191            macro_rules! [<match_ $first_enum:snake _ $second_enum:snake _ $third_enum:snake _ $fourth_enum:snake _ $fifth_enum:snake>] {
192                ($first_var:expr, $second_var:expr, $third_var:expr, $fourth_var:expr, $fifth_var:expr;
193                 $first_type:ident, $second_type:ident, $third_type:ident, $fourth_type:ident, $fifth_type:ident => $code_block:block) => {
194                    [<$first_enum:snake>]!($first_var; $first_type => {
195                        [<$second_enum:snake>]!($second_var; $second_type => {
196                            [<$third_enum:snake>]!($third_var; $third_type => {
197                                [<$fourth_enum:snake>]!($fourth_var; $fourth_type => {
198                                    [<$fifth_enum:snake>]!($fifth_var; $fifth_type => {
199                                        $code_block
200                                    })
201                                })
202                            })
203                        })
204                    })
205                };
206            }
207        }
208    };
209}