Crates.io | former |
lib.rs | former |
version | 2.28.0 |
created_at | 2021-11-23 16:04:07.369546+00 |
updated_at | 2025-08-12 15:17:21.960989+00 |
description | A flexible implementation of the Builder pattern supporting nested builders and collection-specific subformers. Simplify the construction of complex objects. |
homepage | https://github.com/Wandalen/wTools/tree/master/module/core/former |
repository | https://github.com/Wandalen/wTools/tree/master/module/core/former |
max_upload_size | |
id | 486314 |
size | 1,337,592 |
A flexible implementation of the Builder pattern supporting nested builders and collection-specific subformers. Comprehensive struct support with enum support under active development.
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.
Former
?Compared to manually implementing the Builder pattern or using other builder crates, former
offers several advantages:
#[ derive( Former ) ]
automatically generates the builder struct, storage, and setters, saving you significant repetitive coding effort..field_name( value )
).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 = ... ) ]
.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.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.
Derive Former
on your struct and use the generated ::former()
method to start building:
# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ]
# fn main()
# {
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
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:
# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ]
# fn main()
# {
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 );
# }
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
# #[ cfg( not( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "enabled", feature = "derive_former", any( feature = "use_alloc", not( feature = "no_std" ) ) ) ) ]
# fn main()
# {
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.
For scenarios where you want a direct constructor function instead of always starting with YourType::former()
, former
offers standalone constructors.
#[ standalone_constructors ]
to your struct or enum definition.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 }
).#[ former_ignore ]
to exclude them from constructor arguments.#[ former_ignore ]
, the standalone constructor takes all fields as arguments and returns the instance directly (Self
).#[ former_ignore ]
, the standalone constructor takes only non-ignored fields as arguments and returns the Former
type.Example: Struct Standalone Constructors
# #[ cfg( any( not( feature = "derive_former" ), not( feature = "enabled" ) ) ) ]
# fn main() {}
# #[ cfg( all( feature = "derive_former", feature = "enabled" ) ) ]
# fn main()
# {
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
Understanding the terminology used in former
will help you leverage its full potential, especially when working with enums and variants:
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.Status::Active
).Message::Error(String)
, Point::Coords(i32, i32)
).Request::Get { url: String, headers: Vec<String> }
).Status::Active
) or empty tuple (Status::Active()
).Message::Text(String)
or User::Profile { name: String }
).Point::Coords(i32, i32)
or Request::Post { url: String, body: String }
).Message::text("hello")
→ Message::Text("hello")
).Status::active()
→ Status::Active
).MyEnum::variant_name(...)
).#[standalone_constructors]
is used (e.g., variant_name(...)
).#[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.#[ derive( Former ) ]
for structs (enums under development).Default
trait or custom defaultsOption<T>
supportDefault
values and Option< T >
fields. Custom defaults via #[ former( default = ... ) ]
.#[ 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 formerMyEnum::variant()
) - Fully functional#[scalar]
, #[subform_scalar]
, #[standalone_constructors]
for fine-grained control#[scalar]
attribute, #[former_ignore]
not yet implemented#[ scalar( name = ... ) ]
, #[ subform_... ( name = ... ) ]
#[ scalar( setter = false ) ]
, #[ subform_... ( setter = false ) ]
impl Former
#[ subform_collection( definition = ... ) ]
#[ storage_fields( ... ) ]
.#[ mutator( custom ) ]
+ impl FormerMutator
.FormingEnd
.Collection
traits."Missing Former types" Error
BreakFormer not found
or RunFormerDefinition not found
#[derive(Former)]
enabled// #[derive(Debug, Clone, PartialEq, former::Former)]
and uncomment themRaw Identifier Compilation Errors
"KeywordVariantEnumr#breakFormerStorage" is not a valid identifier
r#break
, r#move
)#[scalar]
attribute on variants with keyword identifiersInner Doc Comment Errors (E0753)
inner doc comments are not permitted here
when compiling tests//!
comments included via include!()
macro//!
with regular //
comments in included test filesTest Import/Scope Issues
TestEnum not found
or similar import errors in test filesuse crate::inc::module::TestEnum
)*_only_test.rs
files are included by derive.rs
/manual.rs
, not standalone modulesEnum Field Method Not Found
.field_name()
not found on enum variant former._0(value)
instead of .field_name(value)
#[scalar]
for direct constructionStandalone Constructor Conflicts
Self
directly when no fields are marked with #[former_ignore]