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}