Crate former

Source
Expand description

§Former - Advanced Builder Pattern Implementation

The Former crate provides a comprehensive derive macro ecosystem for implementing the Builder Pattern in Rust with advanced features like subform support, custom validation, and flexible configuration.

§Core Features

  • Fluent Builder API: Generate clean, ergonomic builder interfaces
  • Advanced Generic Support: Handle complex generic parameters and lifetime constraints
  • Subform Integration: Build nested structures with full type safety
  • Collection Builders: Specialized support for Vec, HashMap, HashSet, and custom collections
  • Custom Validation: Pre-formation validation through custom mutators
  • Flexible Configuration: Extensive attribute system for fine-grained control
  • No-std Compatibility: Full support for no-std environments with optional alloc

§Quick Start

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();

§Architecture Overview

The Former pattern generates several key components:

  • Storage Struct: Holds intermediate state during building (all fields are Option< T >)
  • Former Struct: The main builder providing the fluent API
  • Definition Types: Type system integration for advanced scenarios
  • Trait Implementations: Integration with the broader Former ecosystem

§Rule Compliance & Architectural Notes

This crate has been systematically designed to comply with the Design and Codestyle Rulebooks:

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

  2. Dependencies: Uses macro_tools over syn, quote, proc-macro2 per design rule. Uses error_tools for all error handling instead of anyhow or thiserror.

  3. Feature Architecture: All functionality is gated behind “enabled” feature.

§Using Debug Attribute

use former::Former;

// Standalone debug attribute
#[ derive( Debug, PartialEq, Former ) ]
// #[ debug ]  // <-- Commented out - debug attribute only for temporary debugging
pub struct Person {
    name: String,
    age: u32,
    email: Option< String >,
}

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

§Debug Output Categories

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

  1. Input Analysis: Target type, generic parameters, fields/variants, attribute configuration
  2. Generic Classification: How generics are categorized and processed
  3. Generated Components: Complete breakdown of Former ecosystem components
  4. Final Generated Code: The complete TokenStream output

§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

§Debug Benefits

  • Understand Macro Behavior: See exactly how the macro processes your struct/enum
  • Debug Complex Scenarios: Troubleshoot generic parameters, lifetimes, trait bounds
  • Learn Former Pattern: Understand the complete generated ecosystem
  • Verify Configuration: Confirm attribute parsing and code generation decisions

§Integration Points

This crate serves as the main entry point and integrates:

  • former_meta: Procedural macro implementation
  • former_types: Core traits and type definitions
  • External collections through [collection_tools]

For detailed examples and advanced usage patterns, see the module documentation and the comprehensive examples in the repository.

§Module :: former

experimental rust-status docs.rs Open in Gitpod discord

A flexible implementation of the Builder pattern supporting nested builders and collection-specific subformers. Comprehensive struct support with enum support under active development.

§What is Former?

The former crate provides a powerful derive macro, #[ derive( Former ) ], that automatically implements the Builder pattern for your Rust structs and enums.

Its primary goal is to simplify the construction of complex objects, especially those with numerous fields, optional values, default settings, collections, and nested structures, making your initialization code more readable and maintainable.

Current Status: Struct support is fully functional and production-ready. Enum support is actively developed with 227 total tests passing, including functional unit variants, tuple variants, and multi-field patterns. Some advanced features like #[arg_for_constructor] are still under development.

§Why Use Former?

Compared to manually implementing the Builder pattern or using other builder crates, former offers several advantages:

  • Reduced Boilerplate: #[ derive( Former ) ] automatically generates the builder struct, storage, and setters, saving you significant repetitive coding effort.
  • Fluent & Readable API: Construct objects step-by-step using clear, chainable methods (.field_name( value )).
  • Comprehensive Struct Support: Fully implemented builder pattern for structs with automatic generation of setters, defaults, and subformers
  • Effortless Defaults & Optionals: Fields automatically use their Default implementation if not set. Option< T > fields are handled seamlessly – you only set them if you have a Some( value ). Custom defaults can be specified easily with #[ former( default = ... ) ].
  • Powerful Collection & Nested Struct Handling: former truly shines with its subformer system. Easily build Vec, HashMap, HashSet, and other collections element-by-element, or configure nested structs using their own dedicated formers within the parent’s builder chain. This is often more complex to achieve with other solutions.

§Installation

Add former to your Cargo.toml:

cargo add former

The default features enable the Former derive macro and support for standard collections, covering most common use cases.

§Basic Usage

