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}