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