type-state-builder 0.5.1

Type-state builder pattern derive macro with compile-time safety and enhanced ergonomics.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
//! Struct Attribute Parsing
//!
//! This module handles parsing of struct-level builder attributes, providing
//! configuration options that apply to the entire builder generation process.
//!
//! # Supported Struct Attributes
//!
//! - `build_method = "name"` - Specifies a custom name for the build method
//! - `setter_prefix = "prefix_"` - Specifies a prefix for all setter method names
//! - `impl_into` - Use `impl Into<FieldType>` for setter parameters instead of `FieldType`
//! - `const` - Generate const-compatible builder methods for compile-time construction
//!

/// Configuration derived from struct-level builder attributes.
///
/// This struct represents all the builder-specific configuration that can
/// be applied to the entire struct through attributes on the struct definition.
///
/// # Field Descriptions
///
/// * `build_method_name` - Custom name for the final build method (None = "build")
/// * `setter_prefix` - Common prefix for all setter method names (None = no prefix)
/// * `impl_into` - Whether setters should accept `impl Into<FieldType>` (false = use `FieldType`)
/// * `const` - Whether to generate const-compatible builder methods
///
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructAttributes {
    /// Custom name for the build method.
    ///
    /// If None, the build method will be named "build". If Some, the provided
    /// name will be used for the final method that constructs the struct instance.
    ///
    /// # Method Name Requirements
    ///
    /// The build method name must:
    /// - Be a valid Rust identifier
    /// - Not conflict with generated setter method names
    /// - Not be a Rust keyword (unless raw identifier syntax is used)
    ///
    pub build_method_name: Option<String>,

    /// Common prefix for all setter method names.
    ///
    /// If None, setter methods use their natural names (field name or custom setter_name).
    /// If Some, the provided prefix is prepended to all setter method names.
    ///
    /// # Prefix Requirements
    ///
    /// The setter prefix must:
    /// - Be a valid start of a Rust identifier
    /// - Not create conflicts when combined with field names
    /// - Follow Rust naming conventions (typically lowercase with underscore)
    ///
    /// # Examples
    pub setter_prefix: Option<String>,

    /// Whether setter methods should use `impl Into<FieldType>` parameters.
    ///
    /// If false, setter methods use the field type directly: `fn field(value: FieldType)`
    /// If true, setter methods accept any convertible type: `fn field(value: impl Into<FieldType>)`
    ///
    /// This provides ergonomic conversion for common cases like:
    /// - `String` fields accepting `&str`, `String`, `Cow<str>`
    /// - `PathBuf` fields accepting `&str`, `&Path`, `String`
    /// - `Vec<T>` fields accepting arrays, slices, iterators
    ///
    /// # Field-Level Override
    ///
    /// Individual fields can override this struct-level setting using
    /// `#[builder(impl_into = true/false)]` on the field itself.
    ///
    /// See the crate-level documentation for comprehensive usage examples.
    pub impl_into: bool,

    /// Whether to generate const-compatible builder methods.
    ///
    /// When enabled, all builder methods (`builder()`, setters, `build()`) are
    /// generated as `const fn`, enabling compile-time construction.
    ///
    /// # Constraints
    ///
    /// - Cannot be combined with `impl_into` (trait bounds not supported in const fn)
    /// - All optional fields must have explicit `#[builder(default = expr)]`
    /// - Closure converters are transformed into generated const fns
    pub const_builder: bool,
}

impl Default for StructAttributes {
    /// Creates default struct attributes.
    ///
    /// Default configuration:
    /// - `build_method_name: None` - Use "build" as the method name
    /// - `setter_prefix: None` - No prefix for setter methods
    /// - `impl_into: false` - Use direct field types in setters
    /// - `const_builder: false` - Generate regular (non-const) methods
    fn default() -> Self {
        Self {
            build_method_name: None,
            setter_prefix: None,
            impl_into: false,
            const_builder: false,
        }
    }
}

impl StructAttributes {
    /// Gets the build method name, returning "build" if no custom name is set.
    ///
    /// This method provides a convenient way to get the build method name
    /// with the default fallback behavior built in.
    ///
    /// # Returns
    ///
    /// The build method name as a `&str`. Returns "build" if no custom name
    /// was specified, otherwise returns the custom name.
    ///
    pub fn get_build_method_name(&self) -> &str {
        self.build_method_name.as_deref().unwrap_or("build")
    }

