Expand description
Type-State Builder Pattern Implementation
This crate provides a derive macro for implementing the Type-State Builder pattern in Rust, enabling compile-time validation of required fields and zero-cost builder abstractions.
§Overview
The Type-State Builder pattern uses Rust’s type system to enforce compile-time validation of required fields. It automatically selects between two builder patterns:
- Type-State Builder: For structs with required fields, providing compile-time
safety that prevents calling
build()until all required fields are set - Regular Builder: For structs with only optional fields, providing a simple
builder with immediate
build()availability
§Key Features
- Zero Runtime Cost: All validation happens at compile time
- Automatic Pattern Selection: Chooses the best builder pattern for your struct
- Comprehensive Generic Support: Handles complex generic types and lifetimes
- Flexible Configuration: Extensive attribute-based customization
- Excellent Error Messages: Clear guidance for fixing configuration issues
§Quick Start
Add the derive macro to your struct and mark required fields:
use type_state_builder::TypeStateBuilder;
#[derive(TypeStateBuilder)]
struct User {
#[builder(required)]
name: String,
#[builder(required)]
email: String,
age: Option<u32>,
active: bool, // Will use Default::default()
}
// Usage - this enforces that name and email are set
let user = User::builder()
.name("Alice".to_string())
.email("alice@example.com".to_string())
.age(Some(30))
.build();§Supported Attributes
§Struct-level Attributes
#[builder(build_method = "method_name")]- Custom build method name#[builder(setter_prefix = "prefix_")]- Prefix for all setter method names
§Field-level Attributes
#[builder(required)]- Mark field as required#[builder(setter_name = "name")]- Custom setter method name#[builder(setter_prefix = "prefix_")]- Custom prefix for setter method name#[builder(default = "expr")]- Custom default value expression#[builder(skip_setter)]- Don’t generate setter (requires default)
§Advanced Examples
§Custom Defaults and Setter Names
use type_state_builder::TypeStateBuilder;
#[derive(TypeStateBuilder)]
#[builder(build_method = "create")]
struct Document {
#[builder(required)]
title: String,
#[builder(required, setter_name = "set_content")]
content: String,
#[builder(default = "42")]
page_count: u32,
#[builder(default = "String::from(\"draft\")", skip_setter)]
status: String,
}
let doc = Document::builder()
.title("My Document".to_string())
.set_content("Document content here".to_string())
.create(); // Custom build method name§Generic Types and Lifetimes
use type_state_builder::TypeStateBuilder;
#[derive(TypeStateBuilder)]
struct Container<T: Clone>
where
T: Send
{
#[builder(required)]
value: T,
#[builder(required)]
name: String,
tags: Vec<String>,
}
let container = Container::builder()
.value(42)
.name("test".to_string())
.tags(vec!["tag1".to_string()])
.build();§Setter Prefix Examples
use type_state_builder::TypeStateBuilder;
// Struct-level setter prefix applies to all fields
#[derive(TypeStateBuilder)]
#[builder(setter_prefix = "with_")]
struct Config {
#[builder(required)]
host: String,
#[builder(required)]
port: u16,
timeout: Option<u32>,
}
let config = Config::builder()
.with_host("localhost".to_string())
.with_port(8080)
.with_timeout(Some(30))
.build();use type_state_builder::TypeStateBuilder;
// Field-level setter prefix overrides struct-level prefix
#[derive(TypeStateBuilder)]
#[builder(setter_prefix = "with_")]
struct Database {
#[builder(required)]
connection_string: String,
#[builder(required, setter_prefix = "set_")]
credentials: String,
#[builder(setter_name = "timeout_seconds")]
timeout: Option<u32>,
}
let db = Database::builder()
.with_connection_string("postgresql://...".to_string())
.set_credentials("user:pass".to_string())
.with_timeout_seconds(Some(60))
.build();§Optional-Only Structs (Regular Builder)
use type_state_builder::TypeStateBuilder;
// No required fields = regular builder pattern
#[derive(TypeStateBuilder)]
struct Settings {
debug: bool,
max_connections: Option<u32>,
#[builder(default = "\"default.log\".to_string()")]
log_file: String,
#[builder(skip_setter, default = "42")]
magic_number: i32,
}
// Can call build() immediately since no required fields
let settings = Settings::builder()
.debug(true)
.max_connections(Some(100))
.build();§Error Prevention
The macro prevents common mistakes at compile time:
ⓘ
use type_state_builder::TypeStateBuilder;
#[derive(TypeStateBuilder)]
struct User {
#[builder(required)]
name: String,
}
let user = User::builder().build(); // ERROR: required field not setⓘ
use type_state_builder::TypeStateBuilder;
#[derive(TypeStateBuilder)]
struct BadConfig {
#[builder(required, default = "test")] // ERROR: Invalid combination
name: String,
}§Module Organization
The crate is organized into several modules that handle different aspects
of the builder generation process. Most users will only interact with the
TypeStateBuilder derive macro.
For complete documentation, examples, and guides, see the README.
Derive Macros§
- Type
State Builder - Derives a type-safe builder for a struct with compile-time validation of required fields.