TypeSafe Builder
The Ultimate Builder Pattern Implementation Powered by Rust's Type System
Eliminate bugs at the type level and revolutionize your development experience
Why TypeSafe Builder?
Traditional builder patterns can't detect missing required fields until runtime. TypeSafe Builder leverages Rust's powerful type system to verify all constraints at compile time.
// ❌ Traditional builder - potential runtime errors
let user = new
.name
.build?; // Compiles even with missing required fields
// ✅ TypeSafe Builder - compile-time safety guarantee
let user = new
.with_name
.with_email // Compile error if email is required
.build; // Always guaranteed to succeed
Key Features
Type-Level Constraint System
- Required Fields - Completely prevent missing required field configuration
- Optional Fields - Freely configurable fields
- Default Values - Fields with intelligent default values using
Default::default()or custom expressions - Conditional Requirements - Express dynamic dependencies at the type level
- Complex Logic - Support for AND/OR/NOT operators in complex conditional expressions
- Into Conversion - Ergonomic setters with automatic type conversion via
Into<T>
Performance Characteristics
- Zero Runtime Cost - All validation completed at compile time
Safety Guarantees
- No Panic - Complete elimination of runtime panics
Quick Start
[]
= "*.*.*" # Replace with the actual version
use *;
// Type-safe builder pattern
let user = new
.with_name
.with_age
.build; // email will be "user@example.com", active will be false
Advanced Features
1. Conditional Required Fields
use *;
// ✅ Compiles successfully
let account1 = new.build;
// ✅ Compiles successfully
let account2 = new
.with_email
.with_email_verified
.build;
// ❌ Compile error: email_verified is not set
// let account3 = AccountBuilder::new()
// .with_email("user@example.com".to_string())
// .build();
2. Conditional Optional Fields
use *;
// ✅ When debug_mode is not set, log_level is required
let config1 = new
.with_log_level
.build;
// ✅ When debug_mode is set, log_level is optional
let config2 = new
.with_debug_mode
.build;
3. Complex Conditional Logic
use *;
// ✅ All dependencies satisfied (auth + HTTPS)
let client1 = new
.with_use_auth
.with_use_https
.with_api_key
.with_secret
.with_certificate
.build;
// ✅ Insecure configuration with warning
let client2 = new
.with_use_auth
.with_use_https
.with_insecure_warning
.build;
// ✅ Using fallback token when API key is not set
let client3 = new
.with_use_auth
.with_secret
.with_fallback_token
.build;
4. Default Values
TypeSafe Builder supports two ways to specify default values:
Simple Default Values (using Default::default())
use *;
// ✅ Use default values
let config = new
.with_service_name
.build;
// name: "", port: 0, enabled: false, items: [], metadata: {}, custom_field: MyCustomType::default()
Custom Default Expressions
use *;
// ✅ Use default values
let config1 = new
.with_service_name
.build;
// host: "localhost", port: 8080, allowed_methods: ["GET", "POST"], headers: {}
// ✅ Override some defaults
let config2 = new
.with_service_name
.with_host
.with_port
.build;
// host: "0.0.0.0", port: 3000, allowed_methods: ["GET", "POST"], headers: {}
// ✅ Complex default expressions
Mixed Default Types
use *;
let config = new.build;
// name: "", port: 42, tags: [], metadata: {"key": "value"}
Key features of default values:
- Simple defaults: Use
#[builder(default)]for types implementingDefault - Custom expressions: Use
#[builder(default = "expression")]for any valid Rust expression - No type restrictions: Works with primitives, collections, function calls, etc.
- Environment variables: Access environment variables at build time (custom expressions)
- Function calls: Call any function or method as default value (custom expressions)
- Standalone attribute: Cannot be combined with
required,optional, etc. - Zero runtime cost: All defaults are computed at build time
5. Negation Operator Support
use *;
// ✅ Warning configuration for non-SSL usage
let db = new
.with_use_ssl
.with_warning_message
.build;
6. Into Conversion Support
The #[builder(into)] attribute allows setter methods to accept any type that implements Into<T> for the field type T, providing more ergonomic APIs:
use *;
// ✅ Accept &str directly (converts to String via Into)
let user1 = new
.with_name // &str -> String
.with_email // &str -> String
.build;
// ✅ Still works with String directly
let user2 = new
.with_name
.build;
Key benefits:
- Ergonomic APIs: Accept
&strforStringfields without manual conversion - Type flexibility: Any
Into<T>implementation works automatically - Zero overhead: Conversion happens at the call site
- Backward compatible: Works alongside existing setter patterns
7. Custom Builder Name
use *;
// Customize the builder name
// Use the customized builder name
let user = new
.with_name
.build;
Error Handling
Compile-Time Error Examples
// ❌ Compile error
let user = new.build;
// ^^^^^
// error: no method named `build` found for struct `UserBuilder<_TypesafeBuilderEmpty>`
// method `build` is available on `UserBuilder<_TypesafeBuilderFilled>`
Constraint Violation Error Examples
// ❌ Compile error
let config = new
.with_feature
.build;
// ^^^^^
// error: no method named `build` found for struct `ConfigBuilder<_TypesafeBuilderFilled, _TypesafeBuilderEmpty>`
// method `build` is available on `ConfigBuilder<_TypesafeBuilderFilled, _TypesafeBuilderFilled>`
Real-World Use Cases
Web API Configuration
Database Connection
Contributing
We welcome contributions to TypeSafe Builder!
Development Environment Setup
Running Tests
# Run all tests
# UI tests (compile error verification)
Contributors
Amazing developers who have contributed to this project:
Want to see your name here? Contribute now and join our amazing community!
License
MIT License - see the LICENSE file for details.
Give us a star!
If you find this project useful, please consider giving it a star!