    /// Gets the setter prefix, returning None if no custom prefix is set.
    ///
    /// This method provides access to the struct-level setter prefix that
    /// should be applied to all setter method names unless overridden by
    /// field-level settings.
    ///
    /// # Returns
    ///
    /// An `Option<&str>` containing the setter prefix if one was specified,
    /// or None if setter methods should use their natural names.
    ///
    pub fn get_setter_prefix(&self) -> Option<&str> {
        self.setter_prefix.as_deref()
    }

    /// Gets the impl_into setting for the struct.
    ///
    /// This method provides access to the struct-level impl_into setting that
    /// controls whether setter methods should accept `impl Into<FieldType>`
    /// parameters instead of `FieldType` directly.
    ///
    /// # Returns
    ///
    /// A `bool` indicating whether setters should use `impl Into<T>` parameters.
    /// - `true` - Setters accept `impl Into<FieldType>`
    /// - `false` - Setters accept `FieldType` directly
    ///
    pub fn get_impl_into(&self) -> bool {
        self.impl_into
    }

    /// Gets the const_builder setting for the struct.
    ///
    /// # Returns
    ///
    /// A `bool` indicating whether to generate const-compatible builder methods.
    /// - `true` - All builder methods are `const fn`
    /// - `false` - Regular (non-const) methods
    pub fn get_const_builder(&self) -> bool {
        self.const_builder
    }

    /// Validates that the struct attributes are consistent and valid.
    ///
    /// This method checks that all struct-level attributes have valid values
    /// and don't conflict with each other or with Rust language requirements.
    ///
    /// # Returns
    ///
    /// A `syn::Result<()>` indicating success or containing validation errors.
    ///
    /// # Validation Rules
    ///
    /// The method validates:
    /// - Build method names are not empty
    /// - Build method names are valid identifiers (when possible)
    /// - Setter prefixes are not empty
    /// - Setter prefixes are valid identifier beginnings
    /// - No conflicting attribute combinations
    ///
    /// # Errors
    ///
    /// Returns errors for:
    /// - Empty build method names
    /// - Empty setter prefixes
    /// - Invalid identifier syntax (when detectable)
    /// - Reserved keywords without raw identifier syntax
    pub fn validate(&self) -> syn::Result<()> {
        // Validate build method name if provided
        if let Some(build_method_name) = &self.build_method_name {
            if build_method_name.is_empty() {
                return Err(syn::Error::new(
                    proc_macro2::Span::call_site(),
                    "Build method name cannot be empty",
                ));
            }

            // Try to validate as identifier (this catches most invalid cases)
            // Note: This doesn't catch all cases (like reserved keywords) but
            // those will be caught during token generation
            if syn::parse_str::<syn::Ident>(build_method_name).is_err() {
                // If it's not a valid identifier, check if it might be a raw identifier
                if !build_method_name.starts_with("r#") {
                    return Err(syn::Error::new(
                        proc_macro2::Span::call_site(),
                        format!(
                            "Invalid build method name '{build_method_name}'. Build method names must be valid Rust identifiers. \
                            Use raw identifier syntax (r#name) for keywords."
                        ),
                    ));
                }
            }
        }

        // Validate setter prefix if provided
        if let Some(setter_prefix) = &self.setter_prefix {
            if setter_prefix.is_empty() {
                return Err(syn::Error::new(
                    proc_macro2::Span::call_site(),
                    "Setter prefix cannot be empty",
                ));
            }

            // Check if the prefix would create valid identifiers when combined with field names
            // We'll validate this by checking if it starts correctly
            if setter_prefix.chars().next().is_some_and(|c| c.is_numeric()) {
                return Err(syn::Error::new(
                    proc_macro2::Span::call_site(),
                    format!(
                        "Invalid setter prefix '{setter_prefix}'. Setter prefixes cannot start with a number. \
                        Use a valid identifier prefix like 'with_' or 'set_'."
                    ),
                ));
            }

            // Check for basic identifier validity of prefix
            // This is a heuristic check - we'll do full validation when combining with field names
            if !setter_prefix
                .chars()
                .all(|c| c.is_alphanumeric() || c == '_')
            {
                return Err(syn::Error::new(
                    proc_macro2::Span::call_site(),
                    format!(
                        "Invalid setter prefix '{setter_prefix}'. Setter prefixes must contain only alphanumeric characters and underscores."
                    ),
                ));
            }
        }

        // Validate const and impl_into are not used together
        if self.const_builder && self.impl_into {
            return Err(syn::Error::new(
                proc_macro2::Span::call_site(),
                "`const` and `impl_into` cannot be used together. \
                 `impl Into<T>` requires trait bounds which are not supported in const fn.",
            ));
        }

        Ok(())
    }
}

