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