air_derive_ir/lib.rs
1mod builder;
2mod helpers;
3
4use builder::impl_builder;
5use proc_macro::TokenStream;
6use syn::{DeriveInput, parse_macro_input};
7
8///
9/// Derive the [Builder] trait for a struct.
10/// Generates a type-level state machine for transitioning between states.
11///
12/// States correspond to which fields have been set or not.
13/// It takes into account the type of the fields.
14/// The following types are treated as optional fields:
15/// - [BackLink<T>]
16/// - [Vec<T>]
17/// - [Link<Vec<T>>]
18///
19/// For example, given the following struct:
20/// ```ignore
21/// #[derive(Builder, Eq, PartialEq, Debug)]
22/// #[enum_wrapper(Op)]
23/// struct Foo {
24/// a: BackLink<Owner>,
25/// b: Vec<BackLink<Owner>>,
26/// c: i32,
27/// d: Link<Op>,
28/// e: Vec<Link<Op>>,
29/// f: Link<Vec<Link<Op>>>,
30/// }
31/// ```
32///
33/// We now isolate the optional fields:
34/// - `a: BackLink<Owner>`
35/// - `b: Vec<BackLink<Owner>>`
36/// - `e: Vec<Link<Op>>`
37/// - `f: Link<Vec<Link<Op>>>`
38/// and the required fields:
39/// - `c: i32`
40/// - `d: Link<Op>`
41///
42/// We generate an initial state `FooBuilder0` with no fields set,
43/// except for the optional fields which are set to their default values.
44/// ```ignore
45/// let initial_state = [true, true, false, false, true, true];
46/// ```
47///
48/// We then generate a state with each of the required fields set,
49/// and every possible combination of those field states.
50/// We sort the states by the number of fields set, then by leftmost true bits.
51/// ```ignore
52/// let states = [
53/// // required: req, optional: opt
54/// //opt opt req req opt opt // state
55/// [true, true, false, false, true, true], // 0
56/// [true, true, true, false, true, true], // 1
57/// [true, true, false, true, true, true], // 2
58/// [true, true, true, true, true, true], // 3
59/// ];
60/// ```
61///
62/// We generate a transition table for each state,
63/// which maps from the current state to the next state.
64/// rows are indexed by the current state,
65/// columns by the method thst transitions to the next state.
66/// ```ignore
67/// let transitions = [
68/// /* 0: */ [0, 0, 1, 2, 0, 0],
69/// /* 1: */ [1, 1, 1, 3, 1, 1],
70/// /* 2: */ [2, 2, 3, 2, 2, 2],
71/// /* 3: */ [3, 3, 3, 3, 3, 3],
72/// ];
73/// ```
74///
75/// We then generate an implementation for each state,
76/// with a method for each field., which transitions to the next state.
77/// The [enum_wrapper] attribute is used to automatically wrap the struct in a Link<enum_wrapper>
78/// to reduce boilerplate.
79/// The only supported [enum_wrapper]s are `Op` and `Root`.
80///
81/// The following API is generated:
82/// ```ignore
83/// let a: Link<Owner> = todo!();
84/// let b0: Link<Owner> = todo!();
85/// let b1: Link<Owner> = todo!();
86/// let c = 42;
87/// let d: Link<Op> = todo!();
88/// let e0: Link<Op> = todo!();
89/// let e1: Link<Op> = todo!();
90/// let f0: Link<Op> = todo!();
91/// let f1: Link<Op> = todo!();
92/// let foo = Foo::builder()
93/// .a(a.clone())
94/// .b(b0.clone())
95/// .b(b1.clone())
96/// .c(c)
97/// .d(d.clone())
98/// .e(e0.clone())
99/// .e(e1.clone())
100/// .f(f0.clone())
101/// .f(f1.clone())
102/// .build();
103/// assert_eq!(foo.borrow().deref(),
104/// &Op::Foo(
105/// Foo {
106/// a: a.clone().into(),
107/// b: vec![b0.into(), b1.into()],
108/// c,
109/// d,
110/// e: vec![e0, e1],
111/// f: Link::new(vec![f0, f1]),
112/// }
113/// );
114/// ```
115#[proc_macro_derive(Builder, attributes(enum_wrapper))]
116pub fn derive_builder(input: TokenStream) -> TokenStream {
117 let ast = parse_macro_input!(input as DeriveInput);
118 impl_builder(&ast).into()
119}