type_state_builder/lib.rs
1//! Type-State Builder Pattern Implementation
2//!
3//! A derive macro that generates compile-time safe builders using the type-state pattern.
4//! It prevents incomplete object construction by making missing required fields a
5//! compile-time error rather than a runtime failure.
6//!
7//! For complete documentation, installation instructions, and guides, see the
8//! [README](https://github.com/welf/type-state-builder#readme).
9//!
10//! # Design Philosophy
11//!
12//! This crate was designed with AI-assisted development in mind. Two principles
13//! guided its design:
14//!
15//! ## Compiler-Enforced Correctness
16//!
17//! In AI-assisted development, code generation happens rapidly. By encoding field
18//! requirements in the type system, errors are caught immediately by the compiler
19//! rather than manifesting as runtime failures. If the code compiles, the builder
20//! is correctly configured.
21//!
22//! ## Actionable Error Messages
23//!
24//! Instead of using generic type parameters to track field states (which produce
25//! cryptic error messages), this crate generates named structs for each state:
26//!
27//! ```text
28//! UserBuilder_HasName_MissingEmail // Name set, email missing
29//! UserBuilder_HasName_HasEmail // Both set - build() available
30//! ```
31//!
32//! When an error occurs, the type name immediately indicates which fields are missing.
33//! This is particularly valuable for AI assistants that can parse and act on the error.
34//!
35//! ## Trade-offs
36//!
37//! This approach generates more structs than type-parameter-based solutions, slightly
38//! increasing compile time. However, the improved error message clarity is worth this
39//! cost. There is no runtime cost due to Rust's zero-cost abstractions.
40//!
41//! # Compatibility
42//!
43//! - **no_std**: Fully compatible. Generated code uses only `core` types.
44//! - **MSRV**: Rust 1.70.0 or later.
45//!
46//! # Overview
47//!
48//! The macro automatically selects between two builder patterns:
49//!
50//! - **Type-State Builder**: For structs with required fields, providing compile-time
51//! safety that prevents calling `build()` until all required fields are set
52//! - **Regular Builder**: For structs with only optional fields, providing a simple
53//! builder with immediate `build()` availability
54//!
55//! # Quick Start
56//!
57//! Add the derive macro to your struct and mark required fields:
58//!
59//! ```
60//! use type_state_builder::TypeStateBuilder;
61//!
62//! #[derive(TypeStateBuilder)]
63//! #[builder(impl_into)] // Setters arguments are now `impl Into<FieldType>`
64//! struct User {
65//! #[builder(required)]
66//! name: String,
67//!
68//! #[builder(required)]
69//! email: String,
70//!
71//! #[builder(default = Some(30))] // Default value for age
72//! age: Option<u32>,
73//!
74//! #[builder(setter_prefix = "is_")] // The setter is now named `is_active`
75//! active: bool, // Will use Default::default() if not set
76//! }
77//!
78//! // Usage - this enforces that name and email are set
79//! let user = User::builder()
80//! .name("Alice") // `impl_into` allows to pass any type that can be converted into String
81//! .email("alice@example.com") // `impl_into` allows to pass any type that can be converted into String
82//! // age is not set and defaults to Some(30)
83//! // active is not set and defaults to false (Default::default())
84//! .build(); // The `build()` method is available since all required fields are set
85
86//!
87//! assert_eq!(user.name, "Alice".to_string());
88//! assert_eq!(user.email, "alice@example.com".to_string());
89//! assert_eq!(user.age, Some(30));
90//! assert_eq!(user.active, false);
91//! ```
92//!
93//! # Supported Attributes
94//!
95//! ## Struct-level Attributes
96//!
97//! - `#[builder(build_method = "method_name")]` - Custom build method name
98//! - `#[builder(setter_prefix = "prefix_")]` - Prefix for all setter method names
99//! - `#[builder(impl_into)]` - Generate setters with `impl Into<FieldType>` parameters
100//! - `#[builder(const)]` - Generate `const fn` builder methods for compile-time construction
101//!
102//! ## Field-level Attributes
103//!
104//! - `#[builder(required)]` - Mark field as required
105//! - `#[builder(setter_name = "name")]` - Custom setter method name
106//! - `#[builder(setter_prefix = "prefix_")]` - Custom prefix for setter method name
107//! - `#[builder(default = expression)]` - Custom default value
108//! - `#[builder(skip_setter)]` - Don't generate setter (requires default)
109//! - `#[builder(impl_into)]` - Generate setter with `impl Into<FieldType>` parameter
110//! - `#[builder(impl_into = false)]` - Override struct-level `impl_into` for this field
111//! - `#[builder(converter = |param: InputType| -> FieldType { expression })` - Custom conversion logic for setter input
112//!
113//! # Advanced Examples
114//!
115//! ## Custom Defaults and Setter Names
116//!
117//! ```
118//! use type_state_builder::TypeStateBuilder;
119//!
120//! #[derive(TypeStateBuilder)]
121//! #[builder(build_method = "create")]
122//! struct Document {
123//! #[builder(required)]
124//! title: String,
125//!
126//! #[builder(required, setter_name = "set_content")]
127//! content: String,
128//!
129//! #[builder(default = 42)]
130//! page_count: u32,
131//!
132//! #[builder(default = String::from("draft"), skip_setter)]
133//! status: String,
134//! }
135//!
136//! let doc = Document::builder()
137//! .title("My Document".to_string())
138//! .set_content("Document content here".to_string())
139//! .create(); // Custom build method name
140//! ```
141//!
142//! ## Generic Types and Lifetimes
143//!
144//! ```
145//! use type_state_builder::TypeStateBuilder;
146//!
147//! #[derive(TypeStateBuilder)]
148//! struct Container<T: Clone>
149//! where
150//! T: Send
151//! {
152//! #[builder(required)]
153//! value: T,
154//!
155//! #[builder(required)]
156//! name: String,
157//!
158//! tags: Vec<String>,
159//! }
160//!
161//! let container = Container::builder()
162//! .value(42)
163//! .name("test".to_string())
164//! .tags(vec!["tag1".to_string()])
165//! .build();
166//! ```
167//!
168//! ## Setter Prefix Examples
169//!
170//! ```
171//! use type_state_builder::TypeStateBuilder;
172//!
173//! // Struct-level setter prefix applies to all fields
174//! #[derive(TypeStateBuilder)]
175//! #[builder(setter_prefix = "with_")]
176//! struct Config {
177//! #[builder(required)]
178//! host: String,
179//!
180//! #[builder(required)]
181//! port: u16,
182//!
183//! timeout: Option<u32>,
184//! }
185//!
186//! let config = Config::builder()
187//! .with_host("localhost".to_string())
188//! .with_port(8080)
189//! .with_timeout(Some(30))
190//! .build();
191//! ```
192//!
193//! ```
194//! use type_state_builder::TypeStateBuilder;
195//!
196//! // Field-level setter prefix overrides struct-level prefix
197//! #[derive(TypeStateBuilder)]
198//! #[builder(setter_prefix = "with_")]
199//! struct Database {
200//! #[builder(required)]
201//! connection_string: String,
202//!
203//! #[builder(required, setter_prefix = "set_")]
204//! credentials: String,
205//!
206//! #[builder(setter_name = "timeout_seconds")]
207//! timeout: Option<u32>,
208//! }
209//!
210//! let db = Database::builder()
211//! .with_connection_string("postgresql://...".to_string())
212//! .set_credentials("user:pass".to_string())
213//! .with_timeout_seconds(Some(60))
214//! .build();
215//! ```
216//!
217//! ## Ergonomic Conversions with `impl_into`
218//!
219//! The `impl_into` attribute generates setter methods that accept `impl Into<FieldType>`
220//! parameters, allowing for more ergonomic API usage by automatically converting
221//! compatible types.
222//!
223//! ```
224//! use type_state_builder::TypeStateBuilder;
225//!
226//! // Struct-level impl_into applies to all setters
227//! #[derive(TypeStateBuilder)]
228//! #[builder(impl_into)]
229//! struct ApiClient {
230//! #[builder(required)]
231//! base_url: String,
232//!
233//! #[builder(required)]
234//! api_key: String,
235//!
236//! timeout: Option<u32>,
237//! user_agent: String, // Uses Default::default()
238//! }
239//!
240//! // Can now use &str directly instead of String::from() or .to_string()
241//! let client = ApiClient::builder()
242//! .base_url("https://api.example.com") // &str -> String
243//! .api_key("secret-key") // &str -> String
244//! .timeout(Some(30))
245//! .user_agent("MyApp/1.0") // &str -> String
246//! .build();
247//! ```
248//!
249//! ```
250//! use type_state_builder::TypeStateBuilder;
251//!
252//! // Field-level control with precedence rules
253//! #[derive(TypeStateBuilder)]
254//! #[builder(impl_into)] // Default for all fields
255//! struct Document {
256//! #[builder(required)]
257//! title: String, // Inherits impl_into = true
258//!
259//! #[builder(required, impl_into = false)]
260//! content: String, // Override: requires String directly
261//!
262//! #[builder(impl_into = true)]
263//! category: Option<String>, // Explicit impl_into = true
264//!
265//! #[builder(impl_into = false)]
266//! tags: Vec<String>, // Override: requires Vec<String> directly
267//! }
268//!
269//! let doc = Document::builder()
270//! .title("My Document") // &str -> String (inherited)
271//! .content("Content".to_string()) // Must use String (override)
272//! .category(Some("tech".to_string())) // impl Into for Option<String>
273//! .tags(vec!["rust".to_string()]) // Must use Vec<String> (override)
274//! .build();
275//! ```
276//!
277//! **Note**: `impl_into` is incompatible with `skip_setter` since skipped fields
278//! don't have setter methods generated.
279//!
280//! ### Complete `impl_into` Example
281//!
282//! ```
283//! use type_state_builder::TypeStateBuilder;
284//! use std::path::PathBuf;
285//!
286//! // Demonstrate both struct-level and field-level impl_into usage
287//! #[derive(TypeStateBuilder)]
288//! #[builder(impl_into)] // Struct-level default: enable for all fields
289//! struct CompleteExample {
290//! #[builder(required)]
291//! name: String, // Inherits: accepts impl Into<String>
292//!
293//! #[builder(required, impl_into = false)]
294//! id: String, // Override: requires String directly
295//!
296//! #[builder(impl_into = true)]
297//! description: Option<String>, // Explicit: accepts impl Into<String>
298//!
299//! #[builder(default = PathBuf::from("/tmp"))]
300//! work_dir: PathBuf, // Inherits: accepts impl Into<PathBuf>
301//!
302//! #[builder(impl_into = false, default = Vec::new())]
303//! tags: Vec<String>, // Override: requires Vec<String>
304//! }
305//!
306//! // Usage demonstrating the different setter behaviors
307//! let example = CompleteExample::builder()
308//! .name("Alice") // &str -> String (inherited impl_into)
309//! .id("user_123".to_string()) // Must use String (override = false)
310//! .description(Some("Engineer".to_string())) // Option<String> required
311//! .work_dir("/home/alice") // &str -> PathBuf (inherited impl_into)
312//! .tags(vec!["rust".to_string()]) // Must use Vec<String> (override = false)
313//! .build();
314//!
315//! assert_eq!(example.name, "Alice");
316//! assert_eq!(example.id, "user_123");
317//! assert_eq!(example.description, Some("Engineer".to_string()));
318//! assert_eq!(example.work_dir, PathBuf::from("/home/alice"));
319//! assert_eq!(example.tags, vec!["rust"]);
320//! ```
321//!
322//! ## Custom Conversions with `converter`
323//!
324//! The `converter` attribute allows you to specify custom conversion logic for field setters,
325//! enabling more powerful transformations than `impl_into` can provide. Use closure syntax
326//! to define the conversion function with explicit parameter types.
327//!
328//! ```
329//! use type_state_builder::TypeStateBuilder;
330//!
331//! #[derive(TypeStateBuilder)]
332//! struct User {
333//! #[builder(required)]
334//! name: String,
335//!
336//! // Normalize email to lowercase and trim whitespace
337//! #[builder(required, converter = |email: &str| email.trim().to_lowercase())]
338//! email: String,
339//!
340//! // Parse comma-separated tags into Vec<String>
341//! #[builder(converter = |tags: &str| tags.split(',').map(|s| s.trim().to_string()).collect())]
342//! interests: Vec<String>,
343//!
344//! // Parse age from string with fallback
345//! #[builder(converter = |age: &str| age.parse().unwrap_or(0))]
346//! age: u32,
347//! }
348//!
349//! let user = User::builder()
350//! .name("Alice".to_string())
351//! .email(" ALICE@EXAMPLE.COM ") // Will be normalized to "alice@example.com"
352//! .interests("rust, programming, web") // Parsed to Vec<String>
353//! .age("25") // Parsed from string to u32
354//! .build();
355//!
356//! assert_eq!(user.email, "alice@example.com");
357//! assert_eq!(user.interests, vec!["rust", "programming", "web"]);
358//! assert_eq!(user.age, 25);
359//! ```
360//!
361//! ### Complex Converter Examples
362//!
363//! ```
364//! use type_state_builder::TypeStateBuilder;
365//! use std::collections::HashMap;
366//!
367//! #[derive(TypeStateBuilder)]
368//! struct Config {
369//! // Convert environment-style boolean strings
370//! #[builder(converter = |enabled: &str| {
371//! matches!(enabled.to_lowercase().as_str(), "true" | "1" | "yes" | "on")
372//! })]
373//! debug_enabled: bool,
374//!
375//! // Parse key=value pairs into HashMap
376//! #[builder(converter = |pairs: &str| {
377//! pairs.split(',')
378//! .filter_map(|pair| {
379//! let mut split = pair.split('=');
380//! Some((split.next()?.trim().to_string(),
381//! split.next()?.trim().to_string()))
382//! })
383//! .collect()
384//! })]
385//! env_vars: HashMap<String, String>,
386//!
387//! // Transform slice to owned Vec
388//! #[builder(converter = |hosts: &[&str]| {
389//! hosts.iter().map(|s| s.to_string()).collect()
390//! })]
391//! allowed_hosts: Vec<String>,
392//! }
393//!
394//! let config = Config::builder()
395//! .debug_enabled("true")
396//! .env_vars("LOG_LEVEL=debug,PORT=8080")
397//! .allowed_hosts(&["localhost", "127.0.0.1"])
398//! .build();
399//!
400//! assert_eq!(config.debug_enabled, true);
401//! assert_eq!(config.env_vars.get("LOG_LEVEL"), Some(&"debug".to_string()));
402//! assert_eq!(config.allowed_hosts, vec!["localhost", "127.0.0.1"]);
403//! ```
404//!
405//! ### Converter with Generics
406//!
407//! ```
408//! use type_state_builder::TypeStateBuilder;
409//!
410//! #[derive(TypeStateBuilder)]
411//! struct Container<T: Clone + Default> {
412//! #[builder(required, impl_into)]
413//! name: String,
414//!
415//! // Converter works with generic fields too
416//! #[builder(converter = |items: &[T]| items.into_iter().map(|item| item.clone()).collect())]
417//! data: Vec<T>,
418//! }
419//!
420//! // Usage with concrete type
421//! let container = Container::builder()
422//! .name("numbers") // Convert &str to String thanks to `impl_into`
423//! .data(&[1, 2, 3, 4, 5]) // Convert &[T] to Vec<T> thanks to `converter`
424//! .build(); // Available only when all required fields are set
425//!
426//! assert_eq!(container.data, vec![1, 2, 3, 4, 5]);
427//! ```
428//!
429//! ### Converter vs impl_into Comparison
430//!
431//! | Feature | `impl_into` | `converter` |
432//! |---------|-------------|-------------|
433//! | **Type conversions** | Only `Into` trait | Any custom logic |
434//! | **Parsing strings** | Limited | Full support |
435//! | **Data validation** | No | Custom validation |
436//! | **Complex transformations** | No | Full support |
437//! | **Multiple input formats** | Into only | Any input type |
438//! | **Performance** | Zero-cost | Depends on logic |
439//! | **Syntax** | Attribute flag | Closure expression |
440//!
441//! **When to use `converter`:**
442//! - Ergonomic setter generation for `Option<String>` fields
443//! - Parsing structured data from strings
444//! - Normalizing or validating input data
445//! - Complex data transformations
446//! - Converting between incompatible types
447//! - Custom business logic in setters
448//!
449//! **Note**: `converter` is incompatible with `skip_setter` and `impl_into` since
450//! they represent different approaches to setter generation.
451//!
452//! ## Optional-Only Structs (Regular Builder)
453//!
454//! ```
455//! use type_state_builder::TypeStateBuilder;
456//!
457//! // No required fields = regular builder pattern
458//! #[derive(TypeStateBuilder)]
459//! struct Settings {
460//! debug: bool,
461//! max_connections: Option<u32>,
462//!
463//! #[builder(default = "default.log".to_string())]
464//! log_file: String,
465//!
466//! #[builder(skip_setter, default = 42)]
467//! magic_number: i32,
468//! }
469//!
470//! // Can call build() immediately since no required fields
471//! let settings = Settings::builder()
472//! .debug(true)
473//! .max_connections(Some(100))
474//! .build();
475//! ```
476//!
477//! ## Const Builders
478//!
479//! The `#[builder(const)]` attribute generates `const fn` builder methods, enabling
480//! compile-time constant construction. This is useful for embedded systems, static
481//! configuration, and other scenarios where values must be known at compile time.
482//!
483//! ```
484//! use type_state_builder::TypeStateBuilder;
485//!
486//! #[derive(TypeStateBuilder, Debug, PartialEq)]
487//! #[builder(const)]
488//! struct Config {
489//! #[builder(required)]
490//! name: &'static str,
491//!
492//! #[builder(required)]
493//! version: u32,
494//!
495//! #[builder(default = 8080)]
496//! port: u16,
497//! }
498//!
499//! // Compile-time constant construction
500//! const APP_CONFIG: Config = Config::builder()
501//! .name("my-app")
502//! .version(1)
503//! .port(3000)
504//! .build();
505//!
506//! // Also works in static context
507//! static DEFAULT_CONFIG: Config = Config::builder()
508//! .name("default")
509//! .version(0)
510//! .build();
511//!
512//! // And in const fn
513//! const fn make_config(name: &'static str) -> Config {
514//! Config::builder()
515//! .name(name)
516//! .version(1)
517//! .build()
518//! }
519//!
520//! const CUSTOM: Config = make_config("custom");
521//! ```
522//!
523//! ### Const Builder Requirements
524//!
525//! When using `#[builder(const)]`, there are some restrictions:
526//!
527//! - **Explicit defaults required**: Optional fields must use `#[builder(default = expr)]`
528//! because `Default::default()` cannot be called in const context
529//! - **No `impl_into`**: The `impl_into` attribute is incompatible with const builders
530//! because trait bounds are not supported in const fn
531//! - **Const-compatible types**: Field types must support const construction (e.g.,
532//! `&'static str` instead of `String`, arrays instead of `Vec`)
533//!
534//! ### Const Builders with Converters
535//!
536//! Closure converters work with const builders. The macro automatically generates
537//! a `const fn` from the closure body:
538//!
539//! ```
540//! use type_state_builder::TypeStateBuilder;
541//!
542//! #[derive(TypeStateBuilder, Debug, PartialEq)]
543//! #[builder(const)]
544//! struct Data {
545//! // Converter closure is transformed into a const fn
546//! #[builder(required, converter = |s: &'static str| s.len())]
547//! name_length: usize,
548//!
549//! #[builder(default = 0, converter = |n: i32| n * 2)]
550//! doubled: i32,
551//! }
552//!
553//! const DATA: Data = Data::builder()
554//! .name_length("hello") // Converted to 5
555//! .doubled(21) // Converted to 42
556//! .build();
557//!
558//! assert_eq!(DATA.name_length, 5);
559//! assert_eq!(DATA.doubled, 42);
560//! ```
561//!
562//! **Note**: The closure body must be const-evaluable. If it contains non-const
563//! operations (like heap allocation or trait method calls), the Rust compiler
564//! will produce an error.
565//!
566//! # Error Prevention
567//!
568//! The macro prevents common mistakes at compile time:
569//!
570//! ```compile_fail
571//! use type_state_builder::TypeStateBuilder;
572//!
573//! #[derive(TypeStateBuilder)]
574//! struct User {
575//! #[builder(required)]
576//! name: String,
577//! }
578//!
579//! let user = User::builder().build(); // ERROR: required field not set
580//! ```
581//!
582//! ```compile_fail
583//! use type_state_builder::TypeStateBuilder;
584//!
585//! #[derive(TypeStateBuilder)]
586//! struct BadConfig {
587//! #[builder(required, default = test)] // ERROR: Invalid combination
588//! name: String,
589//! }
590//! ```
591//!
592//! # Invalid Attribute Combinations
593//!
594//! Several attribute combinations are invalid and will produce compile-time errors
595//! with helpful error messages:
596//!
597//! ## skip_setter + setter_name
598//!
599//! You can't name a setter method that doesn't exist:
600//!
601//! ```compile_fail
602//! use type_state_builder::TypeStateBuilder;
603//!
604//! #[derive(TypeStateBuilder)]
605//! struct InvalidSetterName {
606//! #[builder(skip_setter, setter_name = "custom_name")] // ERROR: Conflicting attributes
607//! field: String,
608//! }
609//! ```
610//!
611//! ## skip_setter + setter_prefix
612//!
613//! You can't apply prefixes to non-existent setter methods:
614//!
615//! ```compile_fail
616//! use type_state_builder::TypeStateBuilder;
617//!
618//! #[derive(TypeStateBuilder)]
619//! struct InvalidSetterPrefix {
620//! #[builder(skip_setter, setter_prefix = "with_")] // ERROR: Conflicting attributes
621//! field: String,
622//! }
623//! ```
624//!
625//! ## skip_setter + impl_into
626//!
627//! Skipped setters can't have parameter conversion rules:
628//!
629//! ```compile_fail
630//! use type_state_builder::TypeStateBuilder;
631//!
632//! #[derive(TypeStateBuilder)]
633//! struct InvalidImplInto {
634//! #[builder(skip_setter, impl_into = true)] // ERROR: Conflicting attributes
635//! field: String,
636//! }
637//! ```
638//!
639//! ## skip_setter + converter
640//!
641//! Skipped setters can't have custom conversion logic:
642//!
643//! ```compile_fail
644//! use type_state_builder::TypeStateBuilder;
645//!
646//! #[derive(TypeStateBuilder)]
647//! struct InvalidConverter {
648//! #[builder(skip_setter, converter = |x: String| x)] // ERROR: Conflicting attributes
649//! field: String,
650//! }
651//! ```
652//!
653//! ## converter + impl_into
654//!
655//! Custom converters and `impl_into` are different approaches to parameter handling:
656//!
657//! ```compile_fail
658//! use type_state_builder::TypeStateBuilder;
659//!
660//! #[derive(TypeStateBuilder)]
661//! struct InvalidConverterImplInto {
662//! #[builder(converter = |x: String| x, impl_into = true)] // ERROR: Conflicting attributes
663//! field: String,
664//! }
665//! ```
666//!
667//! ## skip_setter + required
668//!
669//! Required fields must have setter methods:
670//!
671//! ```compile_fail
672//! use type_state_builder::TypeStateBuilder;
673//!
674//! #[derive(TypeStateBuilder)]
675//! struct InvalidRequired {
676//! #[builder(skip_setter, required)] // ERROR: Conflicting attributes
677//! field: String,
678//! }
679//! ```
680//!
681//! # Module Organization
682//!
683//! The crate is organized into several modules that handle different aspects
684//! of the builder generation process. Most users will only interact with the
685//! [`TypeStateBuilder`] derive macro.
686//!
687//! For complete documentation, examples, and guides, see the
688//! [README](https://github.com/welf/type-state-builder#readme).
689
690use proc_macro::TokenStream;
691use syn::{parse_macro_input, DeriveInput};
692
693// Internal modules - not exported due to proc-macro restrictions
694mod analysis;
695mod attributes;
696mod generation;
697mod utils;
698mod validation;
699
700/// Derives a type-safe builder for a struct with compile-time validation of required fields.
701///
702/// This macro automatically generates an appropriate builder pattern based on the struct
703/// configuration:
704/// - **Type-State Builder** for structs with required fields
705/// - **Regular Builder** for structs with only optional fields
706///
707/// # Basic Usage
708///
709/// ```
710/// use type_state_builder::TypeStateBuilder;
711///
712/// #[derive(TypeStateBuilder)]
713/// struct Person {
714/// #[builder(required)]
715/// name: String,
716/// age: Option<u32>,
717/// }
718///
719/// let person = Person::builder()
720/// .name("Alice".to_string())
721/// .build();
722/// ```
723///
724/// # Attribute Reference
725///
726/// ## Struct Attributes
727///
728/// - `#[builder(build_method = "name")]` - Custom build method name (default: "build")
729/// - `#[builder(setter_prefix = "prefix_")]` - Prefix for all setter method names
730///
731/// ## Field Attributes
732///
733/// - `#[builder(required)]` - Field must be set before build() (creates type-state builder)
734/// - `#[builder(setter_name = "name")]` - Custom setter method name
735/// - `#[builder(setter_prefix = "prefix_")]` - Custom prefix for this field's setter (overrides struct-level)
736/// - `#[builder(default = expr)]` - Custom default value (must be valid Rust expression)
737/// - `#[builder(skip_setter)]` - Don't generate setter method (requires default value)
738///
739/// # Generated Methods
740///
741/// The macro generates:
742/// - `YourStruct::builder()` - Creates a new builder instance
743/// - `.field_name(value)` - Setter methods for each field (unless skipped)
744/// - `.build()` - Constructs the final instance (or custom name from `build_method`)
745///
746/// # Compile-Time Safety
747///
748/// The type-state builder (used when there are required fields) prevents:
749/// - Calling `build()` before setting required fields
750/// - Setting the same required field multiple times
751/// - Invalid attribute combinations
752///
753/// # Error Messages
754///
755/// The macro provides clear error messages for common mistakes:
756/// - Missing required fields at build time
757/// - Invalid attribute combinations
758/// - Generic parameter mismatches
759/// - Syntax errors in default values
760///
761/// # Examples
762///
763/// ```
764/// use type_state_builder::TypeStateBuilder;
765///
766/// #[derive(TypeStateBuilder)]
767/// struct User {
768/// #[builder(required)]
769/// name: String,
770/// age: Option<u32>,
771/// }
772///
773/// let user = User::builder()
774/// .name("Alice".to_string())
775/// .build();
776/// ```
777#[proc_macro_derive(TypeStateBuilder, attributes(builder))]
778pub fn derive_type_state_builder(input: TokenStream) -> TokenStream {
779 let input = parse_macro_input!(input as DeriveInput);
780
781 match generate_builder_implementation(&input) {
782 Ok(tokens) => tokens.into(),
783 Err(error) => error.to_compile_error().into(),
784 }
785}
786
787/// Generates the complete builder implementation for a struct.
788///
789/// This is the main implementation function that coordinates the analysis,
790/// validation, and generation process.
791///
792/// # Arguments
793///
794/// * `input` - The parsed derive input from the struct definition
795///
796/// # Returns
797///
798/// A `syn::Result<proc_macro2::TokenStream>` containing the complete builder
799/// implementation or detailed error information.
800///
801/// # Process
802///
803/// 1. **Analysis** - Parse and analyze the struct definition
804/// 2. **Validation** - Ensure the configuration is valid
805/// 3. **Generation** - Create the appropriate builder code
806/// 4. **Assembly** - Combine all components into the final output
807///
808/// # Error Handling
809///
810/// Errors are returned with detailed information about:
811/// - What went wrong during analysis or generation
812/// - How to fix configuration issues
813/// - Examples of correct usage
814fn generate_builder_implementation(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
815 // Step 1: Analyze the struct definition
816 let analysis = analysis::analyze_struct(input)?;
817
818 // Step 2: Validate the analysis for builder generation
819 analysis.validate_for_generation()?;
820
821 // Step 3: Generate the appropriate builder implementation
822 generation::generate_builder(&analysis)
823}
824
825// Internal types for testing - not exported due to proc-macro restrictions
826
827#[cfg(test)]
828mod tests {
829 use super::*;
830 use syn::parse_quote;
831
832 #[test]
833 fn test_derive_macro_with_required_fields() {
834 let input: DeriveInput = parse_quote! {
835 struct Example {
836 #[builder(required)]
837 name: String,
838 age: Option<u32>,
839 }
840 };
841
842 let result = generate_builder_implementation(&input);
843 assert!(result.is_ok());
844
845 let tokens = result.unwrap();
846 let code = tokens.to_string();
847
848 // Should contain builder method
849 assert!(code.contains("builder"));
850 // Should contain setter for required field
851 assert!(code.contains("name"));
852 // Should contain build method
853 assert!(code.contains("build"));
854 }
855
856 #[test]
857 fn test_derive_macro_with_optional_only() {
858 let input: DeriveInput = parse_quote! {
859 struct Example {
860 name: Option<String>,
861 age: u32,
862 }
863 };
864
865 let result = generate_builder_implementation(&input);
866 assert!(result.is_ok());
867
868 let tokens = result.unwrap();
869 let code = tokens.to_string();
870
871 // Should generate regular builder for optional-only struct
872 assert!(code.contains("builder"));
873 assert!(code.contains("build"));
874 }
875
876 #[test]
877 fn test_derive_macro_with_custom_attributes() {
878 let input: DeriveInput = parse_quote! {
879 #[builder(build_method = "create")]
880 struct Example {
881 #[builder(required, setter_name = "set_name")]
882 name: String,
883
884 #[builder(default = 42)]
885 count: i32,
886 }
887 };
888
889 let result = generate_builder_implementation(&input);
890 assert!(result.is_ok());
891
892 let tokens = result.unwrap();
893 let code = tokens.to_string();
894
895 // Should respect custom build method name
896 assert!(code.contains("create"));
897 // Should respect custom setter name
898 assert!(code.contains("set_name"));
899 }
900
901 #[test]
902 fn test_derive_macro_with_generics() {
903 let input: DeriveInput = parse_quote! {
904 struct Example<T: Clone> {
905 #[builder(required)]
906 value: T,
907 name: String,
908 }
909 };
910
911 let result = generate_builder_implementation(&input);
912 assert!(result.is_ok());
913
914 let tokens = result.unwrap();
915 let code = tokens.to_string();
916
917 // Should handle generics properly
918 assert!(code.contains("<"));
919 assert!(code.contains("T"));
920 }
921
922 #[test]
923 fn test_derive_macro_with_undeclared_generic() {
924 // This test verifies that undeclared generics are allowed through our validation
925 // and the Rust compiler will catch the error later
926 let input: DeriveInput = parse_quote! {
927 struct Example {
928 value: T, // T is not declared - Rust compiler will catch this
929 }
930 };
931
932 let result = generate_builder_implementation(&input);
933 // Should succeed in our validation (Rust compiler will catch undeclared generic later)
934 assert!(result.is_ok());
935
936 let code = result.unwrap().to_string();
937 // Should generate code that includes the undeclared generic
938 assert!(code.contains("value"));
939 }
940
941 #[test]
942 fn test_unsupported_input_types() {
943 // Test enum - should fail
944 let enum_input: DeriveInput = parse_quote! {
945 enum Example {
946 A, B
947 }
948 };
949 let result = generate_builder_implementation(&enum_input);
950 assert!(result.is_err());
951
952 // Test tuple struct - should fail
953 let tuple_input: DeriveInput = parse_quote! {
954 struct Example(String, i32);
955 };
956 let result = generate_builder_implementation(&tuple_input);
957 assert!(result.is_err());
958
959 // Test unit struct - should fail
960 let unit_input: DeriveInput = parse_quote! {
961 struct Example;
962 };
963 let result = generate_builder_implementation(&unit_input);
964 assert!(result.is_err());
965 }
966}