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 syn::parse_macro_input;
18
19pub(crate) mod apply;
20pub(crate) mod canonicalize;
21pub(crate) mod def_kind;
22pub(crate) mod generate;
23pub(crate) mod impl_kind;
24pub(crate) mod parse;
25
26#[cfg(test)]
27mod property_tests;
28
29/// Generates the name of a `Kind` trait based on its signature.
30///
31/// This macro takes a list of associated type definitions, similar to a trait definition.
32///
33/// # Examples
34///
35/// ```ignore
36/// // Simple signature
37/// let name = Kind!(type Of<T>;);
38///
39/// // Signature with bounds and lifetimes
40/// let name = Kind!(type Of<'a, T: Display>: Debug;);
41///
42/// // Multiple associated types
43/// let name = Kind!(
44///     type Of<T>;
45///     type SendOf<T>: Send;
46/// );
47/// ```
48///
49/// # Limitations
50///
51/// Due to Rust syntax restrictions, this macro cannot be used directly in positions where a
52/// concrete path is expected by the parser, such as:
53/// * Supertrait bounds: `trait MyTrait: Kind!(...) {}` (Invalid)
54/// * Type aliases: `type MyKind = Kind!(...);` (Invalid)
55/// * Trait aliases: `trait MyKind = Kind!(...);` (Invalid)
56///
57/// In these cases, you must use the generated name directly (e.g., `Kind_...`).
58#[proc_macro]
59#[allow(non_snake_case)]
60pub fn Kind(input: TokenStream) -> TokenStream {
61	let input = parse_macro_input!(input as KindInput);
62	let name = generate_name(&input);
63	quote!(#name).into()
64}
65
66/// Defines a new `Kind` trait.
67///
68/// This macro generates a trait definition for a Higher-Kinded Type signature.
69/// It takes a list of associated type definitions, similar to a trait definition.
70///
71/// # Examples
72///
73/// ```ignore
74/// // Simple definition
75/// def_kind!(type Of<T>;);
76///
77/// // Definition with bounds and lifetimes
78/// def_kind!(type Of<'a, T: Display>: Debug;);
79///
80/// // Multiple associated types
81/// def_kind!(
82///     type Of<T>;
83///     type SendOf<T>: Send;
84/// );
85/// ```
86#[proc_macro]
87pub fn def_kind(input: TokenStream) -> TokenStream {
88	let input = parse_macro_input!(input as KindInput);
89	def_kind_impl(input).into()
90}
91
92/// Implements a `Kind` trait for a brand.
93///
94/// This macro simplifies the implementation of a generated `Kind` trait for a specific
95/// brand type. It infers the correct `Kind` trait to implement based on the signature
96/// of the associated types provided in the block.
97///
98/// The signature (names, parameters, and bounds) of the associated types must match
99/// the definition used in [`def_kind!`] or [`Kind!`] to ensure the correct trait is implemented.
100///
101/// # Syntax
102///
103/// ```ignore
104/// impl_kind! {
105///     // Optional impl generics
106///     impl<Generics> for BrandType
107///     // Optional where clause
108///     where Bounds
109///     {
110///         type AssocName<Params> = ConcreteType;
111///         // ... more associated types
112///     }
113/// }
114/// ```
115///
116/// # Examples
117///
118/// ```ignore
119/// // Simple implementation
120/// impl_kind! {
121///     for OptionBrand {
122///         type Of<A> = Option<A>;
123///     }
124/// }
125///
126/// // Implementation with generics
127/// impl_kind! {
128///     impl<E> for ResultBrand<E> {
129///         type Of<A> = Result<A, E>;
130///     }
131/// }
132///
133/// // Implementation with where clause and multiple types
134/// impl_kind! {
135///     impl<E> for MyBrand<E> where E: Clone {
136///         type Of<A> = MyType<A, E>;
137///         type SendOf<A> = MySendType<A, E>;
138///     }
139/// }
140///
141/// // Implementation matching a `Kind` with bounds
142/// // Corresponds to: def_kind!(type Of<T: Display>;);
143/// impl_kind! {
144///     for DisplayBrand {
145///         // Bounds here are used to infer the correct `Kind` trait name
146///         type Of<T: Display> = DisplayType<T>;
147///     }
148/// }
149/// ```
150#[proc_macro]
151pub fn impl_kind(input: TokenStream) -> TokenStream {
152	let input = parse_macro_input!(input as ImplKindInput);
153	impl_kind_impl(input).into()
154}
155
156/// Applies a brand to type arguments.
157///
158/// This macro projects a brand type to its concrete type using the appropriate
159/// `Kind` trait. It uses a syntax that mimics a fully qualified path, where the
160/// `Kind` trait is specified by its signature.
161///
162/// # Syntax
163///
164/// ```ignore
165/// Apply!(<Brand as Kind!( KindSignature )>::AssocType<Args>)
166/// ```
167///
168/// * `Brand`: The brand type (e.g., `OptionBrand`).
169/// * `KindSignature`: A list of associated type definitions defining the `Kind` trait schema.
170/// * `AssocType`: The associated type to project (e.g., `Of`).
171/// * `Args`: The concrete arguments to apply.
172///
173/// # Examples
174///
175/// ```ignore
176/// // Applies MyBrand to lifetime 'static and type String.
177/// type Concrete = Apply!(<MyBrand as Kind!( type Of<'a, T>; )>::Of<'static, String>);
178///
179/// // Applies MyBrand to a generic type T with bounds.
180/// type Concrete = Apply!(<MyBrand as Kind!( type Of<T: Clone>; )>::Of<T>);
181///
182/// // Complex example with lifetimes, types, and output bounds.
183/// type Concrete = Apply!(<MyBrand as Kind!( type Of<'a, T: Clone + Debug>: Display; )>::Of<'a, T>);
184///
185/// // Use a custom associated type for projection.
186/// type Concrete = Apply!(<MyBrand as Kind!( type Of<T>; type SendOf<T>; )>::SendOf<T>);
187/// ```
188#[proc_macro]
189#[allow(non_snake_case)]
190pub fn Apply(input: TokenStream) -> TokenStream {
191	let input = parse_macro_input!(input as ApplyInput);
192	apply_impl(input).into()
193}