type_state_builder/
lib.rs

1//! Type-State Builder Pattern Implementation
2//!
3//! This crate provides a derive macro for implementing the Type-State Builder pattern
4//! in Rust, enabling compile-time validation of required fields and zero-cost builder
5//! abstractions.
6//!
7//! # Overview
8//!
9//! The Type-State Builder pattern uses Rust's type system to enforce compile-time
10//! validation of required fields. It automatically selects between two builder patterns:
11//!
12//! - **Type-State Builder**: For structs with required fields, providing compile-time
13//!   safety that prevents calling `build()` until all required fields are set
14//! - **Regular Builder**: For structs with only optional fields, providing a simple
15//!   builder with immediate `build()` availability
16//!
17//! # Key Features
18//!
19//! - **Zero Runtime Cost**: All validation happens at compile time
20//! - **Automatic Pattern Selection**: Chooses the best builder pattern for your struct
21//! - **Comprehensive Generic Support**: Handles complex generic types and lifetimes
22//! - **Flexible Configuration**: Extensive attribute-based customization
23//! - **Excellent Error Messages**: Clear guidance for fixing configuration issues
24//!
25//! # Quick Start
26//!
27//! Add the derive macro to your struct and mark required fields:
28//!
29//! ```
30//! use type_state_builder::TypeStateBuilder;
31//!
32//! #[derive(TypeStateBuilder)]
33//! struct User {
34//!     #[builder(required)]
35//!     name: String,
36//!     
37//!     #[builder(required)]
38//!     email: String,
39//!     
40//!     age: Option<u32>,
41//!     active: bool, // Will use Default::default()
42//! }
43//!
44//! // Usage - this enforces that name and email are set
45//! let user = User::builder()
46//!     .name("Alice".to_string())
47//!     .email("alice@example.com".to_string())
48//!     .age(Some(30))
49//!     .build();
50//! ```
51//!
52//! # Supported Attributes
53//!
54//! ## Struct-level Attributes
55//!
56//! - `#[builder(build_method = "method_name")]` - Custom build method name
57//! - `#[builder(setter_prefix = "prefix_")]` - Prefix for all setter method names
58//!
59//! ## Field-level Attributes
60//!
61//! - `#[builder(required)]` - Mark field as required
62//! - `#[builder(setter_name = "name")]` - Custom setter method name
63//! - `#[builder(setter_prefix = "prefix_")]` - Custom prefix for setter method name
64//! - `#[builder(default = "expr")]` - Custom default value expression
65//! - `#[builder(skip_setter)]` - Don't generate setter (requires default)
66//!
67//! # Advanced Examples
68//!
69//! ## Custom Defaults and Setter Names
70//!
71//! ```
72//! use type_state_builder::TypeStateBuilder;
73//!
74//! #[derive(TypeStateBuilder)]
75//! #[builder(build_method = "create")]
76//! struct Document {
77//!     #[builder(required)]
78//!     title: String,
79//!     
80//!     #[builder(required, setter_name = "set_content")]
81//!     content: String,
82//!     
83//!     #[builder(default = "42")]
84//!     page_count: u32,
85//!     
86//!     #[builder(default = "String::from(\"draft\")", skip_setter)]
87//!     status: String,
88//! }
89//!
90//! let doc = Document::builder()
91//!     .title("My Document".to_string())
92//!     .set_content("Document content here".to_string())
93//!     .create(); // Custom build method name
94//! ```
95//!
96//! ## Generic Types and Lifetimes
97//!
98//! ```
99//! use type_state_builder::TypeStateBuilder;
100//!
101//! #[derive(TypeStateBuilder)]
102//! struct Container<T: Clone>
103//! where
104//!     T: Send
105//! {
106//!     #[builder(required)]
107//!     value: T,
108//!     
109//!     #[builder(required)]
110//!     name: String,
111//!     
112//!     tags: Vec<String>,
113//! }
114//!
115//! let container = Container::builder()
116//!     .value(42)
117//!     .name("test".to_string())
118//!     .tags(vec!["tag1".to_string()])
119//!     .build();
120//! ```
121//!
122//! ## Setter Prefix Examples
123//!
124//! ```
125//! use type_state_builder::TypeStateBuilder;
126//!
127//! // Struct-level setter prefix applies to all fields
128//! #[derive(TypeStateBuilder)]
129//! #[builder(setter_prefix = "with_")]
130//! struct Config {
131//!     #[builder(required)]
132//!     host: String,
133//!     
134//!     #[builder(required)]
135//!     port: u16,
136//!     
137//!     timeout: Option<u32>,
138//! }
139//!
140//! let config = Config::builder()
141//!     .with_host("localhost".to_string())
142//!     .with_port(8080)
143//!     .with_timeout(Some(30))
144//!     .build();
145//! ```
146//!
147//! ```
148//! use type_state_builder::TypeStateBuilder;
149//!
150//! // Field-level setter prefix overrides struct-level prefix
151//! #[derive(TypeStateBuilder)]
152//! #[builder(setter_prefix = "with_")]
153//! struct Database {
154//!     #[builder(required)]
155//!     connection_string: String,
156//!     
157//!     #[builder(required, setter_prefix = "set_")]
158//!     credentials: String,
159//!     
160//!     #[builder(setter_name = "timeout_seconds")]
161//!     timeout: Option<u32>,
162//! }
163//!
164//! let db = Database::builder()
165//!     .with_connection_string("postgresql://...".to_string())
166//!     .set_credentials("user:pass".to_string())
167//!     .with_timeout_seconds(Some(60))
168//!     .build();
169//! ```
170//!
171//! ## Optional-Only Structs (Regular Builder)
172//!
173//! ```
174//! use type_state_builder::TypeStateBuilder;
175//!
176//! // No required fields = regular builder pattern
177//! #[derive(TypeStateBuilder)]
178//! struct Settings {
179//!     debug: bool,
180//!     max_connections: Option<u32>,
181//!     
182//!     #[builder(default = "\"default.log\".to_string()")]
183//!     log_file: String,
184//!     
185//!     #[builder(skip_setter, default = "42")]
186//!     magic_number: i32,
187//! }
188//!
189//! // Can call build() immediately since no required fields
190//! let settings = Settings::builder()
191//!     .debug(true)
192//!     .max_connections(Some(100))
193//!     .build();
194//! ```
195//!
196//! # Error Prevention
197//!
198//! The macro prevents common mistakes at compile time:
199//!
200//! ```compile_fail
201//! use type_state_builder::TypeStateBuilder;
202//!
203//! #[derive(TypeStateBuilder)]
204//! struct User {
205//!     #[builder(required)]
206//!     name: String,
207//! }
208//!
209//! let user = User::builder().build(); // ERROR: required field not set
210//! ```
211//!
212//! ```compile_fail
213//! use type_state_builder::TypeStateBuilder;
214//!
215//! #[derive(TypeStateBuilder)]
216//! struct BadConfig {
217//!     #[builder(required, default = "test")]  // ERROR: Invalid combination
218//!     name: String,
219//! }
220//! ```
221//!
222//! # Module Organization
223//!
224//! The crate is organized into several modules that handle different aspects
225//! of the builder generation process. Most users will only interact with the
226//! [`TypeStateBuilder`] derive macro.
227//!
228//! For complete documentation, examples, and guides, see the
229//! [README](https://github.com/welf/type-state-builder#readme).
230
231use proc_macro::TokenStream;
232use syn::{parse_macro_input, DeriveInput};
233
234// Internal modules - not exported due to proc-macro restrictions
235mod analysis;
236mod attributes;
237mod generation;
238mod utils;
239mod validation;
240
241/// Derives a type-safe builder for a struct with compile-time validation of required fields.
242///
243/// This macro automatically generates an appropriate builder pattern based on the struct
244/// configuration:
245/// - **Type-State Builder** for structs with required fields
246/// - **Regular Builder** for structs with only optional fields
247///
248/// # Basic Usage
249///
250/// ```
251/// use type_state_builder::TypeStateBuilder;
252///
253/// #[derive(TypeStateBuilder)]
254/// struct Person {
255///     #[builder(required)]
256///     name: String,
257///     age: Option<u32>,
258/// }
259///
260/// let person = Person::builder()
261///     .name("Alice".to_string())
262///     .build();
263/// ```
264///
265/// # Attribute Reference
266///
267/// ## Struct Attributes
268///
269/// - `#[builder(build_method = "name")]` - Custom build method name (default: "build")
270/// - `#[builder(setter_prefix = "prefix_")]` - Prefix for all setter method names
271///
272/// ## Field Attributes
273///
274/// - `#[builder(required)]` - Field must be set before build() (creates type-state builder)
275/// - `#[builder(setter_name = "name")]` - Custom setter method name
276/// - `#[builder(setter_prefix = "prefix_")]` - Custom prefix for this field's setter (overrides struct-level)
277/// - `#[builder(default = "expr")]` - Custom default value (must be valid Rust expression)
278/// - `#[builder(skip_setter)]` - Don't generate setter method (requires default value)
279///
280/// # Generated Methods
281///
282/// The macro generates:
283/// - `YourStruct::builder()` - Creates a new builder instance
284/// - `.field_name(value)` - Setter methods for each field (unless skipped)
285/// - `.build()` - Constructs the final instance (or custom name from `build_method`)
286///
287/// # Compile-Time Safety
288///
289/// The type-state builder (used when there are required fields) prevents:
290/// - Calling `build()` before setting required fields
291/// - Setting the same required field multiple times
292/// - Invalid attribute combinations
293///
294/// # Error Messages
295///
296/// The macro provides clear error messages for common mistakes:
297/// - Missing required fields at build time
298/// - Invalid attribute combinations
299/// - Generic parameter mismatches
300/// - Syntax errors in default values
301///
302/// # Examples
303///
304/// ```
305/// use type_state_builder::TypeStateBuilder;
306///
307/// #[derive(TypeStateBuilder)]
308/// struct User {
309///     #[builder(required)]
310///     name: String,
311///     age: Option<u32>,
312/// }
313///
314/// let user = User::builder()
315///     .name("Alice".to_string())
316///     .build();
317/// ```
318#[proc_macro_derive(TypeStateBuilder, attributes(builder))]
319pub fn derive_type_state_builder(input: TokenStream) -> TokenStream {
320    let input = parse_macro_input!(input as DeriveInput);
321
322    match generate_builder_implementation(&input) {
323        Ok(tokens) => tokens.into(),
324        Err(error) => error.to_compile_error().into(),
325    }
326}
327
328/// Generates the complete builder implementation for a struct.
329///
330/// This is the main implementation function that coordinates the analysis,
331/// validation, and generation process.
332///
333/// # Arguments
334///
335/// * `input` - The parsed derive input from the struct definition
336///
337/// # Returns
338///
339/// A `syn::Result<proc_macro2::TokenStream>` containing the complete builder
340/// implementation or detailed error information.
341///
342/// # Process
343///
344/// 1. **Analysis** - Parse and analyze the struct definition
345/// 2. **Validation** - Ensure the configuration is valid
346/// 3. **Generation** - Create the appropriate builder code
347/// 4. **Assembly** - Combine all components into the final output
348///
349/// # Error Handling
350///
351/// Errors are returned with detailed information about:
352/// - What went wrong during analysis or generation
353/// - How to fix configuration issues
354/// - Examples of correct usage
355fn generate_builder_implementation(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
356    // Step 1: Analyze the struct definition
357    let analysis = analysis::analyze_struct(input)?;
358
359    // Step 2: Validate the analysis for builder generation
360    analysis.validate_for_generation()?;
361
362    // Step 3: Generate the appropriate builder implementation
363    generation::generate_builder(&analysis)
364}
365
366// Internal types for testing - not exported due to proc-macro restrictions
367
368#[cfg(test)]
369mod tests {
370    use super::*;
371    use syn::parse_quote;
372
373    #[test]
374    fn test_derive_macro_with_required_fields() {
375        let input: DeriveInput = parse_quote! {
376            struct Example {
377                #[builder(required)]
378                name: String,
379                age: Option<u32>,
380            }
381        };
382
383        let result = generate_builder_implementation(&input);
384        assert!(result.is_ok());
385
386        let tokens = result.unwrap();
387        let code = tokens.to_string();
388
389        // Should contain builder method
390        assert!(code.contains("builder"));
391        // Should contain setter for required field
392        assert!(code.contains("name"));
393        // Should contain build method
394        assert!(code.contains("build"));
395    }
396
397    #[test]
398    fn test_derive_macro_with_optional_only() {
399        let input: DeriveInput = parse_quote! {
400            struct Example {
401                name: Option<String>,
402                age: u32,
403            }
404        };
405
406        let result = generate_builder_implementation(&input);
407        assert!(result.is_ok());
408
409        let tokens = result.unwrap();
410        let code = tokens.to_string();
411
412        // Should generate regular builder for optional-only struct
413        assert!(code.contains("builder"));
414        assert!(code.contains("build"));
415    }
416
417    #[test]
418    fn test_derive_macro_with_custom_attributes() {
419        let input: DeriveInput = parse_quote! {
420            #[builder(build_method = "create")]
421            struct Example {
422                #[builder(required, setter_name = "set_name")]
423                name: String,
424
425                #[builder(default = "42")]
426                count: i32,
427            }
428        };
429
430        let result = generate_builder_implementation(&input);
431        assert!(result.is_ok());
432
433        let tokens = result.unwrap();
434        let code = tokens.to_string();
435
436        // Should respect custom build method name
437        assert!(code.contains("create"));
438        // Should respect custom setter name
439        assert!(code.contains("set_name"));
440    }
441
442    #[test]
443    fn test_derive_macro_with_generics() {
444        let input: DeriveInput = parse_quote! {
445            struct Example<T: Clone> {
446                #[builder(required)]
447                value: T,
448                name: String,
449            }
450        };
451
452        let result = generate_builder_implementation(&input);
453        assert!(result.is_ok());
454
455        let tokens = result.unwrap();
456        let code = tokens.to_string();
457
458        // Should handle generics properly
459        assert!(code.contains("<"));
460        assert!(code.contains("T"));
461    }
462
463    #[test]
464    fn test_derive_macro_with_undeclared_generic() {
465        // This test verifies that undeclared generics are allowed through our validation
466        // and the Rust compiler will catch the error later
467        let input: DeriveInput = parse_quote! {
468            struct Example {
469                value: T,  // T is not declared - Rust compiler will catch this
470            }
471        };
472
473        let result = generate_builder_implementation(&input);
474        // Should succeed in our validation (Rust compiler will catch undeclared generic later)
475        assert!(result.is_ok());
476
477        let code = result.unwrap().to_string();
478        // Should generate code that includes the undeclared generic
479        assert!(code.contains("value"));
480    }
481
482    #[test]
483    fn test_unsupported_input_types() {
484        // Test enum - should fail
485        let enum_input: DeriveInput = parse_quote! {
486            enum Example {
487                A, B
488            }
489        };
490        let result = generate_builder_implementation(&enum_input);
491        assert!(result.is_err());
492
493        // Test tuple struct - should fail
494        let tuple_input: DeriveInput = parse_quote! {
495            struct Example(String, i32);
496        };
497        let result = generate_builder_implementation(&tuple_input);
498        assert!(result.is_err());
499
500        // Test unit struct - should fail
501        let unit_input: DeriveInput = parse_quote! {
502            struct Example;
503        };
504        let result = generate_builder_implementation(&unit_input);
505        assert!(result.is_err());
506    }
507}