Former

Derive Macro Former 

Source
#[derive(Former)]
{
    // Attributes available to this derive:
    #[debug]
    #[perform]
    #[storage_fields]
    #[mutator]
    #[former]
    #[scalar]
    #[subform_scalar]
    #[subform_collection]
    #[subform_entry]
    #[standalone_constructors]
    #[former_ignore]
    #[arg_for_constructor]
}
Expand description

Derive macro for generating a Former struct, applying a Builder Pattern to the annotated struct.

This macro simplifies the construction of complex objects by automatically generating a builder (former) for the specified struct. It supports extensive customization through attributes that control defaults, setter generation, and field customization, allowing for flexible and fluent object construction.

§Core Capabilities and Limitations

§✅ Supported Scenarios

  • Complex Lifetime Parameters : Handles < 'a, T > patterns, multiple lifetimes, and where clauses
  • Generic Constraints : Works with where T: Hash + Eq, complex trait bounds
  • Nested Structures : Subform support for complex hierarchical data
  • Collection Types : HashMap, Vec, HashSet with proper trait bound handling
  • Optional Fields : Automatic Option< T > handling with sensible defaults
  • Custom Mutators : Pre-formation data manipulation and validation

§⚠️ Common Pitfalls and Solutions

§1. Commented-Out Derive Attributes (90% of issues)

// ❌ WRONG: Derive commented out - will appear as "complex" issue
// #[ derive( Debug, PartialEq, Former ) ]
#[ derive( Debug, PartialEq ) ]
pub struct MyStruct { ... }

// ✅ CORRECT: Uncomment derive attribute
#[ derive( Debug, PartialEq, Former ) ]
pub struct MyStruct { ... }

§2. Feature Gate Requirements for Collections

// ✅ REQUIRED: Collection tests need proper feature gates
#[ cfg(any(not(feature = "no_std"), feature = "use_alloc")) ]
mod test_with_collections;

§3. Hash+Eq Trait Bounds for HashMap Keys

// ❌ WRONG: Using non-Hash type as HashMap key
pub struct Definition; // No Hash+Eq implementation
pub struct MyStruct {
  map: HashMap< Definition, String >, // Will fail
}

// ✅ CORRECT: Implement required traits or use different key type
#[ derive( Hash, Eq, PartialEq ) ]
pub struct Definition; // Now implements Hash+Eq

§4. Lifetime Parameter Complexity

// ✅ WORKS: Complex lifetime scenarios are supported
#[ derive( Former ) ]
pub struct Child< 'child, T >
where
  T: 'child + ?Sized,
{
  name: String,
  data: &'child T,
}

§📋 Diagnostic Workflow

When encountering issues :

  1. Check for commented derives (resolves 90% of issues)
  2. Verify feature gate configuration (for collection tests)
  3. Assess trait bound requirements (Hash+Eq for HashMap keys)
  4. Test incremental complexity (start simple, add complexity gradually)
  5. Enable debug output (use #[ debug ] to see generated code)
  6. Check lifetime parameters (ensure proper lifetime annotations)

§Common Error Patterns and Solutions

§E0277: Trait bound not satisfied
error[E0277] : the trait bound `MyType: Hash` is not satisfied

Solution : Implement required traits for HashMap keys :

#[ derive( Hash, Eq, PartialEq ) ]
struct MyType { /* fields */ }
§E0106: Missing lifetime specifier
error[E0106] : missing lifetime specifier

Solution : Add proper lifetime parameters :

#[ derive( Former ) ]
struct MyStruct< 'a > {
    reference: &'a str,
}
§Commented Derive Issues
// ❌ WRONG: This will appear as a "complex" compilation error
// #[ derive( Debug, PartialEq, Former ) ]
#[ derive( Debug, PartialEq ) ]
struct MyStruct { field: String }

// ✅ CORRECT: Uncomment the derive attribute
#[ derive( Debug, PartialEq, Former ) ]
struct MyStruct { field: String }
§Collection Feature Gate Issues
// ✅ REQUIRED: Add feature gates for collection tests
#[ cfg(any(not(feature = "no_std"), feature = "use_alloc")) ]
mod collection_tests {
    // HashMap/Vec tests here
}

§Struct Attributes

  • debug : Enables debug mode which can be used to print or log the internal state of the builder for debugging purposes.
  • perform : Specifies a custom method to be invoked automatically at the end of the build process.
  • storage_fields : Specifies fields that should be treated as part of the storage for the former.
  • mutator : Defines a custom mutator class or function to manipulate the data just before the object is finalized.
  • standalone_constructors : Generates top-level constructor functions (e.g., my_struct(), my_variant()). Return type depends on former_ignore (see Option 2 logic in Readme/advanced.md).

§Field Attributes

  • former : General attribute to specify various options like defaults or inclusion in the former.
  • scalar : Indicates that the field is a scalar value, enabling direct assignment without the need for a sub-former. Affects the associated method constructor for enum variants.
  • collection : Marks the field as a collection that can use specific former methods to manage its contents.
  • subform : Specifies that the field should utilize a nested former, facilitating the construction of complex nested structures.
  • former_ignore : Excludes a field from being an argument for the standalone constructor. Affects constructor signature and return type (see Option 2 logic in Readme/advanced.md).

