fp_macros/
lib.rs

1//! Procedural macros for the [`fp-library`](https://docs.rs/fp-library/latest/fp_library/) crate.
2//!
3//! This crate provides macros for generating and working with Higher-Kinded Type (HKT) traits.
4//! It includes:
5//! - `Kind!`: Generates the name of a `Kind` trait based on its signature.
6//! - `def_kind!`: Defines a new `Kind` trait.
7//! - `impl_kind!`: Implements a `Kind` trait for a brand.
8//! - `Apply!`: Applies a brand to type arguments.
9
10use apply::{ApplyInput, apply_impl};
11use def_kind::def_kind_impl;
12use generate::generate_name;
13use impl_kind::{ImplKindInput, impl_kind_impl};
14use parse::KindInput;
15use proc_macro::TokenStream;
16use quote::quote;
17use re_export::{ReexportInput, generate_function_re_exports_impl, generate_trait_re_exports_impl};
18use syn::parse_macro_input;
19
20pub(crate) mod apply;
21pub(crate) mod canonicalize;
22pub(crate) mod def_kind;
23pub(crate) mod generate;
24pub(crate) mod impl_kind;
25pub(crate) mod parse;
26pub(crate) mod re_export;
27
28#[cfg(test)]
29mod property_tests;
30
31/// Generates the name of a `Kind` trait based on its signature.
32///
33/// This macro takes a list of associated type definitions, similar to a trait definition.
34///
35/// # Examples
36///
37/// ```ignore
38/// // Simple signature
39/// let name = Kind!(type Of<T>;);
40///
41/// // Signature with bounds and lifetimes
42/// let name = Kind!(type Of<'a, T: Display>: Debug;);
43///
44/// // Multiple associated types
45/// let name = Kind!(
46///     type Of<T>;
47///     type SendOf<T>: Send;
48/// );
49/// ```
50///
51/// # Limitations
52///
53/// Due to Rust syntax restrictions, this macro cannot be used directly in positions where a
54/// concrete path is expected by the parser, such as:
55/// * Supertrait bounds: `trait MyTrait: Kind!(...) {}` (Invalid)
56/// * Type aliases: `type MyKind = Kind!(...);` (Invalid)
57/// * Trait aliases: `trait MyKind = Kind!(...);` (Invalid)
58///
59/// In these cases, you must use the generated name directly (e.g., `Kind_...`).
60#[proc_macro]
61#[allow(non_snake_case)]
62pub fn Kind(input: TokenStream) -> TokenStream {
63	let input = parse_macro_input!(input as KindInput);
64	let name = generate_name(&input);
65	quote!(#name).into()
66}
67
68/// Defines a new `Kind` trait.
69///
70/// This macro generates a trait definition for a Higher-Kinded Type signature.
71/// It takes a list of associated type definitions, similar to a trait definition.
72///
73/// # Examples
74///
75/// ```ignore
76/// // Simple definition
77/// def_kind!(type Of<T>;);
78///
79/// // Definition with bounds and lifetimes
80/// def_kind!(type Of<'a, T: Display>: Debug;);
81///
82/// // Multiple associated types
83/// def_kind!(
84///     type Of<T>;
85///     type SendOf<T>: Send;
86/// );
87/// ```
88#[proc_macro]
89pub fn def_kind(input: TokenStream) -> TokenStream {
90	let input = parse_macro_input!(input as KindInput);
91	def_kind_impl(input).into()
92}
93
94/// Implements a `Kind` trait for a brand.
95///
96/// This macro simplifies the implementation of a generated `Kind` trait for a specific
97/// brand type. It infers the correct `Kind` trait to implement based on the signature
98/// of the associated types provided in the block.
99///
100/// The signature (names, parameters, and bounds) of the associated types must match
101/// the definition used in [`def_kind!`] or [`Kind!`] to ensure the correct trait is implemented.
102///
103/// # Syntax
104///
105/// ```ignore
106/// impl_kind! {
107///     // Optional impl generics
108///     impl<Generics> for BrandType
109///     // Optional where clause
110///     where Bounds
111///     {
112///         type AssocName<Params> = ConcreteType;
113///         // ... more associated types
114///     }
115/// }
116/// ```
117///
118/// # Examples
119///
120/// ```ignore
121/// // Simple implementation
122/// impl_kind! {
123///     for OptionBrand {
124///         type Of<A> = Option<A>;
125///     }
126/// }
127///
128/// // Implementation with generics
129/// impl_kind! {
130///     impl<E> for ResultBrand<E> {
131///         type Of<A> = Result<A, E>;
132///     }
133/// }
134///
135/// // Implementation with where clause and multiple types
136/// impl_kind! {
137///     impl<E> for MyBrand<E> where E: Clone {
138///         type Of<A> = MyType<A, E>;
139///         type SendOf<A> = MySendType<A, E>;
140///     }
141/// }
142///
143/// // Implementation matching a `Kind` with bounds
144/// // Corresponds to: def_kind!(type Of<T: Display>;);
145/// impl_kind! {
146///     for DisplayBrand {
147///         // Bounds here are used to infer the correct `Kind` trait name
148///         type Of<T: Display> = DisplayType<T>;
149///     }
150/// }
151/// ```
152#[proc_macro]
153pub fn impl_kind(input: TokenStream) -> TokenStream {
154	let input = parse_macro_input!(input as ImplKindInput);
155	impl_kind_impl(input).into()
156}
157
158/// Applies a brand to type arguments.
159///
160/// This macro projects a brand type to its concrete type using the appropriate
161/// `Kind` trait. It uses a syntax that mimics a fully qualified path, where the
162/// `Kind` trait is specified by its signature.
163///
164/// # Syntax
165///
166/// ```ignore
167/// Apply!(<Brand as Kind!( KindSignature )>::AssocType<Args>)
168/// ```
169///
170/// * `Brand`: The brand type (e.g., `OptionBrand`).
171/// * `KindSignature`: A list of associated type definitions defining the `Kind` trait schema.
172/// * `AssocType`: The associated type to project (e.g., `Of`).
173/// * `Args`: The concrete arguments to apply.
174///
175/// # Examples
176///
177/// ```ignore
178/// // Applies MyBrand to lifetime 'static and type String.
179/// type Concrete = Apply!(<MyBrand as Kind!( type Of<'a, T>; )>::Of<'static, String>);
180///
181/// // Applies MyBrand to a generic type T with bounds.
182/// type Concrete = Apply!(<MyBrand as Kind!( type Of<T: Clone>; )>::Of<T>);
183///
184/// // Complex example with lifetimes, types, and output bounds.
185/// type Concrete = Apply!(<MyBrand as Kind!( type Of<'a, T: Clone + Debug>: Display; )>::Of<'a, T>);
186///
187/// // Use a custom associated type for projection.
188/// type Concrete = Apply!(<MyBrand as Kind!( type Of<T>; type SendOf<T>; )>::SendOf<T>);
189/// ```
190#[proc_macro]
191#[allow(non_snake_case)]
192pub fn Apply(input: TokenStream) -> TokenStream {
193	let input = parse_macro_input!(input as ApplyInput);
194	apply_impl(input).into()
195}
196
197/// Generates re-exports for all public free functions in a directory.
198///
199/// This macro scans the specified directory for Rust files, parses them to find public free functions,
200/// and generates `pub use` statements for them. It supports aliasing to resolve name conflicts.
201///
202/// # Syntax
203///
204/// ```ignore
205/// generate_function_re_exports!("path/to/directory", {
206///     original_name: aliased_name,
207///     ...
208/// })
209/// ```
210///
211/// * `path/to/directory`: The path to the directory containing the modules, relative to the crate root.
212/// * `aliases`: A map of function names to their desired aliases.
213///
214/// # Examples
215///
216/// ```ignore
217/// generate_function_re_exports!("src/classes", {
218///     identity: category_identity,
219///     new: fn_new,
220/// });
221/// ```
222#[proc_macro]
223pub fn generate_function_re_exports(input: TokenStream) -> TokenStream {
224	let input = parse_macro_input!(input as ReexportInput);
225	generate_function_re_exports_impl(input).into()
226}
227
228/// Generates re-exports for all public traits in a directory.
229///
230/// This macro scans the specified directory for Rust files, parses them to find public traits,
231/// and generates `pub use` statements for them.
232///
233/// # Syntax
234///
235/// ```ignore
236/// generate_trait_re_exports!("path/to/directory", {
237///     original_name: aliased_name,
238///     ...
239/// })
240/// ```
241///
242/// * `path/to/directory`: The path to the directory containing the modules, relative to the crate root.
243/// * `aliases`: A map of trait names to their desired aliases (optional).
244///
245/// # Examples
246///
247/// ```ignore
248/// generate_trait_re_exports!("src/classes", {});
249/// ```
250#[proc_macro]
251pub fn generate_trait_re_exports(input: TokenStream) -> TokenStream {
252	let input = parse_macro_input!(input as ReexportInput);
253	generate_trait_re_exports_impl(input).into()
254}