/// Parses builder attributes from a struct's attribute list.
///
/// This function processes all `#[builder(...)]` attributes on a struct and
/// combines them into a single `StructAttributes` configuration. It validates
/// that the attributes are valid and returns appropriate errors for invalid
/// configurations.
///
/// # Arguments
///
/// * `attrs` - The list of attributes from a struct definition
///
/// # Returns
///
/// A `Result<StructAttributes, syn::Error>` containing the parsed configuration
/// or a descriptive error for invalid attributes.
///
/// # Supported Attribute Syntax
///
/// Supported struct-level attributes include:
/// - `build_method = "name"` - Custom build method name
/// - `setter_prefix = "prefix_"` - Prefix for all setter method names
/// - `impl_into` - Use `impl Into<FieldType>` for setter parameters
/// - Combined attributes in a single attribute block
///
/// # Errors
///
/// Returns errors for:
/// - Invalid attribute syntax
/// - Unknown struct-level attribute names
/// - Invalid build method names
/// - Empty attribute values
/// - Conflicting attribute combinations
///
/// # Implementation Details
///
/// The function:
/// 1. Iterates through all attributes looking for `#[builder(...)]`
/// 2. Parses each builder attribute using `syn::parse_nested_meta`
/// 3. Validates individual attribute values
/// 4. Combines multiple attributes into a single configuration
/// 5. Validates the final configuration for consistency
/// 6. Returns the complete configuration or the first error encountered
pub fn parse_struct_attributes(attrs: &[syn::Attribute]) -> syn::Result<StructAttributes> {
    let mut struct_attributes = StructAttributes::default();

    // Process each attribute in the list
    for attr in attrs {
        // Only process #[builder(...)] attributes
        if attr.path().is_ident("builder") {
            // Parse the nested meta inside the builder attribute
            attr.parse_nested_meta(|meta| {
                if meta.path.is_ident("build_method") {
                    // #[builder(build_method = "name")]
                    let value = meta.value()?;
                    let lit_str: syn::LitStr = value.parse()?;
                    let build_method_name = lit_str.value();

                    // Validate that the build method name is not empty
                    if build_method_name.is_empty() {
                        return Err(meta.error("Build method name cannot be empty"));
                    }

                    struct_attributes.build_method_name = Some(build_method_name);
                    Ok(())
                } else if meta.path.is_ident("setter_prefix") {
                    // #[builder(setter_prefix = "prefix_")]
                    let value = meta.value()?;
                    let lit_str: syn::LitStr = value.parse()?;
                    let setter_prefix = lit_str.value();

                    // Validate that the setter prefix is not empty
                    if setter_prefix.is_empty() {
                        return Err(meta.error("Setter prefix cannot be empty"));
                    }

                    struct_attributes.setter_prefix = Some(setter_prefix);
                    Ok(())
                } else if meta.path.is_ident("impl_into") {
                    // #[builder(impl_into)]
                    struct_attributes.impl_into = true;
                    Ok(())
                } else if meta.path.is_ident("const") {
                    // #[builder(const)]
                    struct_attributes.const_builder = true;
                    Ok(())
                } else {
                    // Unknown struct-level attribute
                    Err(meta.error(
                        "Unknown struct-level builder attribute. Supported attributes: build_method, setter_prefix, impl_into, const"
                    ))
                }
            })?;
        }
    }

    // Validate the final configuration
    struct_attributes.validate()?;

    Ok(struct_attributes)
}

#[cfg(test)]
mod tests {
    use super::*;
    use syn::parse_quote;

    #[test]
    fn test_default_struct_attributes() {
        let attrs = StructAttributes::default();
        assert!(attrs.build_method_name.is_none());
        assert!(attrs.setter_prefix.is_none());
        assert!(!attrs.impl_into);
        assert!(!attrs.const_builder);
        assert_eq!(attrs.get_build_method_name(), "build");
        assert_eq!(attrs.get_setter_prefix(), None);
        assert!(!attrs.get_impl_into());
        assert!(!attrs.get_const_builder());
    }

