Skip to main content

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
5use apply::{ApplyInput, apply_impl};
6use def_kind::def_kind_impl;
7use generate::generate_name;
8use impl_kind::{ImplKindInput, impl_kind_impl};
9use parse::KindInput;
10use proc_macro::TokenStream;
11use quote::quote;
12use re_export::{ReexportInput, generate_function_re_exports_impl, generate_trait_re_exports_impl};
13use syn::parse_macro_input;
14
15pub(crate) mod apply;
16pub(crate) mod canonicalize;
17pub(crate) mod def_kind;
18pub(crate) mod generate;
19pub(crate) mod impl_kind;
20pub(crate) mod parse;
21pub(crate) mod re_export;
22
23#[cfg(test)]
24mod property_tests;
25
26/// Generates the name of a `Kind` trait based on its signature.
27///
28/// This macro takes a list of associated type definitions, similar to a trait definition.
29///
30/// ### Syntax
31///
32/// ```ignore
33/// Kind!(
34///     type AssocName<Params>: Bounds;
35///     // ...
36/// )
37/// ```
38///
39/// ### Parameters
40///
41/// * `Associated Types`: A list of associated type definitions (e.g., `type Of<T>;`) that define the signature of the Kind.
42///
43/// ### Generates
44///
45/// The name of the generated `Kind` trait (e.g., `Kind_0123456789abcdef`).
46/// The name is deterministic and based on a hash of the signature.
47///
48/// ### Examples
49///
50/// ```ignore
51/// // Invocation
52/// let name = Kind!(type Of<T>;);
53///
54/// // Expanded code
55/// let name = Kind_...; // e.g., Kind_a1b2c3d4e5f67890
56/// ```
57///
58/// ```ignore
59/// // Invocation
60/// let name = Kind!(type Of<'a, T: Display>: Debug;);
61///
62/// // Expanded code
63/// let name = Kind_...; // Unique hash based on signature
64/// ```
65///
66/// ```ignore
67/// // Invocation
68/// let name = Kind!(
69///     type Of<T>;
70///     type SendOf<T>: Send;
71/// );
72///
73/// // Expanded code
74/// let name = Kind_...; // Unique hash based on signature
75/// ```
76///
77/// # Limitations
78///
79/// Due to Rust syntax restrictions, this macro cannot be used directly in positions where a
80/// concrete path is expected by the parser, such as:
81/// * Supertrait bounds: `trait MyTrait: Kind!(...) {}` (Invalid)
82/// * Type aliases: `type MyKind = Kind!(...);` (Invalid)
83/// * Trait aliases: `trait MyKind = Kind!(...);` (Invalid)
84///
85/// In these cases, you must use the generated name directly (e.g., `Kind_...`).
86#[proc_macro]
87#[allow(non_snake_case)]
88pub fn Kind(input: TokenStream) -> TokenStream {
89	let input = parse_macro_input!(input as KindInput);
90	let name = generate_name(&input);
91	quote!(#name).into()
92}
93
94/// Defines a new `Kind` trait.
95///
96/// This macro generates a trait definition for a Higher-Kinded Type signature.
97///
98/// ### Syntax
99///
100/// ```ignore
101/// def_kind!(
102///     type AssocName<Params>: Bounds;
103///     // ...
104/// )
105/// ```
106///
107/// ### Parameters
108///
109/// * `Associated Types`: A list of associated type definitions (e.g., `type Of<T>;`) that define the signature of the Kind.
110///
111/// ### Generates
112///
113/// A public trait definition with a unique name derived from the signature (format: `Kind_{hash}`).
114///
115/// ### Examples
116///
117/// ```ignore
118/// // Invocation
119/// def_kind!(type Of<T>;);
120///
121/// // Expanded code
122/// pub trait Kind_... { // e.g., Kind_a1b2c3d4e5f67890
123///     type Of<T>;
124/// }
125/// ```
126///
127/// ```ignore
128/// // Invocation
129/// def_kind!(type Of<'a, T: Display>: Debug;);
130///
131/// // Expanded code
132/// pub trait Kind_... {
133///     type Of<'a, T: Display>: Debug;
134/// }
135/// ```
136///
137/// ```ignore
138/// // Invocation
139/// def_kind!(
140///     type Of<T>;
141///     type SendOf<T>: Send;
142/// );
143///
144/// // Expanded code
145/// pub trait Kind_... {
146///     type Of<T>;
147///     type SendOf<T>: Send;
148/// }
149/// ```
150#[proc_macro]
151pub fn def_kind(input: TokenStream) -> TokenStream {
152	let input = parse_macro_input!(input as KindInput);
153	def_kind_impl(input).into()
154}
155
156/// Implements a `Kind` trait for a brand.
157///
158/// This macro simplifies the implementation of a generated `Kind` trait for a specific
159/// brand type. It infers the correct `Kind` trait to implement based on the signature
160/// of the associated types provided in the block.
161///
162/// The signature (names, parameters, and bounds) of the associated types must match
163/// the definition used in [`def_kind!`] or [`Kind!`] to ensure the correct trait is implemented.
164///
165/// ### Syntax
166///
167/// ```ignore
168/// impl_kind! {
169///     // Optional impl generics
170///     impl<Generics> for BrandType
171///     // Optional where clause
172///     where Bounds
173///     {
174///         type AssocName<Params> = ConcreteType;
175///         // ... more associated types
176///     }
177/// }
178/// ```
179///
180/// ### Parameters
181///
182/// * `Generics`: Optional generic parameters for the implementation.
183/// * `BrandType`: The brand type to implement the Kind for.
184/// * `Bounds`: Optional where clause bounds.
185/// * `Associated Types`: The associated type assignments (e.g., `type Of<A> = Option<A>;`).
186///
187/// ### Generates
188///
189/// An implementation of the appropriate `Kind` trait for the brand.
190///
191/// ### Examples
192///
193/// ```ignore
194/// // Invocation
195/// impl_kind! {
196///     for OptionBrand {
197///         type Of<A> = Option<A>;
198///     }
199/// }
200///
201/// // Expanded code
202/// impl Kind_... for OptionBrand { // e.g., Kind_a1b2c3d4e5f67890
203///     type Of<A> = Option<A>;
204/// }
205/// ```
206///
207/// ```ignore
208/// // Invocation
209/// impl_kind! {
210///     impl<E> for ResultBrand<E> {
211///         type Of<A> = Result<A, E>;
212///     }
213/// }
214///
215/// // Expanded code
216/// impl<E> Kind_... for ResultBrand<E> {
217///     type Of<A> = Result<A, E>;
218/// }
219/// ```
220///
221/// ```ignore
222/// // Invocation
223/// impl_kind! {
224///     impl<E> for MyBrand<E> where E: Clone {
225///         type Of<A> = MyType<A, E>;
226///         type SendOf<A> = MySendType<A, E>;
227///     }
228/// }
229///
230/// // Expanded code
231/// impl<E> Kind_... for MyBrand<E> where E: Clone {
232///     type Of<A> = MyType<A, E>;
233///     type SendOf<A> = MySendType<A, E>;
234/// }
235/// ```
236///
237/// ```ignore
238/// // Invocation
239/// // Corresponds to: def_kind!(type Of<T: Display>;);
240/// impl_kind! {
241///     for DisplayBrand {
242///         // Bounds here are used to infer the correct `Kind` trait name
243///         type Of<T: Display> = DisplayType<T>;
244///     }
245/// }
246///
247/// // Expanded code
248/// impl Kind_... for DisplayBrand {
249///     type Of<T: Display> = DisplayType<T>;
250/// }
251/// ```
252#[proc_macro]
253pub fn impl_kind(input: TokenStream) -> TokenStream {
254	let input = parse_macro_input!(input as ImplKindInput);
255	impl_kind_impl(input).into()
256}
257
258/// Applies a brand to type arguments.
259///
260/// This macro projects a brand type to its concrete type using the appropriate
261/// `Kind` trait. It uses a syntax that mimics a fully qualified path, where the
262/// `Kind` trait is specified by its signature.
263///
264/// ### Syntax
265///
266/// ```ignore
267/// Apply!(<Brand as Kind!( KindSignature )>::AssocType<Args>)
268/// ```
269///
270/// ### Parameters
271///
272/// * `Brand`: The brand type (e.g., `OptionBrand`).
273/// * `KindSignature`: A list of associated type definitions defining the `Kind` trait schema.
274/// * `AssocType`: The associated type to project (e.g., `Of`).
275/// * `Args`: The concrete arguments to apply.
276///
277/// ### Generates
278///
279/// The concrete type resulting from applying the brand to the arguments.
280///
281/// ### Examples
282///
283/// ```ignore
284/// // Invocation
285/// // Applies MyBrand to lifetime 'static and type String.
286/// type Concrete = Apply!(<MyBrand as Kind!( type Of<'a, T>; )>::Of<'static, String>);
287///
288/// // Expanded code
289/// type Concrete = <MyBrand as Kind_...>::Of<'static, String>;
290/// ```
291///
292/// ```ignore
293/// // Invocation
294/// // Applies MyBrand to a generic type T with bounds.
295/// type Concrete = Apply!(<MyBrand as Kind!( type Of<T: Clone>; )>::Of<T>);
296///
297/// // Expanded code
298/// type Concrete = <MyBrand as Kind_...>::Of<T>;
299/// ```
300///
301/// ```ignore
302/// // Invocation
303/// // Complex example with lifetimes, types, and output bounds.
304/// type Concrete = Apply!(<MyBrand as Kind!( type Of<'a, T: Clone + Debug>: Display; )>::Of<'a, T>);
305///
306/// // Expanded code
307/// type Concrete = <MyBrand as Kind_...>::Of<'a, T>;
308/// ```
309///
310/// ```ignore
311/// // Invocation
312/// // Use a custom associated type for projection.
313/// type Concrete = Apply!(<MyBrand as Kind!( type Of<T>; type SendOf<T>; )>::SendOf<T>);
314///
315/// // Expanded code
316/// type Concrete = <MyBrand as Kind_...>::SendOf<T>;
317/// ```
318#[proc_macro]
319#[allow(non_snake_case)]
320pub fn Apply(input: TokenStream) -> TokenStream {
321	let input = parse_macro_input!(input as ApplyInput);
322	apply_impl(input).into()
323}
324
325/// Generates re-exports for all public free functions in a directory.
326///
327/// This macro scans the specified directory for Rust files, parses them to find public free functions,
328/// and generates `pub use` statements for them. It supports aliasing to resolve name conflicts.
329///
330/// ### Syntax
331///
332/// ```ignore
333/// generate_function_re_exports!("path/to/directory", {
334///     original_name: aliased_name,
335///     ...
336/// })
337/// ```
338///
339/// ### Parameters
340///
341/// * `path/to/directory`: The path to the directory containing the modules, relative to the crate root.
342/// * `aliases`: A map of function names to their desired aliases.
343///
344/// ### Generates
345///
346/// `pub use` statements for each public function found in the directory.
347///
348/// ### Examples
349///
350/// ```ignore
351/// // Invocation
352/// generate_function_re_exports!("src/classes", {
353///     identity: category_identity,
354///     new: fn_new,
355/// });
356///
357/// // Expanded code
358/// pub use src::classes::category::identity as category_identity;
359/// pub use src::classes::function::new as fn_new;
360/// // ... other re-exports
361/// ```
362#[proc_macro]
363pub fn generate_function_re_exports(input: TokenStream) -> TokenStream {
364	let input = parse_macro_input!(input as ReexportInput);
365	generate_function_re_exports_impl(input).into()
366}
367
368/// Generates re-exports for all public traits in a directory.
369///
370/// This macro scans the specified directory for Rust files, parses them to find public traits,
371/// and generates `pub use` statements for them.
372///
373/// ### Syntax
374///
375/// ```ignore
376/// generate_trait_re_exports!("path/to/directory", {
377///     original_name: aliased_name,
378///     ...
379/// })
380/// ```
381///
382/// ### Parameters
383///
384/// * `path/to/directory`: The path to the directory containing the modules, relative to the crate root.
385/// * `aliases`: A map of trait names to their desired aliases (optional).
386///
387/// ### Generates
388///
389/// `pub use` statements for each public trait found in the directory.
390///
391/// ### Examples
392///
393/// ```ignore
394/// // Invocation
395/// generate_trait_re_exports!("src/classes", {});
396///
397/// // Expanded code
398/// pub use src::classes::functor::Functor;
399/// pub use src::classes::monad::Monad;
400/// // ... other re-exports
401/// ```
402#[proc_macro]
403pub fn generate_trait_re_exports(input: TokenStream) -> TokenStream {
404	let input = parse_macro_input!(input as ReexportInput);
405	generate_trait_re_exports_impl(input).into()
406}