https://github.com/tomoikey/typesafe_builder
🚀 The Ultimate Builder Pattern Implementation Powered by Rust's Type System. Type safety is not a luxury, it's a necessity.
https://github.com/tomoikey/typesafe_builder
builder crate crates crates-io library patterns rust rust-lang typesafe
Last synced: 23 days ago
JSON representation
🚀 The Ultimate Builder Pattern Implementation Powered by Rust's Type System. Type safety is not a luxury, it's a necessity.
- Host: GitHub
- URL: https://github.com/tomoikey/typesafe_builder
- Owner: tomoikey
- License: mit
- Created: 2025-05-31T21:40:35.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2025-08-16T04:01:18.000Z (about 2 months ago)
- Last Synced: 2025-08-16T05:39:18.384Z (about 2 months ago)
- Topics: builder, crate, crates, crates-io, library, patterns, rust, rust-lang, typesafe
- Language: Rust
- Homepage:
- Size: 69.3 KB
- Stars: 28
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# TypeSafe Builder
![]()
![]()
![]()
![]()
Compile-Time Type Safety • Zero Runtime Cost • Blazing Fast Builds
**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**.```rust
// ❌ Traditional builder - potential runtime errors
let user = UserBuilder::new()
.name("Alice")
.build()?; // Compiles even with missing required fields// ✅ TypeSafe Builder - compile-time safety guarantee
let user = UserBuilder::new()
.with_name("Alice".to_string())
.with_email("alice@example.com".to_string()) // 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`### Performance Characteristics
- **Zero Runtime Cost** - All validation completed at compile time### Safety Guarantees
- **No Panic** - Complete elimination of runtime panics## Quick Start
```toml
[dependencies]
typesafe_builder = "*.*.*" # Replace with the actual version
``````rust
use typesafe_builder::*;#[derive(Builder)]
struct User {
#[builder(required)]
name: String,
#[builder(optional)]
age: Option,
#[builder(default = "String::from(\"user@example.com\")")]
email: String,
#[builder(default)]
active: bool,
}// Type-safe builder pattern
let user = UserBuilder::new()
.with_name("Alice".to_string())
.with_age(30)
.build(); // email will be "user@example.com", active will be false
```## Advanced Features
### 1. Conditional Required Fields
```rust
use typesafe_builder::*;#[derive(Builder)]
struct Account {
#[builder(optional)]
email: Option,
#[builder(required_if = "email")] // Required when email is set
email_verified: Option,
}// ✅ Compiles successfully
let account1 = AccountBuilder::new().build();// ✅ Compiles successfully
let account2 = AccountBuilder::new()
.with_email("user@example.com".to_string())
.with_email_verified(true)
.build();// ❌ Compile error: email_verified is not set
// let account3 = AccountBuilder::new()
// .with_email("user@example.com".to_string())
// .build();
```### 2. Conditional Optional Fields
```rust
use typesafe_builder::*;#[derive(Builder)]
struct Config {
#[builder(optional)]
debug_mode: Option,
#[builder(optional_if = "debug_mode")] // Required when debug_mode is not set
log_level: Option,
}// ✅ When debug_mode is not set, log_level is required
let config1 = ConfigBuilder::new()
.with_log_level("INFO".to_string())
.build();// ✅ When debug_mode is set, log_level is optional
let config2 = ConfigBuilder::new()
.with_debug_mode(true)
.build();
```### 3. Complex Conditional Logic
```rust
use typesafe_builder::*;#[derive(Builder)]
struct ApiClient {
#[builder(optional)]
use_auth: Option,
#[builder(optional)]
use_https: Option,
#[builder(optional)]
api_key: Option,// Secret is required if using auth OR HTTPS
#[builder(required_if = "use_auth || use_https")]
secret: Option,// Certificate is required only when using both auth AND HTTPS
#[builder(required_if = "use_auth && use_https")]
certificate: Option,// Warning is required when using neither auth NOR HTTPS
#[builder(required_if = "!use_auth && !use_https")]
insecure_warning: Option,// Complex condition: Token required when (auth OR HTTPS) AND (no API key)
#[builder(required_if = "(use_auth || use_https) && !api_key")]
fallback_token: Option,
}// ✅ All dependencies satisfied (auth + HTTPS)
let client1 = ApiClientBuilder::new()
.with_use_auth(true)
.with_use_https(true)
.with_api_key("key123".to_string())
.with_secret("secret456".to_string())
.with_certificate("cert.pem".to_string())
.build();// ✅ Insecure configuration with warning
let client2 = ApiClientBuilder::new()
.with_use_auth(false)
.with_use_https(false)
.with_insecure_warning("WARNING: Insecure connection!".to_string())
.build();// ✅ Using fallback token when API key is not set
let client3 = ApiClientBuilder::new()
.with_use_auth(true)
.with_secret("secret".to_string())
.with_fallback_token("backup_token".to_string())
.build();
```### 4. Default Values
TypeSafe Builder supports two ways to specify default values:
#### Simple Default Values (using `Default::default()`)
```rust
use typesafe_builder::*;#[derive(Builder)]
struct Config {
// Uses String::default() (empty string)
#[builder(default)]
name: String,// Uses i32::default() (0)
#[builder(default)]
port: i32,// Uses bool::default() (false)
#[builder(default)]
enabled: bool,// Uses Vec::default() (empty vector)
#[builder(default)]
items: Vec,// Uses HashMap::default() (empty map)
#[builder(default)]
metadata: std::collections::HashMap,// Works with custom types that implement Default
#[builder(default)]
custom_field: MyCustomType,#[builder(required)]
service_name: String,
}// ✅ Use default values
let config = ConfigBuilder::new()
.with_service_name("my-service".to_string())
.build();
// name: "", port: 0, enabled: false, items: [], metadata: {}, custom_field: MyCustomType::default()
```#### Custom Default Expressions
```rust
use typesafe_builder::*;#[derive(Builder)]
struct ServerConfig {
#[builder(default = "String::from(\"localhost\")")]
host: String,#[builder(default = "8080")]
port: u16,#[builder(default = "vec![\"GET\".to_string(), \"POST\".to_string()]")]
allowed_methods: Vec,#[builder(default = "std::collections::HashMap::new()")]
headers: std::collections::HashMap,#[builder(required)]
service_name: String,#[builder(optional)]
ssl_cert: Option,
}// ✅ Use default values
let config1 = ServerConfigBuilder::new()
.with_service_name("my-api".to_string())
.build();
// host: "localhost", port: 8080, allowed_methods: ["GET", "POST"], headers: {}// ✅ Override some defaults
let config2 = ServerConfigBuilder::new()
.with_service_name("my-api".to_string())
.with_host("0.0.0.0".to_string())
.with_port(3000)
.build();
// host: "0.0.0.0", port: 3000, allowed_methods: ["GET", "POST"], headers: {}// ✅ Complex default expressions
#[derive(Builder)]
struct AppConfig {
#[builder(default = "std::env::var(\"APP_NAME\").unwrap_or_else(|_| \"default-app\".to_string())")]
app_name: String,#[builder(default = "chrono::Utc::now()")]
created_at: chrono::DateTime,#[builder(default = "uuid::Uuid::new_v4()")]
instance_id: uuid::Uuid,
}
```#### Mixed Default Types
```rust
use typesafe_builder::*;#[derive(Builder)]
struct MixedConfig {
// Simple default (uses Default::default())
#[builder(default)]
name: String,// Custom expression default
#[builder(default = "42")]
port: i32,// Simple default for collections
#[builder(default)]
tags: Vec,// Custom expression for complex initialization
#[builder(default = "std::collections::HashMap::from([(\"key\".to_string(), \"value\".to_string())])")]
metadata: std::collections::HashMap,
}let config = MixedConfigBuilder::new().build();
// name: "", port: 42, tags: [], metadata: {"key": "value"}
```Key features of default values:
- **Simple defaults**: Use `#[builder(default)]` for types implementing `Default`
- **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
```rust
use typesafe_builder::*;#[derive(Builder)]
struct Database {
#[builder(optional)]
use_ssl: Option,// Warning message required when NOT using SSL
#[builder(required_if = "!use_ssl")]
warning_message: Option,
}// ✅ Warning configuration for non-SSL usage
let db = DatabaseBuilder::new()
.with_use_ssl(false)
.with_warning_message("Insecure connection!".to_string())
.build();
```### 6. Into Conversion Support
The `#[builder(into)]` attribute allows setter methods to accept any type that implements `Into` for the field type `T`, providing more ergonomic APIs:
```rust
use typesafe_builder::*;#[derive(Builder)]
struct User {
#[builder(required)]
#[builder(into)]
name: String,#[builder(optional)]
#[builder(into)]
email: Option,
}// ✅ Accept &str directly (converts to String via Into)
let user1 = UserBuilder::new()
.with_name("Alice") // &str -> String
.with_email("alice@example.com") // &str -> String
.build();// ✅ Still works with String directly
let user2 = UserBuilder::new()
.with_name("Bob".to_string())
.build();
```Key benefits:
- Ergonomic APIs: Accept `&str` for `String` fields without manual conversion
- Type flexibility: Any `Into` implementation works automatically
- Zero overhead: Conversion happens at the call site
- Backward compatible: Works alongside existing setter patterns### 7. Custom Builder Name
```rust
use typesafe_builder::*;#[derive(Builder)]
#[builder(name = "MyCustomBuilder")] // Customize the builder name
struct User {
#[builder(required)]
name: String,
}// Use the customized builder name
let user = MyCustomBuilder::new()
.with_name("Alice".to_string())
.build();
```## Error Handling
### Compile-Time Error Examples
```rust
#[derive(Builder)]
struct User {
#[builder(required)]
name: String,
}// ❌ Compile error
let user = UserBuilder::new().build();
// ^^^^^
// error: no method named `build` found for struct `UserBuilder<_TypesafeBuilderEmpty>`
// method `build` is available on `UserBuilder<_TypesafeBuilderFilled>`
```### Constraint Violation Error Examples
```rust
#[derive(Builder)]
struct Config {
#[builder(optional)]
feature: Option,
#[builder(required_if = "feature")]
config: Option,
}// ❌ Compile error
let config = ConfigBuilder::new()
.with_feature(true)
.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
```rust
#[derive(Builder)]
struct ApiConfig {
#[builder(required)]
base_url: String,#[builder(optional)]
use_auth: Option,#[builder(required_if = "use_auth")]
api_key: Option,#[builder(required_if = "use_auth")]
secret: Option,#[builder(default = "30")]
timeout_seconds: u64,#[builder(default = "String::from(\"application/json\")")]
content_type: String,
}
```### Database Connection
```rust
#[derive(Builder)]
struct DatabaseConfig {
#[builder(required)]
host: String,#[builder(required)]
database: String,#[builder(default = "5432")]
port: u16,#[builder(default = "10")]
max_connections: u32,#[builder(optional)]
use_ssl: Option,#[builder(required_if = "use_ssl")]
ssl_cert_path: Option,#[builder(optional_if = "!use_ssl")]
allow_insecure: Option,
}
```## Contributing
We welcome contributions to TypeSafe Builder!
### Development Environment Setup
```bash
git clone https://github.com/tomoikey/typesafe_builder.git
cd typesafe_builder
cargo test
```### Running Tests
```bash
# Run all tests
cargo test# UI tests (compile error verification)
cargo test --package typesafe_builder_derive --test ui
```## Contributors
Amazing developers who have contributed to this project:
![]()
tomoikey
Creator & Maintainer
![]()
ramsyana
Contributor
![]()
Your Name Here
Next Contributor
*Want to see your name here? [Contribute now](https://github.com/tomoikey/typesafe_builder/blob/main/CONTRIBUTING.md) and join our amazing community!*
[](https://github.com/tomoikey/typesafe_builder/graphs/contributors)
## License
MIT License - see the [LICENSE](LICENSE) file for details.
## Give us a star!
If you find this project useful, please consider giving it a star!
---
Made with ❤️ by Rust community
Type safety is not a luxury, it's a necessity.