use std::collections::HashMap;
use std::path::PathBuf;
use type_state_builder::TypeStateBuilder;
#[derive(TypeStateBuilder, Debug, PartialEq)]
#[builder(impl_into)] struct WebServerConfig {
#[builder(required)]
host: String,
#[builder(required)]
port: u16,
#[builder(default = String::from("web"))]
name: String,
#[builder(default = PathBuf::from("/var/log/app.log"))]
log_file: PathBuf,
#[builder(default = Vec::new())]
allowed_origins: Vec<String>,
#[builder(impl_into = false)] headers: HashMap<String, String>,
ssl_enabled: Option<bool>,
}
#[derive(TypeStateBuilder, Debug, PartialEq)]
struct UserProfile {
#[builder(required, impl_into)] username: String,
#[builder(required)] user_id: String,
#[builder(impl_into = true)] display_name: Option<String>,
#[builder(default = Vec::new(), impl_into = false)] tags: Vec<String>,
#[builder(default = false)]
is_admin: bool,
}
fn main() {
println!("=== WebServerConfig with struct-level impl_into ===");
let server_config = WebServerConfig::builder()
.host("localhost") .port(8080u16) .name("api-server") .log_file("/tmp/server.log") .allowed_origins(vec!["https://example.com".to_string()])
.headers({
let mut headers = HashMap::new();
headers.insert("Content-Type".to_string(), "application/json".to_string());
headers.insert("X-API-Version".to_string(), "1.0".to_string());
headers
})
.ssl_enabled(Some(true))
.build();
println!("{server_config:#?}");
assert_eq!(server_config.host, "localhost");
assert_eq!(server_config.port, 8080u16);
assert_eq!(server_config.name, "api-server");
assert_eq!(server_config.log_file, PathBuf::from("/tmp/server.log"));
assert_eq!(server_config.allowed_origins, vec!["https://example.com"]);
assert_eq!(
server_config.headers.get("Content-Type"),
Some(&"application/json".to_string())
);
assert_eq!(server_config.ssl_enabled, Some(true));
println!("\n=== UserProfile with mixed impl_into settings ===");
let profile = UserProfile::builder()
.username("alice") .user_id("user_12345".to_string()) .display_name(Some("Alice Smith".to_string())) .tags(vec!["developer".to_string(), "rust".to_string()]) .is_admin(false)
.build();
println!("{profile:#?}");
assert_eq!(profile.username, "alice");
assert_eq!(profile.user_id, "user_12345");
assert_eq!(profile.display_name, Some("Alice Smith".to_string()));
assert_eq!(profile.tags, vec!["developer", "rust"]);
assert!(!profile.is_admin);
println!("\n=== Demonstrating type flexibility ===");
let flexible_config = WebServerConfig::builder()
.host("0.0.0.0".to_string()) .port(3000u16) .name("flexible-server") .log_file(PathBuf::from("/custom/path.log")) .allowed_origins(vec!["*".to_string()])
.headers(HashMap::new()) .build();
println!("{flexible_config:#?}");
assert_eq!(flexible_config.host, "0.0.0.0");
assert_eq!(flexible_config.port, 3000u16);
assert_eq!(flexible_config.name, "flexible-server");
assert_eq!(flexible_config.log_file, PathBuf::from("/custom/path.log"));
assert_eq!(flexible_config.allowed_origins, vec!["*"]);
assert!(flexible_config.headers.is_empty());
assert_eq!(flexible_config.ssl_enabled, None);
println!("\n=== Benefits of impl_into ===");
println!("✅ More ergonomic: Use '\"string\"' instead of '\"string\".to_string()'");
println!("✅ Flexible: Accepts String, &str, Cow<str>, etc.");
println!("✅ Zero cost: Conversions happen at compile time");
println!("✅ Type safe: Only accepts types that implement Into<FieldType>");
println!("✅ Optional override: Field-level settings can override struct-level defaults");
#[derive(TypeStateBuilder, Debug, PartialEq)]
struct ContactInfo {
#[builder(required, impl_into)]
name: String,
#[builder(required, converter = |email: &str| email.trim().to_lowercase())]
email: String,
#[builder(converter = |tags: &str| tags.split(',').map(|s| s.trim().to_string()).collect())]
tags: Vec<String>,
#[builder(converter = |age: &str| age.parse().unwrap_or(0))]
age: u32,
}
println!("\n=== Converter vs impl_into comparison ===");
let contact = ContactInfo::builder()
.name("Alice") .email(" ALICE@EXAMPLE.COM ") .tags("developer, rust, backend") .age("25") .build();
println!("{contact:#?}");
assert_eq!(contact.name, "Alice");
assert_eq!(contact.email, "alice@example.com"); assert_eq!(contact.tags, vec!["developer", "rust", "backend"]); assert_eq!(contact.age, 25);
println!("\n=== When to use impl_into vs converter ===");
println!("📋 Use `impl_into` when:");
println!(" • You want simple type conversions (String/&str, PathBuf/&Path)");
println!(" • The Into trait already provides the conversion you need");
println!(" • You want zero-cost abstractions");
println!(" • You want zero-cost abstractions\n");
println!("🔧 Use `converter` when:");
println!(" • You need custom transformation logic (parsing, validation, normalization)");
println!(" • You want to parse structured data from strings");
println!(" • You need conversions that Into trait cannot provide");
}