    #[test]
    fn test_get_build_method_name() {
        // Default case
        let default_attrs = StructAttributes::default();
        assert_eq!(default_attrs.get_build_method_name(), "build");

        // Custom case
        let custom_attrs = StructAttributes {
            build_method_name: Some("create".to_string()),
            setter_prefix: None,
            impl_into: false,
            const_builder: false,
        };
        assert_eq!(custom_attrs.get_build_method_name(), "create");
    }

    #[test]
    fn test_with_build_method_name() {
        let attrs = StructAttributes {
            build_method_name: Some("create".to_string()),
            setter_prefix: None,
            impl_into: false,
            const_builder: false,
        };
        assert_eq!(attrs.get_build_method_name(), "create");

        let attrs2 = StructAttributes {
            build_method_name: Some("construct".to_string()),
            setter_prefix: None,
            impl_into: false,
            const_builder: false,
        };
        assert_eq!(attrs2.get_build_method_name(), "construct");
    }

    #[test]
    fn test_validate_valid_attributes() {
        let valid_attrs = StructAttributes {
            build_method_name: Some("create".to_string()),
            setter_prefix: Some("with_".to_string()),
            impl_into: false,
            const_builder: false,
        };
        assert!(valid_attrs.validate().is_ok());

        let default_attrs = StructAttributes::default();
        assert!(default_attrs.validate().is_ok());
    }