§Usage Examples

§Basic Structure Building

use former ::Former;

#[ derive( Debug, PartialEq, Former ) ]
pub struct UserProfile {
    age: i32,
    username: String,
    bio_optional: Option< String >,
}

let profile = UserProfile ::former()
    .age(30)
    .username("JohnDoe".to_string())
    .bio_optional("Software Developer".to_string())
    .form();

§Collection Handling

use former ::Former;
use std ::collections ::HashMap;

#[ derive( Debug, Former ) ]
pub struct Config {
    #[ collection ]
    settings: HashMap< String, String >,
    #[ collection ]
    tags: Vec< String >,
}

let config = Config ::former()
    .settings().insert("debug", "true").end()
    .tags().push("production").push("web").end()
    .form();

§Complex Generic Scenarios

use former ::Former;

#[ derive( Debug, Former ) ]
pub struct Container< 'a, T >
where
    T: Clone + 'a,
{
    data: &'a T,
    metadata: Option< String >,
}

let value = "hello".to_string();
let container = Container ::former()
    .data(&value)
    .metadata("example".to_string())
    .form();

§Custom Validation with Mutators

use former ::Former;

#[ derive( Debug, Former ) ]
#[ mutator( custom ) ]
pub struct ValidatedStruct {
    min_value: i32,
    max_value: i32,
}

// Custom mutator implementation
impl FormerMutator  for ValidatedStructDefinitionTypes
{
    fn form_mutation(storage: &mut Self ::Storage, _context: &mut Option< Self ::Context >) {
 if let (Some(min), Some(max)) = (&storage.min_value, &storage.max_value) {
 if min > max {
                std ::mem ::swap(&mut storage.min_value, &mut storage.max_value);
}
}
}
}

§Debugging Generated Code

The Former derive macro provides comprehensive debugging capabilities through the #[ debug ] attribute, following the design principle that “Proc Macros: Must Implement a ‘debug’ Attribute”.

§Debug Attribute Usage

use former ::Former;

// Standalone debug attribute
#[ derive( Debug, PartialEq, Former ) ]
#[ debug ]  // <-- Enables comprehensive debug output
pub struct Person {
    name: String,
    age: u32,
    email: Option< String >,
}

// Within #[ former( ... ) ] container
#[ derive( Debug, PartialEq, Former ) ]
#[ former( debug, standalone_constructors ) ]  // <-- Debug with other attributes
pub struct Config {
    host: String,
    port: u16,
}

§Comprehensive Debug Information

When #[ debug ] is present and the former_diagnostics_print_generated feature is enabled, the macro provides detailed information in four phases :

§Phase 1 : Input Analysis
  • Target Type Information : Name, kind (struct/enum), visibility
  • Generic Parameters Analysis : Lifetimes, type parameters, const parameters, where clauses
  • Field/Variant Analysis : Field names, types, visibility for structs; variant information for enums
  • Attribute Configuration : All parsed Former attributes, storage fields, mutator settings
§Phase 2 : Generic Classification
  • Classification Results : How generics are categorized (lifetime-only, type-only, mixed, empty)
  • Generated Generic Components : impl_generics, ty_generics, where_clause breakdown
  • Strategy Explanation : Why certain generation strategies were chosen
§Phase 3 : Generated Components Analysis
  • Core Components : FormerStorage, FormerDefinition, FormerDefinitionTypes, Former struct
  • Trait Implementations : EntityToStorage, EntityToFormer, EntityToDefinition, etc.
  • Formation Process : Step-by-step formation workflow explanation
  • Customizations : How attributes affect the generated code structure
§Phase 4 : Complete Generated Code
  • Final TokenStream : The complete code that will be compiled
  • Integration Points : How generated code integrates with existing types

§Enabling Debug Output

# See debug information during compilation
cargo build --features former_diagnostics_print_generated

# For examples
cargo run --example former_debug --features former_diagnostics_print_generated

# For tests with debug output
cargo test --features former_diagnostics_print_generated

§Debug Use Cases

The debug attribute is particularly useful for :

  1. Understanding Macro Behavior : See exactly how the macro processes your struct/enum definition
  2. Debugging Complex Scenarios : Troubleshoot generic parameters, lifetime issues, trait bound problems
  3. Learning Former Pattern : Understand the complete ecosystem generated for your types
  4. Verifying Configuration : Confirm that attributes are parsed correctly and generate expected code
  5. Performance Analysis : Understand the complexity of generated code for optimization

§Integration with Development Workflow

The debug system integrates seamlessly with existing development tools :

  • Zero Runtime Cost : Debug analysis only runs during compilation
  • Conditional Compilation : Debug code only included with feature flag
  • IDE Integration : Debug output appears in compiler output and can be captured by IDEs
  • CI/CD Friendly : Can be enabled in build pipelines for automated analysis