Skip to main content

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}