fp_macros/
lib.rs

1//! Procedural macros for the `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
8use apply::{ApplyInput, apply_impl};
9use def_kind::def_kind_impl;
10use generate::generate_name;
11use impl_kind::{ImplKindInput, impl_kind_impl};
12use parse::KindInput;
13use proc_macro::TokenStream;
14use quote::quote;
15use syn::parse_macro_input;
16
17pub(crate) mod apply;
18pub(crate) mod canonicalize;
19pub(crate) mod def_kind;
20pub(crate) mod generate;
21pub(crate) mod impl_kind;
22pub(crate) mod parse;
23
24#[cfg(test)]
25mod property_tests;
26
27/// Generates the name of a Kind trait based on its signature.
28///
29/// This macro takes three parenthesized groups representing the signature:
30/// 1. **Lifetimes**: A comma-separated list of lifetimes (e.g., `('a, 'b)`).
31/// 2. **Types**: A comma-separated list of types with optional bounds (e.g., `(T, U: Display)`).
32/// 3. **Output Bounds**: A `+`-separated list of bounds on the output type (e.g., `(Display + Clone)`).
33///
34/// # Example
35///
36/// ```ignore
37/// // Generates the name for a Kind with:
38/// // - 1 lifetime ('a)
39/// // - 1 type parameter (T) bounded by Display and Clone
40/// // - Output type bounded by Debug
41/// let name = Kind!(('a), (T: Display + Clone), (Debug));
42/// ```
43///
44/// # Limitations
45///
46/// Due to Rust syntax restrictions, this macro cannot be used directly in positions where a
47/// concrete path is expected by the parser, such as:
48/// * Supertrait bounds: `trait MyTrait: Kind!(...) {}` (Invalid)
49/// * Type aliases: `type MyKind = Kind!(...);` (Invalid)
50/// * Trait aliases: `trait MyKind = Kind!(...);` (Invalid)
51///
52/// In these cases, you must use the generated name directly (e.g., `Kind_...`).
53#[proc_macro]
54#[allow(non_snake_case)]
55pub fn Kind(input: TokenStream) -> TokenStream {
56	let input = parse_macro_input!(input as KindInput);
57	let name = generate_name(&input);
58	quote!(#name).into()
59}
60
61/// Defines a new Kind trait.
62///
63/// This macro generates a trait definition for a Higher-Kinded Type signature.
64/// It takes the same three arguments as `Kind!`:
65/// 1. **Lifetimes**
66/// 2. **Types**
67/// 3. **Output Bounds**
68///
69/// The generated trait includes a single associated type `Of`.
70///
71/// # Example
72///
73/// ```ignore
74/// // Defines a Kind trait for a signature with:
75/// // - 1 lifetime ('a)
76/// // - 1 type parameter (T) bounded by Display
77/// // - Output type bounded by Debug
78/// def_kind!(('a), (T: Display), (Debug));
79/// ```
80#[proc_macro]
81pub fn def_kind(input: TokenStream) -> TokenStream {
82	let input = parse_macro_input!(input as KindInput);
83	def_kind_impl(input).into()
84}
85
86/// Implements a Kind trait for a brand.
87///
88/// This macro simplifies the implementation of a generated Kind trait for a specific
89/// brand type. It infers the correct Kind trait to implement based on the signature
90/// of the associated type `Of`.
91///
92/// # Syntax
93///
94/// ```ignore
95/// impl_kind! {
96///     impl<GENERICS> for BrandType {
97///         type Of<PARAMS> = ConcreteType;
98///     }
99/// }
100/// ```
101///
102/// Or with where clause:
103///
104/// ```ignore
105/// impl_kind! {
106///     impl<E> for ResultBrand<E> where E: Debug {
107///         type Of<A> = Result<A, E>;
108///     }
109/// }
110/// ```
111///
112/// # Example
113///
114/// ```ignore
115/// impl_kind! {
116///     for OptionBrand {
117///         type Of<A> = Option<A>;
118///     }
119/// }
120/// ```
121#[proc_macro]
122pub fn impl_kind(input: TokenStream) -> TokenStream {
123	let input = parse_macro_input!(input as ImplKindInput);
124	impl_kind_impl(input).into()
125}
126
127/// Applies a brand to type arguments.
128///
129/// This macro projects a brand type to its concrete type using the appropriate
130/// Kind trait. It uses named parameters syntax.
131///
132/// # Modes
133///
134/// The macro supports two modes of operation:
135///
136/// 1. **Unified Signature Mode** (Recommended): Uses a single `signature` parameter to specify both
137///    the schema (for Kind trait name generation) and the concrete values (for projection).
138/// 2. **Explicit Kind Mode** (Advanced): Uses an explicit `kind` parameter along with separate
139///    `lifetimes` and `types` parameters.
140///
141/// # Parameters
142///
143/// * `brand`: (Required) The brand type (e.g., `OptionBrand`).
144/// * `signature`: (Mode 1) The unified signature containing both schema and values.
145/// * `kind`: (Mode 2) An explicit Kind trait to use.
146/// * `lifetimes`: (Mode 2) Lifetime arguments to apply. Required with `kind`.
147/// * `types`: (Mode 2) Type arguments to apply. Required with `kind`.
148///
149/// # Unified Signature Syntax
150///
151/// The `signature` parameter uses a syntax similar to a function signature:
152/// `(param1, param2, ...) -> OutputBounds`
153///
154/// * **Lifetimes**: Specified as `'a`, `'static`, etc.
155/// * **Types**: Specified as `Type` or `Type: Bounds`.
156///   * The `Type` part is used as the concrete value for projection.
157///   * The `Bounds` part (optional) is used for Kind trait name generation.
158///
159/// # Examples
160///
161/// ## Unified Signature Mode (Recommended)
162///
163/// ```ignore
164/// // Applies MyBrand to lifetime 'static and type String.
165/// // The schema is inferred as: 1 lifetime, 1 type parameter (unbounded).
166/// type Concrete = Apply!(
167///     brand: MyBrand,
168///     signature: ('static, String)
169/// );
170///
171/// // Applies MyBrand to a generic type T with bounds.
172/// // The schema is inferred as: 1 type parameter with Clone bound.
173/// type Concrete = Apply!(
174///     brand: MyBrand,
175///     signature: (T: Clone)
176/// );
177///
178/// // Complex example with lifetimes, types, and output bounds.
179/// type Concrete = Apply!(
180///     brand: MyBrand,
181///     signature: ('a, T: Clone + Debug) -> Display
182/// );
183/// ```
184///
185/// ## Explicit Kind Mode (Advanced)
186///
187/// Use this mode when you need to specify a custom Kind trait directly.
188///
189/// ```ignore
190/// // Applies OptionBrand using an explicit Kind trait.
191/// type Concrete = Apply!(
192///     brand: OptionBrand,
193///     kind: SomeKind,
194///     lifetimes: ('a),
195///     types: (String)
196/// );
197/// ```
198#[proc_macro]
199#[allow(non_snake_case)]
200pub fn Apply(input: TokenStream) -> TokenStream {
201	let input = parse_macro_input!(input as ApplyInput);
202	apply_impl(input).into()
203}