typestate_builder/
lib.rs

1// Copyright (c) 2024 Andy Allison
2//
3// Licensed under either of
4//
5// * MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
6// * Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
7//
8// at your option.
9//
10// Unless you explicitly state otherwise, any contribution intentionally submitted
11// for inclusion in the work by you, as defined in the Apache-2.0 license, shall
12// be dual licensed as above, without any additional terms or conditions.
13
14#![warn(missing_docs)]
15
16/*!
17# typestate-builder
18
19`TypestateBuilder` is a Rust procedural macro that enables the creation of builder patterns using the typestate design pattern. This macro ensures that your structs are built in a way that enforces compile-time safety, ensuring that required fields are initialized before the struct is created.
20
21## Table of Contents
22
23- [Features](#features)
24- [Usage](#usage)
25  - [Deriving the Macro](#deriving-the-macro)
26  - [Example](#example)
27- [How It Works](#how-it-works)
28- [Code Expanded](#code-expanded)
29- [Limitations](#limitations)
30- [License](#license)
31
32## Features
33
34- Enforced Typestate Pattern: Leveraging Rust's type system, the macro ensures that required fields are set before a struct can be created.
35- No Runtime Checks: The macro generates the necessary types and performs checks at compile time, eliminating the need for Option or Result types.
36- Compile-Time Safety: All required fields must be initialized before creating an instance of the struct, promoting safer code.
37- Support for Named and Tuple Structs: Works seamlessly with both named and tuple structs, with clear error messages for unsupported configurations.
38- Fluent and Intuitive Syntax: Offers a simple and idiomatic Rust syntax for creating builders, enhancing code readability and usability.
39
40## Example
41
42Here’s a basic example demonstrating how to use the `TypestateBuilder` macro:
43
44```rust
45use typestate_builder::TypestateBuilder;
46
47#[derive(Debug, TypestateBuilder)] // `Debug` is not a must.
48struct Person {
49    name: String,
50    age: u32,
51    email: Option<String>,
52}
53
54let person = Person::builder()
55    .name("Alice Johnson".to_string())
56    .age(30)
57    .email(Some("alice@example.com".to_string()))
58    .build();
59println!("Created person: {:?}", person);
60```
61
62In this example, the Person struct uses the `TypestateBuilder` derive macro to create a builder. Each field can be set in a fluent interface style, and the build method assembles the `Person` instance once all required fields have been set.
63
64## How It Works
65
66- State Management: The macro generates intermediate state structs for each field of the struct being built. Each state struct represents a stage in the building process.
67
68- Method Chaining: For each field, a method is generated that accepts a value for that field and returns a new builder state, ensuring that only valid transitions are allowed.
69
70- Final Assembly: The `build` method assembles the final struct once all required fields have been set, preventing the creation of incomplete instances.
71
72- Graph analysis: This crate internally analyzes the sub-elements of structures with graph and describes the relationships between them. Thus, the source of the final generated code is derived from this analysis.
73
74## Code Expanded
75
76The expanded version of the above code is like this:
77
78```rust
79struct PersonBuilder<NameGenericParam, AgeGenericParam, EmailGenericParam> {
80    name: NameGenericParam,
81    age: AgeGenericParam,
82    email: EmailGenericParam,
83}
84struct PersonBuilderNameEmpty;
85struct PersonBuilderNameAdded(String);
86struct PersonBuilderAgeEmpty;
87struct PersonBuilderAgeAdded(u32);
88struct PersonBuilderEmailEmpty;
89struct PersonBuilderEmailAdded(Option<String>);
90impl Person {
91    fn builder() -> PersonBuilder<
92        PersonBuilderNameEmpty,
93        PersonBuilderAgeEmpty,
94        PersonBuilderEmailEmpty,
95    > {
96        PersonBuilder {
97            name: PersonBuilderNameEmpty,
98            age: PersonBuilderAgeEmpty,
99            email: PersonBuilderEmailEmpty,
100        }
101    }
102}
103impl<
104    AgeGenericParam,
105    EmailGenericParam,
106> PersonBuilder<PersonBuilderNameEmpty, AgeGenericParam, EmailGenericParam> {
107    fn name(
108        self,
109        name: String,
110    ) -> PersonBuilder<PersonBuilderNameAdded, AgeGenericParam, EmailGenericParam> {
111        PersonBuilder {
112            name: PersonBuilderNameAdded(name),
113            age: self.age,
114            email: self.email,
115        }
116    }
117}
118impl<
119    NameGenericParam,
120    EmailGenericParam,
121> PersonBuilder<NameGenericParam, PersonBuilderAgeEmpty, EmailGenericParam> {
122    fn age(
123        self,
124        age: u32,
125    ) -> PersonBuilder<NameGenericParam, PersonBuilderAgeAdded, EmailGenericParam> {
126        PersonBuilder {
127            name: self.name,
128            age: PersonBuilderAgeAdded(age),
129            email: self.email,
130        }
131    }
132}
133impl<
134    NameGenericParam,
135    AgeGenericParam,
136> PersonBuilder<NameGenericParam, AgeGenericParam, PersonBuilderEmailEmpty> {
137    fn email(
138        self,
139        email: Option<String>,
140    ) -> PersonBuilder<NameGenericParam, AgeGenericParam, PersonBuilderEmailAdded> {
141        PersonBuilder {
142            name: self.name,
143            age: self.age,
144            email: PersonBuilderEmailAdded(email),
145        }
146    }
147}
148impl PersonBuilder<
149    PersonBuilderNameAdded,
150    PersonBuilderAgeAdded,
151    PersonBuilderEmailAdded,
152> {
153    fn build(self) -> Person {
154        Person {
155            name: self.name.0,
156            age: self.age.0,
157            email: self.email.0,
158        }
159    }
160}
161```
162
163## Possible Limitations
164
165In fact, I’m not entirely sure what the upper limit is for the most complex struct that this crate can handle. However, I’ve added [dozens of complex structs here](https://github.com/aalowlevel/typestate-builder/blob/master/src/lib.rs) for testing purposes, and the crate successfully handles all of them. If you have any new ideas for testing structs, feel free to send me a PR.
166
167## License
168
169`TypestateBuilder` is dual-licensed under the MIT and Apache 2.0 licenses. See the LICENSE-MIT and LICENSE-APACHE files for details.
170Derive-macro-based generator that combines `Typestate` and `Builder` patterns. */
171
172pub use typestate_builder_macro::TypestateBuilder;
173
174#[cfg(test)]
175#[allow(dead_code)]
176mod tests {
177    use std::{
178        alloc::{GlobalAlloc, Layout},
179        collections::HashMap,
180        marker::PhantomData,
181    };
182
183    use typestate_builder_macro::TypestateBuilder;
184
185    #[derive(TypestateBuilder)]
186    struct BasicStructWithLifetimes<'a> {
187        name: &'a str,
188        age: u32,
189        description: &'a str,
190    }
191
192    #[derive(TypestateBuilder)]
193    struct GenericStruct<T, U>
194    where
195        T: Copy,
196        U: Copy,
197    {
198        field1: T,
199        field2: U,
200    }
201
202    #[derive(TypestateBuilder)]
203    struct InnerStruct {
204        x: i32,
205        y: i32,
206    }
207
208    #[derive(TypestateBuilder)]
209    struct OuterStruct {
210        name: String,
211        inner: InnerStruct,
212        active: bool,
213    }
214
215    /* Panic: TypestateBuilder only supports structs */
216    // #[derive(TypestateBuilder)]
217    enum Status {
218        Active,
219        Inactive,
220        Suspended,
221    }
222
223    #[derive(TypestateBuilder)]
224    struct EnumWithinStruct {
225        user_id: u64,
226        status: Status,
227    }
228
229    #[derive(TypestateBuilder)]
230    struct StructWithOptionals {
231        title: Option<String>,
232        description: Option<String>,
233        rating: Option<u8>,
234    }
235
236    #[derive(TypestateBuilder)]
237    struct TupleStruct(i32, f64, String, Option<u8>);
238
239    #[derive(TypestateBuilder)]
240    struct ComplexStruct {
241        name: String,
242        values: Vec<i32>,
243        map: HashMap<String, u64>,
244    }
245
246    #[derive(TypestateBuilder)]
247    struct RecursiveStruct {
248        value: i32,
249        next: Option<Box<RecursiveStruct>>,
250    }
251
252    #[derive(TypestateBuilder)]
253    struct PhantomStruct<T> {
254        data: i32,
255        marker: PhantomData<T>,
256    }
257
258    #[derive(TypestateBuilder)]
259    struct MultiBoundGeneric<T, U>
260    where
261        T: Clone + Default + std::fmt::Debug,
262        U: Into<String> + Copy,
263    {
264        item1: T,
265        item2: U,
266        data: Vec<T>,
267    }
268
269    trait Processor {
270        type Input;
271        type Output;
272
273        fn process(&self, input: Self::Input) -> Self::Output;
274    }
275
276    #[derive(TypestateBuilder)]
277    struct StructWithAssociatedTypes<T>
278    where
279        T: Processor,
280    {
281        processor: T,
282        input: T::Input,
283    }
284
285    trait Container {
286        type Item;
287
288        fn add_item(&mut self, item: Self::Item);
289        fn get_item(&self) -> &Self::Item;
290    }
291
292    #[derive(TypestateBuilder)]
293    struct GenericContainer<T>
294    where
295        T: Container,
296    {
297        container: T,
298    }
299
300    #[derive(TypestateBuilder)]
301    struct AdvancedPhantomStruct<'a, T>
302    where
303        T: 'a + Clone,
304    {
305        reference: &'a T,
306        marker: PhantomData<T>,
307    }
308
309    trait Drawable {
310        fn draw(&self);
311    }
312
313    #[derive(TypestateBuilder)]
314    struct GenericWithTraitObject<'a, T>
315    where
316        T: 'a + Drawable,
317    {
318        drawable_item: Box<dyn Drawable + 'a>,
319        generic_item: T,
320    }
321
322    #[derive(TypestateBuilder)]
323    struct StructWithFunctionPointer<T, F>
324    where
325        F: Fn(T) -> T,
326    {
327        func: F,
328        value: T,
329    }
330
331    trait Graph {
332        type Node<'a>
333        where
334            Self: 'a;
335        type Edge<'a>
336        where
337            Self: 'a;
338
339        fn get_node(&self) -> Self::Node<'_>;
340        fn get_edge(&self) -> Self::Edge<'_>;
341    }
342
343    #[derive(TypestateBuilder)]
344    struct GraphStruct<T>
345    where
346        T: Graph,
347    {
348        graph: T,
349    }
350
351    impl<T> GraphStruct<T>
352    where
353        T: Graph,
354    {
355        fn display_node(&self) {
356            // let node = self.graph.get_node();
357            // Implement some logic to display the node
358        }
359    }
360
361    #[derive(TypestateBuilder)]
362    struct ArrayWrapper<T, const N: usize> {
363        items: [T; N],
364    }
365
366    impl<T, const N: usize> ArrayWrapper<T, N> {
367        fn new(items: [T; N]) -> Self {
368            ArrayWrapper { items }
369        }
370
371        fn get_length(&self) -> usize {
372            N
373        }
374    }
375
376    trait Action {
377        fn execute(&self);
378    }
379
380    #[derive(TypestateBuilder)]
381    struct Dispatcher<T: Action> {
382        handler: Box<dyn Action>,
383        generic_handler: T,
384    }
385
386    impl<T: Action> Dispatcher<T> {
387        fn run(&self) {
388            self.handler.execute();
389            self.generic_handler.execute();
390        }
391    }
392
393    #[derive(TypestateBuilder)]
394    struct NestedGenerics<'a, T, U>
395    where
396        T: 'a + Copy + Clone,
397        U: 'a + AsRef<T> + Clone,
398    {
399        value: &'a T,
400        ref_container: U,
401    }
402
403    impl<'a, T, U> NestedGenerics<'a, T, U>
404    where
405        T: 'a + Copy + Clone,
406        U: 'a + AsRef<T> + Clone,
407    {
408        fn get_value(&self) -> T {
409            *self.value
410        }
411    }
412
413    #[derive(TypestateBuilder)]
414    struct Tree<'a, T, U>
415    where
416        T: 'a + Clone,
417        U: 'a + Clone,
418    {
419        value: &'a T,
420        children: Vec<Tree<'a, U, T>>,
421    }
422
423    impl<'a, T, U> Tree<'a, T, U>
424    where
425        T: 'a + Clone,
426        U: 'a + Clone,
427    {
428        fn new(value: &'a T) -> Self {
429            Tree {
430                value,
431                children: Vec::new(),
432            }
433        }
434
435        fn add_child(&mut self, child: Tree<'a, U, T>) {
436            self.children.push(child);
437        }
438    }
439
440    #[derive(TypestateBuilder)]
441    struct ComplexGraph<'a, N, E>
442    where
443        N: 'a,
444        E: 'a,
445    {
446        nodes: Vec<N>,
447        edges: Vec<(N, N, E)>,
448        _marker: PhantomData<&'a ()>,
449    }
450
451    #[derive(TypestateBuilder)]
452    struct OptionWrapper<T, const IS_SOME: bool> {
453        value: Option<T>,
454    }
455
456    impl<T> OptionWrapper<T, true> {
457        fn new(value: T) -> Self {
458            OptionWrapper { value: Some(value) }
459        }
460
461        fn get(&self) -> &T {
462            self.value.as_ref().unwrap()
463        }
464    }
465
466    impl<T> OptionWrapper<T, false> {
467        fn new_none() -> Self {
468            OptionWrapper { value: None }
469        }
470    }
471
472    #[derive(TypestateBuilder)]
473    struct DeeplyNested<'a, 'b, T, U>
474    where
475        T: 'a + Copy,
476        U: 'b + Clone,
477    {
478        level_one: &'a T,
479        level_two: &'b U,
480        sub_nested: Vec<&'a DeeplyNested<'a, 'b, T, U>>,
481    }
482
483    impl<'a, 'b, T, U> DeeplyNested<'a, 'b, T, U>
484    where
485        T: 'a + Copy,
486        U: 'b + Clone,
487    {
488        fn new(level_one: &'a T, level_two: &'b U) -> Self {
489            DeeplyNested {
490                level_one,
491                level_two,
492                sub_nested: Vec::new(),
493            }
494        }
495
496        fn add_nested(&mut self, nested: &'a DeeplyNested<'a, 'b, T, U>) {
497            self.sub_nested.push(nested);
498        }
499    }
500
501    #[derive(TypestateBuilder)]
502    struct CustomAllocator<T, A: GlobalAlloc> {
503        allocator: A,
504        data: *mut T,
505    }
506
507    impl<T, A: GlobalAlloc> CustomAllocator<T, A> {
508        fn allocate(&mut self) -> *mut T {
509            let layout = Layout::new::<T>();
510            unsafe { self.allocator.alloc(layout) as *mut T }
511        }
512
513        fn deallocate(&mut self, ptr: *mut T) {
514            let layout = Layout::new::<T>();
515            unsafe { self.allocator.dealloc(ptr as *mut u8, layout) }
516        }
517    }
518
519    #[derive(TypestateBuilder)]
520    struct ComplexTuple<'a, T, U, V>(
521        T,                 // Generic type T
522        &'a U,             // Reference to type U with lifetime 'a
523        Option<V>,         // Optional value of type V
524        [T; 3],            // An array with 3 elements of type T
525        fn(T, U) -> V,     // Function pointer: takes T, U, returns V
526        &'a str,           // Static string slice with lifetime 'a
527        Result<V, String>, // A Result type with V for success, String for error
528    );
529}