    #[test]
    fn test_validate_empty_build_method_name() {
        let invalid_attrs = StructAttributes {
            build_method_name: Some("".to_string()),
            setter_prefix: None,
            impl_into: false,
            const_builder: false,
        };
        let result = invalid_attrs.validate();
        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("Build method name cannot be empty"));
    }

    #[test]
    fn test_parse_build_method_attribute() {
        let attrs = vec![parse_quote!(#[builder(build_method = "create")])];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        assert_eq!(struct_attrs.build_method_name, Some("create".to_string()));
        assert_eq!(struct_attrs.get_build_method_name(), "create");
    }

    #[test]
    fn test_parse_raw_identifier_build_method() {
        let attrs = vec![parse_quote!(#[builder(build_method = "r#type")])];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        assert_eq!(struct_attrs.build_method_name, Some("r#type".to_string()));
        assert_eq!(struct_attrs.get_build_method_name(), "r#type");
    }

    #[test]
    fn test_parse_no_builder_attributes() {
        let attrs = vec![parse_quote!(#[derive(Debug)])];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        // Should return default attributes
        assert!(struct_attrs.build_method_name.is_none());
        assert_eq!(struct_attrs.get_build_method_name(), "build");
    }

    #[test]
    fn test_parse_multiple_builder_attributes() {
        // Multiple #[builder(...)] attributes should be combined
        let attrs = vec![
            parse_quote!(#[builder(build_method = "create")]),
            // Could add more struct-level attributes here in the future
        ];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        assert_eq!(struct_attrs.get_build_method_name(), "create");
    }

    #[test]
    fn test_parse_empty_build_method_error() {
        let attrs = vec![parse_quote!(#[builder(build_method = "")])];
        let result = parse_struct_attributes(&attrs);

        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("cannot be empty"));
    }

    #[test]
    fn test_parse_unknown_attribute_error() {
        let attrs = vec![parse_quote!(#[builder(unknown_attr = "value")])];
        let result = parse_struct_attributes(&attrs);

        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("Unknown struct-level builder attribute"));
    }

    #[test]
    fn test_parse_invalid_build_method_name() {
        // This test depends on syn's identifier validation
        // Some invalid names might not be caught until token generation
        let attrs = vec![parse_quote!(#[builder(build_method = "123invalid")])];
        let result = parse_struct_attributes(&attrs);

        // Should either succeed (caught later) or fail with identifier error
        if let Err(e) = result {
            assert!(
                e.to_string().contains("Invalid build method name")
                    || e.to_string().contains("identifier")
            );
        }
    }

    // Tests for setter_prefix functionality

    #[test]
    fn test_get_setter_prefix() {
        // Default case (no prefix)
        let default_attrs = StructAttributes::default();
        assert_eq!(default_attrs.get_setter_prefix(), None);

        // Custom prefix case
        let custom_attrs = StructAttributes {
            build_method_name: None,
            setter_prefix: Some("with_".to_string()),
            impl_into: false,
            const_builder: false,
        };
        assert_eq!(custom_attrs.get_setter_prefix(), Some("with_"));
    }

    #[test]
    fn test_parse_setter_prefix_attribute() {
        let attrs = vec![parse_quote!(#[builder(setter_prefix = "with_")])];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        assert_eq!(struct_attrs.setter_prefix, Some("with_".to_string()));
        assert_eq!(struct_attrs.get_setter_prefix(), Some("with_"));
        assert_eq!(struct_attrs.get_build_method_name(), "build"); // default
    }

    #[test]
    fn test_parse_combined_attributes() {
        let attrs =
            vec![parse_quote!(#[builder(build_method = "create", setter_prefix = "with_")])];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        assert_eq!(struct_attrs.build_method_name, Some("create".to_string()));
        assert_eq!(struct_attrs.setter_prefix, Some("with_".to_string()));
        assert_eq!(struct_attrs.get_build_method_name(), "create");
        assert_eq!(struct_attrs.get_setter_prefix(), Some("with_"));
    }

    #[test]
    fn test_parse_empty_setter_prefix_error() {
        let attrs = vec![parse_quote!(#[builder(setter_prefix = "")])];
        let result = parse_struct_attributes(&attrs);

        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("Setter prefix cannot be empty"));
    }

    #[test]
    fn test_validate_empty_setter_prefix() {
        let invalid_attrs = StructAttributes {
            build_method_name: None,
            setter_prefix: Some("".to_string()),
            impl_into: false,
            const_builder: false,
        };
        let result = invalid_attrs.validate();
        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("Setter prefix cannot be empty"));
    }

    #[test]
    fn test_validate_invalid_setter_prefix_starting_with_number() {
        let invalid_attrs = StructAttributes {
            build_method_name: None,
            setter_prefix: Some("1invalid_".to_string()),
            impl_into: false,
            const_builder: false,
        };
        let result = invalid_attrs.validate();
        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("cannot start with a number"));
    }

    #[test]
    fn test_validate_invalid_setter_prefix_special_chars() {
        let invalid_attrs = StructAttributes {
            build_method_name: None,
            setter_prefix: Some("with-".to_string()),
            impl_into: false,
            const_builder: false,
        };
        let result = invalid_attrs.validate();
        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("alphanumeric characters and underscores"));
    }

    #[test]
    fn test_validate_valid_setter_prefixes() {
        let valid_prefixes = [
            "with_", "set_", "use_", "add_", "remove_", "update_", "get_",
        ];

        for prefix in valid_prefixes {
            let attrs = StructAttributes {
                build_method_name: None,
                setter_prefix: Some(prefix.to_string()),
                impl_into: false,
                const_builder: false,
            };
            assert!(
                attrs.validate().is_ok(),
                "Prefix '{prefix}' should be valid"
            );
        }
    }

    #[test]
    fn test_parse_multiple_setter_prefix_attributes() {
        // Multiple #[builder(...)] attributes with different settings
        let attrs = vec![
            parse_quote!(#[builder(setter_prefix = "with_")]),
            parse_quote!(#[builder(build_method = "create")]),
        ];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        assert_eq!(struct_attrs.get_setter_prefix(), Some("with_"));
        assert_eq!(struct_attrs.get_build_method_name(), "create");
    }

    // Tests for impl_into functionality

    #[test]
    fn test_get_impl_into() {
        // Default case (false)
        let default_attrs = StructAttributes::default();
        assert!(!default_attrs.get_impl_into());

        // Explicit true case
        let impl_into_attrs = StructAttributes {
            build_method_name: None,
            setter_prefix: None,
            impl_into: true,
            const_builder: false,
        };
        assert!(impl_into_attrs.get_impl_into());

        // Explicit false case
        let no_impl_into_attrs = StructAttributes {
            build_method_name: None,
            setter_prefix: None,
            impl_into: false,
            const_builder: false,
        };
        assert!(!no_impl_into_attrs.get_impl_into());
    }

    #[test]
    fn test_parse_impl_into_attribute() {
        let attrs = vec![parse_quote!(#[builder(impl_into)])];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        assert!(struct_attrs.impl_into);
        assert!(struct_attrs.get_impl_into());
        assert_eq!(struct_attrs.get_build_method_name(), "build"); // default
        assert_eq!(struct_attrs.get_setter_prefix(), None); // default
    }

    #[test]
    fn test_parse_combined_attributes_with_impl_into() {
        let attrs = vec![parse_quote!(#[builder(
            impl_into,
            build_method = "create",
            setter_prefix = "with_"
        )])];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        assert!(struct_attrs.impl_into);
        assert_eq!(struct_attrs.build_method_name, Some("create".to_string()));
        assert_eq!(struct_attrs.setter_prefix, Some("with_".to_string()));
        assert!(struct_attrs.get_impl_into());
        assert_eq!(struct_attrs.get_build_method_name(), "create");
        assert_eq!(struct_attrs.get_setter_prefix(), Some("with_"));
    }

    #[test]
    fn test_parse_multiple_impl_into_attributes() {
        // Multiple #[builder(...)] attributes with different settings
        let attrs = vec![
            parse_quote!(#[builder(impl_into)]),
            parse_quote!(#[builder(build_method = "create")]),
        ];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        assert!(struct_attrs.get_impl_into());
        assert_eq!(struct_attrs.get_build_method_name(), "create");
    }

    #[test]
    fn test_validate_impl_into_attributes() {
        // Valid: impl_into with other attributes (but not const)
        let valid_attrs = StructAttributes {
            build_method_name: Some("create".to_string()),
            setter_prefix: Some("with_".to_string()),
            impl_into: true,
            const_builder: false,
        };
        assert!(valid_attrs.validate().is_ok());

        // Valid: impl_into alone
        let impl_into_only = StructAttributes {
            build_method_name: None,
            setter_prefix: None,
            impl_into: true,
            const_builder: false,
        };
        assert!(impl_into_only.validate().is_ok());

        // Valid: no impl_into (default case)
        let no_impl_into = StructAttributes {
            build_method_name: None,
            setter_prefix: None,
            impl_into: false,
            const_builder: false,
        };
        assert!(no_impl_into.validate().is_ok());
    }

    // Tests for const_builder functionality

    #[test]
    fn test_get_const_builder() {
        // Default case (false)
        let default_attrs = StructAttributes::default();
        assert!(!default_attrs.get_const_builder());

        // Explicit true case
        let const_attrs = StructAttributes {
            build_method_name: None,
            setter_prefix: None,
            impl_into: false,
            const_builder: true,
        };
        assert!(const_attrs.get_const_builder());
    }

    #[test]
    fn test_parse_const_attribute() {
        let attrs = vec![parse_quote!(#[builder(const)])];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        assert!(struct_attrs.const_builder);
        assert!(struct_attrs.get_const_builder());
        assert_eq!(struct_attrs.get_build_method_name(), "build"); // default
        assert!(!struct_attrs.get_impl_into()); // default
    }

    #[test]
    fn test_parse_const_with_other_attributes() {
        let attrs = vec![parse_quote!(#[builder(const, build_method = "create")])];
        let struct_attrs = parse_struct_attributes(&attrs).unwrap();

        assert!(struct_attrs.const_builder);
        assert_eq!(struct_attrs.build_method_name, Some("create".to_string()));
        assert!(struct_attrs.get_const_builder());
        assert_eq!(struct_attrs.get_build_method_name(), "create");
    }

    #[test]
    fn test_validate_const_with_impl_into_error() {
        let invalid_attrs = StructAttributes {
            build_method_name: None,
            setter_prefix: None,
            impl_into: true,
            const_builder: true,
        };
        let result = invalid_attrs.validate();
        assert!(result.is_err());
        let err_msg = result.unwrap_err().to_string();
        assert!(err_msg.contains("`const` and `impl_into` cannot be used together"));
    }

    #[test]
    fn test_parse_const_with_impl_into_error() {
        let attrs = vec![parse_quote!(#[builder(const, impl_into)])];
        let result = parse_struct_attributes(&attrs);

        assert!(result.is_err());
        let err_msg = result.unwrap_err().to_string();
        assert!(err_msg.contains("`const` and `impl_into` cannot be used together"));
    }

    #[test]
    fn test_validate_const_alone_is_valid() {
        let const_only = StructAttributes {
            build_method_name: None,
            setter_prefix: None,
            impl_into: false,
            const_builder: true,
        };
        assert!(const_only.validate().is_ok());
    }

    #[test]
    fn test_validate_const_with_setter_prefix_is_valid() {
        let attrs = StructAttributes {
            build_method_name: Some("create".to_string()),
            setter_prefix: Some("with_".to_string()),
            impl_into: false,
            const_builder: true,
        };
        assert!(attrs.validate().is_ok());
    }
}