Derive Former on your struct and use the generated ::former() method to start building:

  use former::Former;

  #[ derive( Debug, PartialEq, Former ) ]
  pub struct UserProfile
  {
    age : i32, // Required field
    username : String, // Required field
    bio : Option< String >, // Optional field
  }

  let profile = UserProfile::former()
  .age( 30 )
  .username( "JohnDoe".to_string() )
  // .bio is optional, so we don't *have* to call its setter
  .form();

  let expected = UserProfile
  {
    age : 30,
    username : "JohnDoe".to_string(),
    bio : None, // Defaults to None if not set
  };
  assert_eq!( profile, expected );
  dbg!( &profile );
  // > &profile = UserProfile {
  // >     age: 30,
  // >     username: "JohnDoe",
  // >     bio: None,
  // > }

  // Example setting the optional field:
  let profile_with_bio = UserProfile::former()
  .age( 30 )
  .username( "JohnDoe".to_string() )
  .bio( "Software Developer".to_string() ) // Set the optional bio
  .form();

  let expected_with_bio = UserProfile
  {
    age : 30,
    username : "JohnDoe".to_string(),
    bio : Some( "Software Developer".to_string() ),
  };
  assert_eq!( profile_with_bio, expected_with_bio );
  dbg!( &profile_with_bio );
  // > &profile_with_bio = UserProfile {
  // >     age: 30,
  // >     username: "JohnDoe",
  // >     bio: Some( "Software Developer" ),
  // > }

Run this example locally | Try it online

§Handling Optionals and Defaults

Former makes working with optional fields and default values straightforward:

  • Option< T > Fields: As seen in the basic example, fields of type Option< T > automatically default to None. You only need to call the setter if you have a Some( value ).

  • Custom Defaults: For required fields that don’t implement Default, or when you need a specific default value other than the type’s default, use the #[ former( default = ... ) ] attribute:

  use former::Former;

  #[ derive( Debug, PartialEq, Former ) ]
  pub struct Config
  {
    #[ former( default = 1024 ) ] // Use 1024 if .buffer_size() is not called
    buffer_size : i32,
    timeout : Option< i32 >, // Defaults to None
    #[ former( default = true ) ] // Default for bool
    enabled : bool,
  }

  // Only set the optional timeout
  let config1 = Config::former()
  .timeout( 5000 )
  .form();

  assert_eq!( config1.buffer_size, 1024 ); // Got default
  assert_eq!( config1.timeout, Some( 5000 ) );
  assert_eq!( config1.enabled, true ); // Got default

  // Set everything, overriding defaults
  let config2 = Config::former()
  .buffer_size( 4096 )
  .timeout( 1000 )
  .enabled( false )
  .form();

  assert_eq!( config2.buffer_size, 4096 );
  assert_eq!( config2.timeout, Some( 1000 ) );
  assert_eq!( config2.enabled, false );

See full example code

§Building Collections & Nested Structs (Subformers)

Where former significantly simplifies complex scenarios is in building collections (Vec, HashMap, etc.) or nested structs. It achieves this through subformers. Instead of setting the entire collection/struct at once, you get a dedicated builder for the field:

Example: Building a Vec

  use former::Former;

  #[ derive( Debug, PartialEq, Former ) ]
  pub struct Report
  {
    title : String,
    #[ subform_collection( definition = former::VectorDefinition ) ] // Enables the `.entries()` subformer
    entries : Vec< String >,
  }

  let report = Report::former()
  .title( "Log Report".to_string() )
  .entries() // Get the subformer for the Vec
    .add( "Entry 1".to_string() ) // Use subformer methods to modify the Vec
    .add( "Entry 2".to_string() )
    .end() // Return control to the parent former (ReportFormer)
  .form(); // Finalize the Report

  assert_eq!( report.title, "Log Report" );
  assert_eq!( report.entries, vec![ "Entry 1".to_string(), "Entry 2".to_string() ] );
  dbg!( &report );
  // > &report = Report {
  // >     title: "Log Report",
  // >     entries: [
  // >         "Entry 1",
  // >         "Entry 2",
  // >     ],
  // > }

See Vec example | See HashMap example

