type_state_builder/
lib.rs

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