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: 5 months 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 (8 months ago)
- Default Branch: main
- Last Pushed: 2025-08-16T04:01:18.000Z (6 months ago)
- Last Synced: 2025-08-16T05:39:18.384Z (6 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.