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}