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 doc_params::doc_params_impl;
8use doc_type_params::doc_type_params_impl;
9use generate::generate_name;
10use hm_signature::hm_signature_impl;
11use impl_kind::{ImplKindInput, impl_kind_impl};
12use parse::KindInput;
13use proc_macro::TokenStream;
14use quote::quote;
15use re_export::{ReexportInput, generate_function_re_exports_impl, generate_trait_re_exports_impl};
16use syn::parse_macro_input;
17
18pub(crate) mod apply;
19pub(crate) mod canonicalize;
20pub(crate) mod def_kind;
21pub(crate) mod doc_params;
22pub(crate) mod doc_type_params;
23pub(crate) mod doc_utils;
24pub(crate) mod function_utils;
25pub(crate) mod generate;
26pub(crate) mod hm_ast;
27pub(crate) mod hm_signature;
28pub(crate) mod impl_kind;
29pub(crate) mod parse;
30pub(crate) mod re_export;
31
32#[cfg(test)]
33mod property_tests;
34
35/// Generates the name of a `Kind` trait based on its signature.
36///
37/// This macro takes a list of associated type definitions, similar to a trait definition.
38///
39/// ### Syntax
40///
41/// ```ignore
42/// Kind!(
43///     type AssocName<Params>: Bounds;
44///     // ...
45/// )
46/// ```
47///
48/// ### Parameters
49///
50/// * `Associated Types`: A list of associated type definitions (e.g., `type Of<T>;`) that define the signature of the Kind.
51///
52/// ### Generates
53///
54/// The name of the generated `Kind` trait (e.g., `Kind_0123456789abcdef`).
55/// The name is deterministic and based on a hash of the signature.
56///
57/// ### Examples
58///
59/// ```ignore
60/// // Invocation
61/// let name = Kind!(type Of<T>;);
62///
63/// // Expanded code
64/// let name = Kind_...; // e.g., Kind_a1b2c3d4e5f67890
65/// ```
66///
67/// ```ignore
68/// // Invocation
69/// let name = Kind!(type Of<'a, T: Display>: Debug;);
70///
71/// // Expanded code
72/// let name = Kind_...; // Unique hash based on signature
73/// ```
74///
75/// ```ignore
76/// // Invocation
77/// let name = Kind!(
78///     type Of<T>;
79///     type SendOf<T>: Send;
80/// );
81///
82/// // Expanded code
83/// let name = Kind_...; // Unique hash based on signature
84/// ```
85///
86/// # Limitations
87///
88/// Due to Rust syntax restrictions, this macro cannot be used directly in positions where a
89/// concrete path is expected by the parser, such as:
90/// * Supertrait bounds: `trait MyTrait: Kind!(...) {}` (Invalid)
91/// * Type aliases: `type MyKind = Kind!(...);` (Invalid)
92/// * Trait aliases: `trait MyKind = Kind!(...);` (Invalid)
93///
94/// In these cases, you must use the generated name directly (e.g., `Kind_...`).
95#[proc_macro]
96#[allow(non_snake_case)]
97pub fn Kind(input: TokenStream) -> TokenStream {
98	let input = parse_macro_input!(input as KindInput);
99	let name = generate_name(&input);
100	quote!(#name).into()
101}
102
103/// Defines a new `Kind` trait.
104///
105/// This macro generates a trait definition for a Higher-Kinded Type signature.
106///
107/// ### Syntax
108///
109/// ```ignore
110/// def_kind!(
111///     type AssocName<Params>: Bounds;
112///     // ...
113/// )
114/// ```
115///
116/// ### Parameters
117///
118/// * `Associated Types`: A list of associated type definitions (e.g., `type Of<T>;`) that define the signature of the Kind.
119///
120/// ### Generates
121///
122/// A public trait definition with a unique name derived from the signature (format: `Kind_{hash}`).
123///
124/// ### Examples
125///
126/// ```ignore
127/// // Invocation
128/// def_kind!(type Of<T>;);
129///
130/// // Expanded code
131/// pub trait Kind_... { // e.g., Kind_a1b2c3d4e5f67890
132///     type Of<T>;
133/// }
134/// ```
135///
136/// ```ignore
137/// // Invocation
138/// def_kind!(type Of<'a, T: Display>: Debug;);
139///
140/// // Expanded code
141/// pub trait Kind_... {
142///     type Of<'a, T: Display>: Debug;
143/// }
144/// ```
145///
146/// ```ignore
147/// // Invocation
148/// def_kind!(
149///     type Of<T>;
150///     type SendOf<T>: Send;
151/// );
152///
153/// // Expanded code
154/// pub trait Kind_... {
155///     type Of<T>;
156///     type SendOf<T>: Send;
157/// }
158/// ```
159#[proc_macro]
160pub fn def_kind(input: TokenStream) -> TokenStream {
161	let input = parse_macro_input!(input as KindInput);
162	def_kind_impl(input).into()
163}
164
165/// Implements a `Kind` trait for a brand.
166///
167/// This macro simplifies the implementation of a generated `Kind` trait for a specific
168/// brand type. It infers the correct `Kind` trait to implement based on the signature
169/// of the associated types provided in the block.
170///
171/// The signature (names, parameters, and bounds) of the associated types must match
172/// the definition used in [`def_kind!`] or [`Kind!`] to ensure the correct trait is implemented.
173///
174/// ### Syntax
175///
176/// ```ignore
177/// impl_kind! {
178///     // Optional impl generics
179///     impl<Generics> for BrandType
180///     // Optional where clause
181///     where Bounds
182///     {
183///         type AssocName<Params> = ConcreteType;
184///         // ... more associated types
185///     }
186/// }
187/// ```
188///
189/// ### Parameters
190///
191/// * `Generics`: Optional generic parameters for the implementation.
192/// * `BrandType`: The brand type to implement the Kind for.
193/// * `Bounds`: Optional where clause bounds.
194/// * `Associated Types`: The associated type assignments (e.g., `type Of<A> = Option<A>;`).
195///
196/// ### Generates
197///
198/// An implementation of the appropriate `Kind` trait for the brand.
199///
200/// ### Examples
201///
202/// ```ignore
203/// // Invocation
204/// impl_kind! {
205///     for OptionBrand {
206///         type Of<A> = Option<A>;
207///     }
208/// }
209///
210/// // Expanded code
211/// impl Kind_... for OptionBrand { // e.g., Kind_a1b2c3d4e5f67890
212///     type Of<A> = Option<A>;
213/// }
214/// ```
215///
216/// ```ignore
217/// // Invocation
218/// impl_kind! {
219///     impl<E> for ResultBrand<E> {
220///         type Of<A> = Result<A, E>;
221///     }
222/// }
223///
224/// // Expanded code
225/// impl<E> Kind_... for ResultBrand<E> {
226///     type Of<A> = Result<A, E>;
227/// }
228/// ```
229///
230/// ```ignore
231/// // Invocation
232/// impl_kind! {
233///     impl<E> for MyBrand<E> where E: Clone {
234///         type Of<A> = MyType<A, E>;
235///         type SendOf<A> = MySendType<A, E>;
236///     }
237/// }
238///
239/// // Expanded code
240/// impl<E> Kind_... for MyBrand<E> where E: Clone {
241///     type Of<A> = MyType<A, E>;
242///     type SendOf<A> = MySendType<A, E>;
243/// }
244/// ```
245///
246/// ```ignore
247/// // Invocation
248/// // Corresponds to: def_kind!(type Of<T: Display>;);
249/// impl_kind! {
250///     for DisplayBrand {
251///         // Bounds here are used to infer the correct `Kind` trait name
252///         type Of<T: Display> = DisplayType<T>;
253///     }
254/// }
255///
256/// // Expanded code
257/// impl Kind_... for DisplayBrand {
258///     type Of<T: Display> = DisplayType<T>;
259/// }
260/// ```
261#[proc_macro]
262pub fn impl_kind(input: TokenStream) -> TokenStream {
263	let input = parse_macro_input!(input as ImplKindInput);
264	impl_kind_impl(input).into()
265}
266
267/// Applies a brand to type arguments.
268///
269/// This macro projects a brand type to its concrete type using the appropriate
270/// `Kind` trait. It uses a syntax that mimics a fully qualified path, where the
271/// `Kind` trait is specified by its signature.
272///
273/// ### Syntax
274///
275/// ```ignore
276/// Apply!(<Brand as Kind!( KindSignature )>::AssocType<Args>)
277/// ```
278///
279/// ### Parameters
280///
281/// * `Brand`: The brand type (e.g., `OptionBrand`).
282/// * `KindSignature`: A list of associated type definitions defining the `Kind` trait schema.
283/// * `AssocType`: The associated type to project (e.g., `Of`).
284/// * `Args`: The concrete arguments to apply.
285///
286/// ### Generates
287///
288/// The concrete type resulting from applying the brand to the arguments.
289///
290/// ### Examples
291///
292/// ```ignore
293/// // Invocation
294/// // Applies MyBrand to lifetime 'static and type String.
295/// type Concrete = Apply!(<MyBrand as Kind!( type Of<'a, T>; )>::Of<'static, String>);
296///
297/// // Expanded code
298/// type Concrete = <MyBrand as Kind_...>::Of<'static, String>;
299/// ```
300///
301/// ```ignore
302/// // Invocation
303/// // Applies MyBrand to a generic type T with bounds.
304/// type Concrete = Apply!(<MyBrand as Kind!( type Of<T: Clone>; )>::Of<T>);
305///
306/// // Expanded code
307/// type Concrete = <MyBrand as Kind_...>::Of<T>;
308/// ```
309///
310/// ```ignore
311/// // Invocation
312/// // Complex example with lifetimes, types, and output bounds.
313/// type Concrete = Apply!(<MyBrand as Kind!( type Of<'a, T: Clone + Debug>: Display; )>::Of<'a, T>);
314///
315/// // Expanded code
316/// type Concrete = <MyBrand as Kind_...>::Of<'a, T>;
317/// ```
318///
319/// ```ignore
320/// // Invocation
321/// // Use a custom associated type for projection.
322/// type Concrete = Apply!(<MyBrand as Kind!( type Of<T>; type SendOf<T>; )>::SendOf<T>);
323///
324/// // Expanded code
325/// type Concrete = <MyBrand as Kind_...>::SendOf<T>;
326/// ```
327#[proc_macro]
328#[allow(non_snake_case)]
329pub fn Apply(input: TokenStream) -> TokenStream {
330	let input = parse_macro_input!(input as ApplyInput);
331	apply_impl(input).into()
332}
333
334/// Generates re-exports for all public free functions in a directory.
335///
336/// This macro scans the specified directory for Rust files, parses them to find public free functions,
337/// and generates `pub use` statements for them. It supports aliasing to resolve name conflicts.
338///
339/// ### Syntax
340///
341/// ```ignore
342/// generate_function_re_exports!("path/to/directory", {
343///     original_name: aliased_name,
344///     ...
345/// })
346/// ```
347///
348/// ### Parameters
349///
350/// * `path/to/directory`: The path to the directory containing the modules, relative to the crate root.
351/// * `aliases`: A map of function names to their desired aliases.
352///
353/// ### Generates
354///
355/// `pub use` statements for each public function found in the directory.
356///
357/// ### Examples
358///
359/// ```ignore
360/// // Invocation
361/// generate_function_re_exports!("src/classes", {
362///     identity: category_identity,
363///     new: fn_new,
364/// });
365///
366/// // Expanded code
367/// pub use src::classes::category::identity as category_identity;
368/// pub use src::classes::function::new as fn_new;
369/// // ... other re-exports
370/// ```
371#[proc_macro]
372pub fn generate_function_re_exports(input: TokenStream) -> TokenStream {
373	let input = parse_macro_input!(input as ReexportInput);
374	generate_function_re_exports_impl(input).into()
375}
376
377/// Generates re-exports for all public traits in a directory.
378///
379/// This macro scans the specified directory for Rust files, parses them to find public traits,
380/// and generates `pub use` statements for them.
381///
382/// ### Syntax
383///
384/// ```ignore
385/// generate_trait_re_exports!("path/to/directory", {
386///     original_name: aliased_name,
387///     ...
388/// })
389/// ```
390///
391/// ### Parameters
392///
393/// * `path/to/directory`: The path to the directory containing the modules, relative to the crate root.
394/// * `aliases`: A map of trait names to their desired aliases (optional).
395///
396/// ### Generates
397///
398/// `pub use` statements for each public trait found in the directory.
399///
400/// ### Examples
401///
402/// ```ignore
403/// // Invocation
404/// generate_trait_re_exports!("src/classes", {});
405///
406/// // Expanded code
407/// pub use src::classes::functor::Functor;
408/// pub use src::classes::monad::Monad;
409/// // ... other re-exports
410/// ```
411#[proc_macro]
412pub fn generate_trait_re_exports(input: TokenStream) -> TokenStream {
413	let input = parse_macro_input!(input as ReexportInput);
414	generate_trait_re_exports_impl(input).into()
415}
416
417/// Generates a Hindley-Milner style type signature for a function.
418///
419/// This macro analyzes the function signature and generates a documentation comment
420/// containing the corresponding HM type signature.
421///
422/// ### Syntax
423///
424/// ```ignore
425/// #[hm_signature]
426/// pub fn function_name<Generics>(params) -> ReturnType { ... }
427/// ```
428///
429/// When applying this macro to a method inside a trait, you can provide the trait name
430/// as an argument to correctly generate the `Trait self` constraint.
431///
432/// ### Generates
433///
434/// A documentation comment with the generated signature, prepended to the function definition.
435///
436/// ### Examples
437///
438/// ```ignore
439/// // Invocation
440/// #[hm_signature]
441/// pub fn map<F: Functor, A, B>(f: impl Fn(A) -> B, fa: F::Of<A>) -> F::Of<B> { ... }
442///
443/// // Expanded code
444/// /// `forall f a b. Functor f => (a -> b, f a) -> f b`
445/// pub fn map<F: Functor, A, B>(f: impl Fn(A) -> B, fa: F::Of<A>) -> F::Of<B> { ... }
446/// ```
447///
448/// ```ignore
449/// // Invocation
450/// #[hm_signature]
451/// pub fn foo(x: impl Iterator<Item = String>) -> i32 { ... }
452///
453/// // Expanded code
454/// /// `iterator -> i32`
455/// pub fn foo(x: impl Iterator<Item = String>) -> i32 { ... }
456/// ```
457///
458/// ```ignore
459/// // Invocation
460/// trait Functor {
461///     #[hm_signature(Functor)]
462///     fn map<A, B>(f: impl Fn(A) -> B, fa: Self::Of<A>) -> Self::Of<B>;
463/// }
464///
465/// // Expanded code
466/// trait Functor {
467///     /// `forall self a b. Functor self => (a -> b, self a) -> self b`
468///     fn map<A, B>(f: impl Fn(A) -> B, fa: Self::Of<A>) -> Self::Of<B>;
469/// }
470/// ```
471#[proc_macro_attribute]
472pub fn hm_signature(
473	attr: TokenStream,
474	item: TokenStream,
475) -> TokenStream {
476	hm_signature_impl(attr.into(), item.into()).into()
477}
478
479/// Generates documentation for a function's type parameters.
480///
481/// This macro analyzes the function signature and generates a documentation comment
482/// list based on the provided descriptions.
483///
484/// ### Syntax
485///
486/// ```ignore
487/// #[doc_type_params(
488///     "Description for first parameter",
489///     ("OverriddenName", "Description for second parameter"),
490///     ...
491/// )]
492/// pub fn function_name<Generics>(params) -> ReturnType { ... }
493/// ```
494///
495/// ### Parameters
496///
497/// * `Descriptions`: A comma-separated list. Each entry can be either a string literal
498///   or a tuple of two string literals `(Name, Description)`.
499///
500/// ### Generates
501///
502/// A list of documentation comments, one for each generic parameter, prepended to the
503/// function definition.
504///
505/// ### Examples
506///
507/// ```ignore
508/// // Invocation
509/// #[doc_type_params(
510///     "The type of the elements.",
511///     ("E", "The error type.")
512/// )]
513/// pub fn map<T, ERR>(...) { ... }
514///
515/// // Expanded code
516/// /// * `T`: The type of the elements.
517/// /// * `E`: The error type.
518/// pub fn map<T, ERR>(...) { ... }
519/// ```
520///
521/// ### Constraints
522///
523/// * The number of arguments must exactly match the number of generic parameters
524///   (including lifetimes, types, and const generics) in the function signature.
525#[proc_macro_attribute]
526pub fn doc_type_params(
527	attr: TokenStream,
528	item: TokenStream,
529) -> TokenStream {
530	doc_type_params_impl(attr.into(), item.into()).into()
531}
532
533/// Generates documentation for a function's parameters.
534///
535/// This macro analyzes the function signature and generates a documentation comment
536/// list based on the provided descriptions. It also handles curried return types.
537///
538/// ### Syntax
539///
540/// ```ignore
541/// #[doc_params(
542///     "Description for first parameter",
543///     ("overridden_name", "Description for second parameter"),
544///     ...
545/// )]
546/// pub fn function_name(params) -> impl Fn(...) { ... }
547/// ```
548///
549/// ### Parameters
550///
551/// * `Descriptions`: A comma-separated list. Each entry can be either a string literal
552///   or a tuple of two string literals `(Name, Description)`.
553///
554/// ### Generates
555///
556/// A list of documentation comments, one for each parameter, prepended to the
557/// function definition.
558///
559/// ### Examples
560///
561/// ```ignore
562/// // Invocation
563/// #[doc_params(
564///     "The first input value.",
565///     ("y", "The second input value.")
566/// )]
567/// pub fn foo(x: i32) -> impl Fn(i32) -> i32 { ... }
568///
569/// // Expanded code
570/// /// * `x`: The first input value.
571/// /// * `y`: The second input value.
572/// pub fn foo(x: i32) -> impl Fn(i32) -> i32 { ... }
573/// ```
574///
575/// ### Constraints
576///
577/// * The number of arguments must exactly match the number of function parameters
578///   (excluding `self` but including parameters from curried return types).
579#[proc_macro_attribute]
580pub fn doc_params(
581	attr: TokenStream,
582	item: TokenStream,
583) -> TokenStream {
584	doc_params_impl(attr.into(), item.into()).into()
585}