Releases: welf/type-state-builder
Release v0.5.0
Added
- Const builder support with
#[builder(const)]attribute- All builder methods (
builder(), setters,build()) generated asconst fn - Enables compile-time constant construction for embedded systems, static configuration, etc.
- Closure converters automatically transformed to
const fnhelpers - Uses struct reconstruction pattern (
Self { field, ..self }) for const compatibility
- All builder methods (
Validation
- Require explicit defaults for optional fields when using const builders (
Default::default()is not const) - Disallow
impl_intoattribute with const builders (trait bounds not supported in const fn) - Skip
Defaulttrait implementation for const builders
v0.4.1
What's New
no_std Support
TypeStateBuilder now generates code compatible with no_std environments. The generated code uses core:: types instead of std:: types:
core::option::Optioncore::marker::PhantomDatacore::fmt::Debugcore::default::Default
No feature flags required.
Documentation Improvements
- Added Design Philosophy section explaining the rationale behind actionable error messages
- Updated lib.rs documentation to complement README
Other
- Downgraded proptest dev dependency to 1.4.0 to maintain MSRV compatibility (Rust 1.70.0)
Release v0.3.1
Fixed
- Documentation example in generated
buildermethod now usesignoreattribute to prevent doctest execution conflicts
Release v0.3.0
Added
- Custom Converter Attribute: New
converterattribute for advanced field transformations using closures#[builder(converter = |value: InputType| expression)]for custom conversion logic- Support for complex transformations beyond simple
Intoconversions - Works with any valid Rust expression or closure
- Examples:
#[builder(converter = |s: &str| s.to_uppercase())] name: String, #[builder(converter = |items: Vec<&str>| items.iter().map(|s| s.to_string()).collect())] tags: Vec<String>,
- Comprehensive validation prevents conflicts with
impl_intoandskip_setterattributes - Zero runtime overhead - all conversions happen at compile time
Changed
- Error message format improved to follow Rust's standard diagnostic format with structured error/note/help components
- All validation error messages now provide clearer, more actionable guidance
Improved
- Developer Experience: More flexible field transformation options beyond basic
Intoconversions - Documentation: Comprehensive examples for converter usage patterns and best practices
- Testing: Added extensive test coverage for converter functionality with edge cases
Fixed
- UI test expectations updated for improved error messages
- More validation tests for attribute conflicts and invalid combinations
Migration Guide
The converter attribute is a new optional feature that doesn't affect existing code. All existing
#[derive(TypeStateBuilder)] usage continues to work exactly as before.
New converter functionality:
#[derive(TypeStateBuilder)]
struct Config {
// New: Custom converter for complex transformations
#[builder(converter = |path: &str| PathBuf::from(path).canonicalize().unwrap())]
config_path: PathBuf,
// Existing functionality unchanged
#[builder(required)]
name: String,
#[builder(impl_into, default = "description".to_string()")]
description: String,
}Release v0.2.0: Ergonomic Conversions with impl_into
🎯 What's New
Ergonomic Conversions with impl_into
The headline feature of this release is the new impl_into attribute system that allows setter methods to accept impl Into<FieldType> parameters instead of requiring the exact field type. This eliminates the need for manual .to_string() calls and other common conversions.
Before v0.2.0:
let user = User::builder()
.name("Alice".to_string()) // Manual conversion required
.email("alice@example.com".to_string()) // Manual conversion required
.build();With v0.2.0:
#[derive(TypeStateBuilder)]
#[builder(impl_into)] // Enable ergonomic conversions
struct User {
#[builder(required)]
name: String,
#[builder(required)]
email: String,
}
let user = User::builder()
.name("Alice") // &str automatically converts to String
.email("alice@example.com") // &str automatically converts to String
.build();✨ Key Features
Flexible Configuration Options
- Struct-level: Apply
#[builder(impl_into)]to enable for all setters - Field-level: Use
#[builder(impl_into)]or#[builder(impl_into = false)]for fine-grained control - Smart precedence: Field-level settings override struct-level defaults
#[derive(TypeStateBuilder)]
#[builder(impl_into)] // Default for all fields
struct Document {
#[builder(required)]
title: String, // Inherits: accepts &str, String, etc.
#[builder(required, impl_into = false)]
content: String, // Override: requires String directly
#[builder(impl_into = true)]
category: Option<String>, // Explicit: accepts impl Into<String>
}Zero Runtime Cost
All conversions happen at compile time with no performance overhead. The generated code is as efficient as if you wrote the conversions manually.
Type Safety Guaranteed
Only types that implement Into<FieldType> are accepted, maintaining Rust's compile-time safety guarantees.
Common Use Cases Supported
- String fields: Accept
&str,String,Cow<str>, and more - Path fields: Accept
&str,&Path,String,PathBuf - Collection fields: Accept arrays, slices, iterators where applicable
- Custom types: Any type implementing
Into<T>works automatically
🛡️ Safety & Validation
- Conflict detection: Prevents invalid combinations like
impl_intowithskip_setter - Clear error messages: Descriptive compile-time errors guide you to correct usage
- Comprehensive validation: All attribute combinations are validated at compile time
📚 Enhanced Documentation
- Complete examples: Real-world usage patterns and best practices
- Working doctests: All documentation examples are tested and guaranteed to work
- Migration guide: Easy upgrade path from previous versions
🧪 Robust Testing
This release includes 341+ comprehensive tests covering:
- Unit tests for all new functionality
- Integration tests for real-world scenarios
- UI tests for error message validation
- Doctest coverage for all examples
🔄 Backward Compatibility
This is a fully backward-compatible release. All existing code continues to work without any changes. The impl_into attribute is completely opt-in.
📊 What This Means For You
More Ergonomic APIs
// Before: Verbose and repetitive
let config = ServerConfig::builder()
.host("localhost".to_string())
.database_url("postgresql://...".to_string())
.log_file("/var/log/app.log".to_string())
.build();// After: Clean and natural
#[derive(TypeStateBuilder)]
#[builder(impl_into)]
struct ServerConfig { /* ... */ }
let config = ServerConfig::builder()
.host("localhost")
.database_url("postgresql://...")
.log_file("/var/log/app.log")
.build();Better Developer Experience
- Less boilerplate code
- More readable builder chains
- Fewer manual type conversions
- Natural API feel
Maintained Safety
- Same compile-time guarantees
- No runtime performance cost
- Type safety preserved
- Clear error messages
🚀 Getting Started
Update your Cargo.toml:
[dependencies]
type-state-builder = "0.2.0"Add the attribute to your structs:
use type_state_builder::TypeStateBuilder;
#[derive(TypeStateBuilder)]
#[builder(impl_into)] // Enable ergonomic conversions
struct YourStruct {
// Your fields here
}Full Changelog: v0.1.2...v0.2.0
v0.1.2 - Improved Type Names
Type-State Builder v0.1.2 - Improved Type Names 🏗️
This release significantly improves the readability of generated builder type names by converting snake_case field names to PascalCase in type names.
🎯 What's New
Enhanced Type Name Readability
- Generated builder type names now use PascalCase for field names instead of preserving underscores
- Error messages are now much clearer and follow Rust type naming conventions
📝 Before vs After
Before (0.1.1):
// Hard to read type names in error messages
LanguageConfigBuilder_HasLanguage_id_MissingFqn_separator
After (0.1.2):
// Clean, readable type names
LanguageConfigBuilder_HasLanguageId_MissingFqnSeparator
🔧 Example
#[derive(TypeStateBuilder)]
pub struct LanguageConfig {
#[builder(required)]
pub language_id: String,
#[builder(required)]
pub fqn_separator: String,
}
// Generated types are now much more readable:
// - LanguageConfigBuilder_MissingLanguageId_MissingFqnSeparator
// - LanguageConfigBuilder_HasLanguageId_MissingFqnSeparator
// - LanguageConfigBuilder_HasLanguageId_HasFqnSeparator
## 🛠️ Technical Details
- Implemented automatic snake_case to PascalCase conversion for field names in type generation
- Improved developer experience with clearer error messages
- All existing functionality remains unchanged
- Comprehensive test coverage for the new naming conventions
## 📦 Installation
```toml
[dependencies]
type-state-builder = "0.1.2"Full Changelog: v0.1.1...v0.1.2
v0.1.1 - Fix builder visibility issue
🔧 Bug Fix Release: Visibility Inheritance
This patch release fixes a critical issue with builder visibility that prevented cross-module usage of the type-state builder pattern.
🐛 Fixed
- Visibility inheritance: Generated builder types now correctly inherit the visibility of the original struct
- Public structs generate public builders for cross-module usage
- Private structs generate private builders to respect Rust privacy rules
- All visibility levels supported:
pub,pub(crate),pub(super),pub(in path), and private - Fixes compilation errors when using builders across module boundaries
v0.1.0 - Initial Release
Initial release of TypeStateBuilder - a derive macro for compile-time safe builder patterns in Rust.
Features
- Initial implementation of the type-state builder pattern derive macro
- Support for required and optional fields with compile-time validation
- Custom setter names and prefixes for flexible API design
- Custom build method names for enhanced usability
- Comprehensive generic type support including lifetimes and where clauses
- Skip setter functionality for auto-generated fields
- Custom default values for optional fields
- Zero runtime overhead with compile-time state machine validation
- Automatic builder pattern selection (type-state vs regular) based on field requirements
- Extensive test coverage including UI tests for error messages
- Complete documentation with examples and usage patterns
Installation
Add this to your Cargo.toml:
[dependencies]
type-state-builder = "0.1.0"See the https://docs.rs/type-state-builder for usage examples.