former provides different subform attributes (#[ subform_collection ], #[ subform_entry ], #[ subform_scalar ]) for various collection and nesting patterns.

§Standalone Constructors

For scenarios where you want a direct constructor function instead of always starting with YourType::former(), former offers standalone constructors.

  • Enable: Add #[ standalone_constructors ] to your struct or enum definition.
  • Function Name: A function named after your type (in snake_case) will be generated (e.g., my_struct() for struct MyStruct). For enums, functions are named after variants (e.g., my_variant() for enum E { MyVariant }).
  • Arguments: By default, all fields become constructor arguments.
  • Exclude Arguments: Mark specific fields with #[ former_ignore ] to exclude them from constructor arguments.
  • Return Type Logic:
    • If no fields are marked with #[ former_ignore ], the standalone constructor takes all fields as arguments and returns the instance directly (Self).
    • If any fields are marked with #[ former_ignore ], the standalone constructor takes only non-ignored fields as arguments and returns the Former type.

Example: Struct Standalone Constructors

  use former::Former;

  #[ derive( Debug, PartialEq ) ] // Former not yet implemented for standalone_constructors
  // #[ standalone_constructors ] // Enable standalone constructors
  pub struct ServerConfig
  {
    host : String,     // Will be constructor arg
    port : u16,        // Will be constructor arg  
    #[ former_ignore ] // This field is NOT a constructor arg
    timeout : Option< u32 >,
  }

  // Some fields ignored, so `server_config` returns the Former
  let config_former = server_config( "localhost".to_string(), 8080u16 ); // Added u16 suffix

  // Set the ignored field and form
  let config = config_former
  .timeout( 5000u32 ) // Added u32 suffix
  .form();

  assert_eq!( config.host, "localhost" );
  assert_eq!( config.port, 8080u16 ); // Added u16 suffix
  assert_eq!( config.timeout, Some( 5000u32 ) ); // Added u32 suffix

  #[ derive( Debug, PartialEq, Former ) ]
  #[ standalone_constructors ]
  pub struct Point
  {
    x : i32,  // Will be constructor arg
    y : i32,  // Will be constructor arg
  }

  // NO fields ignored, so `point` returns Self directly
  let p = point( 10, 20 );
  assert_eq!( p.x, 10 );
  assert_eq!( p.y, 20 );

Example: Enum Standalone Constructors

§Vocabulary & Terminology

Understanding the terminology used in former will help you leverage its full potential, especially when working with enums and variants:

§Core Concepts

  • Former: A builder object that accumulates field values and produces the final instance via .form().
  • Storage: Internal structure that holds the building state, containing options for each field.
  • Subformer: A specialized former for building nested structures, collections, or complex field types.
  • FormingEnd: A mechanism that controls what happens when .form() is called on a (sub)former.

§Variant Types (for Enums)

  • Unit Variant: An enum variant with no associated data (e.g., Status::Active).
  • Tuple Variant: An enum variant with unnamed fields in parentheses (e.g., Message::Error(String), Point::Coords(i32, i32)).
  • Struct Variant: An enum variant with named fields in braces (e.g., Request::Get { url: String, headers: Vec<String> }).

§Variant Field Categories

  • Zero-Field Variant: A variant with no fields - can be unit (Status::Active) or empty tuple (Status::Active()).
  • Single-Field Variant: A variant with exactly one field (e.g., Message::Text(String) or User::Profile { name: String }).
  • Multi-Field Variant: A variant with multiple fields (e.g., Point::Coords(i32, i32) or Request::Post { url: String, body: String }).

§Constructor Types

  • Scalar Constructor: A method that takes direct values and immediately returns the enum instance (e.g., Message::text("hello")Message::Text("hello")).
  • Subform Constructor: A method that returns a former/builder for constructing the variant step-by-step, useful for complex variants.
  • Direct Constructor: Simple constructor for variants with no fields (e.g., Status::active()Status::Active).

§Enum Constructor Patterns

  • Method-style Constructor: Instance methods on the enum type (e.g., MyEnum::variant_name(...)).
  • Standalone Constructor: Top-level functions generated when #[standalone_constructors] is used (e.g., variant_name(...)).

§Variant Attributes

  • #[scalar]: Forces generation of a scalar constructor that takes field values directly and returns the enum instance.
  • #[subform_scalar]: For single-field variants where the field type implements Former - generates a method returning the field’s former.
  • #[standalone_constructors]: Applied to the enum itself, generates top-level constructor functions for each variant.
  • #[former_ignore]: Applied to individual fields, excludes them from being parameters in standalone constructors.

§Advanced Concepts

  • Implicit Variant Former: An automatically generated former for variants with multiple fields, providing individual field setters.
  • End-of-forming Logic: Custom behavior when a former completes, enabling advanced patterns like validation or transformation.
  • Context Propagation: Mechanism for passing data through nested formers in complex builder hierarchies.

§Key Features Overview

  • Automatic Builder Generation: #[ derive( Former ) ] for structs (enums under development).
  • Fluent API: Chainable setter methods for a clean construction flow.
  • Production-Ready Struct Support: Complete implementation with all features working:
    • Field setters: Individual setter methods for each field
    • Default handling: Automatic use of Default trait or custom defaults
    • Optional fields: Seamless Option<T> support
    • Subformers: Nested builders for complex field types
  • Defaults & Optionals: Seamless handling of Default values and Option< T > fields. Custom defaults via #[ former( default = ... ) ].
  • Collection & Nested Struct Support: Powerful subformer system for building complex structures:
    • #[ subform_scalar ]: For fields whose type also derives Former
    • #[ subform_collection ]: For collections like Vec, HashMap, HashSet, etc., providing methods like .add() or .insert()
    • #[ subform_entry ]: For collections where each entry is built individually using its own former
  • Enum Support (Active Development): Comprehensive implementation with working functionality:
    • Unit variants: Direct constructors (e.g., MyEnum::variant()) - Fully functional
    • Tuple variants: Scalar constructors and subformers based on field count and attributes - Core patterns working
    • Struct variants: Subformers with individual field setters or scalar constructors - Core patterns working
    • Flexible attributes: #[scalar], #[subform_scalar], #[standalone_constructors] for fine-grained control
    • Known limitations: Single-field tuple variants with primitives require explicit #[scalar] attribute, #[former_ignore] not yet implemented
  • Customization:
    • Rename setters: #[ scalar( name = ... ) ], #[ subform_... ( name = ... ) ]
    • Disable default setters: #[ scalar( setter = false ) ], #[ subform_... ( setter = false ) ]
    • Define custom setters directly in impl Former
    • Specify collection definitions: #[ subform_collection( definition = ... ) ]
  • Advanced Control:
    • Storage-only fields: #[ storage_fields( ... ) ].
    • Custom mutation logic: #[ mutator( custom ) ] + impl FormerMutator.
    • Custom end-of-forming logic: Implement FormingEnd.
    • Custom collection support: Implement Collection traits.

§Troubleshooting

§Common Issues

“Missing Former types” Error

  • Symptom: Errors like BreakFormer not found or RunFormerDefinition not found
  • Cause: Required struct types don’t have #[derive(Former)] enabled
  • Solution: Check for commented-out // #[derive(Debug, Clone, PartialEq, former::Former)] and uncomment them
  • Note: Historical “trailing comma issue” has been resolved - Former derive works correctly now

Raw Identifier Compilation Errors

  • Symptom: Panic with error like "KeywordVariantEnumr#breakFormerStorage" is not a valid identifier
  • Cause: Bug in enum variant handling with raw identifiers (e.g., r#break, r#move)
  • Workaround: Use explicit #[scalar] attribute on variants with keyword identifiers
  • Status: Known issue with utility functions available but not fully integrated

Inner Doc Comment Errors (E0753)

  • Symptom: inner doc comments are not permitted here when compiling tests
  • Cause: Files with //! comments included via include!() macro
  • Solution: Replace //! with regular // comments in included test files

Test Import/Scope Issues

  • Symptom: TestEnum not found or similar import errors in test files
  • Solution: Update import paths to use full crate paths (e.g., use crate::inc::module::TestEnum)
  • Architecture: *_only_test.rs files are included by derive.rs/manual.rs, not standalone modules

Enum Field Method Not Found

  • Symptom: Method like .field_name() not found on enum variant former
  • Cause: Current enum Former implementation uses positional setters, not field delegation
  • Workaround: Use positional setters like ._0(value) instead of .field_name(value)
  • Alternative: Mark complex variants as #[scalar] for direct construction

Standalone Constructor Conflicts

  • Symptom: “Old behavior conflicts” in manual implementations
  • Cause: Manual implementations following outdated patterns
  • Solution: Update standalone constructors to return Self directly when no fields are marked with #[former_ignore]

§Where to Go Next

Modules§

dependency
Namespace with dependencies
derive
Former Meta - Procedural Macro Implementation
exposed
Exposed namespace of the module
orphan
Parented namespace of the module
own
Own namespace of the module
prelude
Prelude to use essentials

Structs§

BTreeMapDefinition
Represents the formation definition for a hash map-like collection within the former framework.
BTreeMapDefinitionTypes
Holds the generic parameters for the BTreeMapDefinition.
BTreeSetDefinition
Represents the formation definition for a binary tree set-like collection within the former framework.
BTreeSetDefinitionTypes
Holds the generic parameters for the BTreeSetDefinition.
BinaryHeapDefinition
Represents the formation definition for a binary heap-like collection within the former framework.
BinaryHeapDefinitionTypes
Holds the generic parameters for the BinaryHeapDefinition.
CollectionFormer
A builder structure for constructing collections with a fluent and flexible interface.
FormingEndClosure
A wrapper around a closure to be used as a FormingEnd.
HashMapDefinition
Represents the formation definition for a hash map-like collection within the former framework.
HashMapDefinitionTypes
Holds the generic parameters for the HashMapDefinition.
HashSetDefinition
Represents the formation definition for a hash set-like collection within the former framework.
HashSetDefinitionTypes
Holds the generic parameters for the HashSetDefinition.
LinkedListDefinition
Represents the formation definition for a list-like collection within the former framework.
LinkedListDefinitionTypes
Holds the generic parameters for the LinkedListDefinition.
NoEnd
A placeholder FormingEnd used when no end operation is required or applicable.
ReturnPreformed
A FormingEnd implementation that directly returns the formed collection as the final product of the forming process.
ReturnStorage
A FormingEnd implementation that returns the storage itself as the formed entity, disregarding any contextual data.
VecDequeDefinition
Represents the formation definition for a vector deque-like collection within the former framework.
VecDequeDefinitionTypes
Holds the generic parameters for the VecDequeDefinition.
VectorDefinition
Represents the formation definition for a vector-like collection within the former framework.
VectorDefinitionTypes
Holds the generic parameters for the VectorDefinition.

Traits§

BTreeMapExt
Provides an extension method for hash maps to facilitate the use of the builder pattern.
BTreeSetExt
Provides an extension method for binary tree sets to facilitate the use of the builder pattern.
BinaryHeapExt
Provides an extension method for binary heaps to facilitate the use of the builder pattern.
Collection
Represents a collection by defining the types of entries and values it handles.
CollectionAdd
Provides functionality to add individual entries to a collection.
CollectionAssign
Defines the capability to replace all entries in a collection with a new set of entries.
CollectionValToEntry
Provides a mechanism for transforming a value back into a collection-specific entry format.
EntityToDefinition
Maps a type of entity to its corresponding former definition.
EntityToDefinitionTypes
Provides a mapping between a type of entity and its associated formation type definitions.
EntityToFormer
Maps a type of entity to its corresponding former (builder) implementation.
EntityToStorage
Maps a type of entity to its storage type. This trait defines what storage structure is used to hold the interim state of an entity during its formation.
EntryToVal
Facilitates the conversion of collection entries to their corresponding value representations.
FormerBegin
A trait for initiating a structured subforming process with contextual and intermediary storage linkage.
FormerDefinition
Expands on FormerDefinitionTypes by incorporating an ending mechanism for the formation process. This trait connects the formation types with a specific endpoint, defining how the formation process concludes, including any necessary transformations or validations.
FormerDefinitionTypes
Defines the fundamental components involved in the formation of an entity. This trait specifies the types of storage, the formed entity, and the context used during the formation process.
FormerMutator
Provides a mechanism for mutating the context and storage just before the forming process is completed.
FormingEnd
Defines a handler for the end of a subforming process, enabling the return of the original context.
HashMapExt
Provides an extension method for hash maps to facilitate the use of the builder pattern.
HashSetExt
Provides an extension method for HashSet to facilitate the use of the builder pattern.
LinkedListExt
Provides an extension method for lists to facilitate the use of the builder pattern.
Storage
Defines the storage interface for entities being constructed using a forming pattern.
StoragePreform
Provides a mechanism to finalize the forming process by converting storage into its final formed state.
ValToEntry
Facilitates the conversion of values back into entries for specific collection types.
VecDequeExt
Provides an extension method for vector deques to facilitate the use of the builder pattern.
VecExt
Provides an extension method for vectors to facilitate the use of the builder pattern.

Type Aliases§

BTreeMapFormer
Provides a streamlined builder interface for constructing hash map-like collections.
BTreeSetFormer
Provides a streamlined builder interface for constructing binary tree set-like collections.
BinaryHeapFormer
Provides a streamlined builder interface for constructing binary heap-like collections.
HashMapFormer
Provides a streamlined builder interface for constructing hash map-like collections.
HashSetFormer
Provides a concise alias for CollectionFormer configured specifically for HashSet-like collections.
LinkedListFormer
Provides a streamlined builder interface for constructing list-like collections.
VecDequeFormer
Provides a streamlined builder interface for constructing vector deque-like collections.
VectorFormer
Provides a streamlined builder interface for constructing vector-like collections.

Derive Macros§

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