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//! - `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 type `Of`.
97///
98/// # Syntax
99///
100/// ```ignore
101/// impl_kind! {
102///     impl<GENERICS> for BrandType {
103///         type Of<PARAMS> = ConcreteType;
104///     }
105/// }
106/// ```
107///
108/// Or with where clause:
109///
110/// ```ignore
111/// impl_kind! {
112///     impl<E> for ResultBrand<E> where E: Debug {
113///         type Of<A> = Result<A, E>;
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#[proc_macro]
144pub fn impl_kind(input: TokenStream) -> TokenStream {
145	let input = parse_macro_input!(input as ImplKindInput);
146	impl_kind_impl(input).into()
147}
148
149/// Applies a brand to type arguments.
150///
151/// This macro projects a brand type to its concrete type using the appropriate
152/// Kind trait. It uses a syntax that mimics a fully qualified path, where the
153/// Kind trait is specified by its signature.
154///
155/// # Syntax
156///
157/// `Apply!(<Brand as Kind!( KindSignature )>::AssocType<Args>)`
158///
159/// * `Brand`: The brand type (e.g., `OptionBrand`).
160/// * `KindSignature`: A list of associated type definitions defining the Kind trait schema.
161/// * `AssocType`: The associated type to project (e.g., `Of`).
162/// * `Args`: The concrete arguments to apply.
163///
164/// # Examples
165///
166/// ```ignore
167/// // Applies MyBrand to lifetime 'static and type String.
168/// type Concrete = Apply!(<MyBrand as Kind!( type Of<'a, T>; )>::Of<'static, String>);
169///
170/// // Applies MyBrand to a generic type T with bounds.
171/// type Concrete = Apply!(<MyBrand as Kind!( type Of<T: Clone>; )>::Of<T>);
172///
173/// // Complex example with lifetimes, types, and output bounds.
174/// type Concrete = Apply!(<MyBrand as Kind!( type Of<'a, T: Clone + Debug>: Display; )>::Of<'a, T>);
175///
176/// // Use a custom associated type for projection.
177/// type Concrete = Apply!(<MyBrand as Kind!( type Of<T>; type SendOf<T>; )>::SendOf<T>);
178/// ```
179#[proc_macro]
180#[allow(non_snake_case)]
181pub fn Apply(input: TokenStream) -> TokenStream {
182	let input = parse_macro_input!(input as ApplyInput);
183	apply_impl(input).into()
184}