use type_state_builder::TypeStateBuilder;
#[derive(TypeStateBuilder, Debug, PartialEq)]
struct TypeStateWithCustomConverters {
#[builder(required, converter = |items: Vec<&str>| items.into_iter().map(|s| s.to_string()).collect())]
tags: Vec<String>,
#[builder(required, converter = |path: &str| std::path::PathBuf::from(path))]
config_path: std::path::PathBuf,
#[builder(converter = |seconds: u64| Some(std::time::Duration::from_secs(seconds)))]
timeout: Option<std::time::Duration>,
#[builder(converter = |text: &str| Some(text.to_uppercase()))]
description: Option<String>,
}
#[test]
fn test_type_state_custom_converters() {
let instance = TypeStateWithCustomConverters::builder()
.tags(vec!["rust", "testing"]) .config_path("/etc/config") .timeout(30) .description("hello world") .build();
assert_eq!(
instance.tags,
vec!["rust".to_string(), "testing".to_string()]
);
assert_eq!(
instance.config_path,
std::path::PathBuf::from("/etc/config")
);
assert_eq!(instance.timeout, Some(std::time::Duration::from_secs(30)));
assert_eq!(instance.description, Some("HELLO WORLD".to_string()));
}
#[test]
fn test_type_state_custom_converters_minimal() {
let instance = TypeStateWithCustomConverters::builder()
.tags(vec!["tag1", "tag2"]) .config_path("minimal.conf") .build();
assert_eq!(instance.tags, vec!["tag1".to_string(), "tag2".to_string()]);
assert_eq!(
instance.config_path,
std::path::PathBuf::from("minimal.conf")
);
assert_eq!(instance.timeout, None);
assert_eq!(instance.description, None);
}
#[derive(TypeStateBuilder, Debug, PartialEq)]
struct RegularWithCustomConverters {
#[builder(converter = |items: Vec<&str>| items.into_iter().map(|s| s.to_string()).collect())]
items: Vec<String>,
#[builder(converter = |seconds: u64| std::time::Duration::from_secs(seconds))]
delay: std::time::Duration,
#[builder(converter = |input: &str| format!("PARSED: {input}"))]
name: String,
#[builder(converter = |n: i32| n * 2)]
count: i32,
}
#[test]
fn test_regular_custom_converters() {
let instance = RegularWithCustomConverters::builder()
.items(vec!["item1", "item2"]) .delay(5) .name("test name") .count(21) .build();
assert_eq!(
instance.items,
vec!["item1".to_string(), "item2".to_string()]
);
assert_eq!(instance.delay, std::time::Duration::from_secs(5));
assert_eq!(instance.name, "PARSED: test name".to_string());
assert_eq!(instance.count, 42);
}
#[test]
fn test_regular_custom_converters_default_fallback() {
let instance = RegularWithCustomConverters::builder()
.name("only name")
.build();
assert_eq!(instance.items, Vec::<String>::new());
assert_eq!(instance.delay, std::time::Duration::default());
assert_eq!(instance.name, "PARSED: only name".to_string());
assert_eq!(instance.count, 0);
}
#[derive(TypeStateBuilder, Debug, PartialEq)]
#[builder(setter_prefix = "with_")]
struct MixedCustomConverters {
#[builder(required)]
id: u32,
#[builder(required, converter = |items: Vec<&str>| items.into_iter().map(|s| s.to_string()).collect(), setter_name = "categories")]
tags: Vec<String>,
#[builder(converter = |value: i32| Some(value), setter_prefix = "set_")]
value: Option<i32>,
#[builder(default = String::from("default"), converter = |text: &str| text.to_uppercase())]
label: String,
}
#[test]
fn test_mixed_custom_converters_with_attributes() {
let instance = MixedCustomConverters::builder()
.with_id(123) .with_categories(vec!["cat1", "cat2"]) .set_value(42) .with_label("custom label") .build();
assert_eq!(instance.id, 123);
assert_eq!(instance.tags, vec!["cat1".to_string(), "cat2".to_string()]);
assert_eq!(instance.value, Some(42));
assert_eq!(instance.label, "CUSTOM LABEL".to_string());
}
#[test]
fn test_mixed_custom_converters_with_defaults() {
let instance = MixedCustomConverters::builder()
.with_id(456)
.with_categories(vec!["single"]) .build();
assert_eq!(instance.id, 456);
assert_eq!(instance.tags, vec!["single".to_string()]);
assert_eq!(instance.value, None); assert_eq!(instance.label, "default".to_string()); }
#[derive(TypeStateBuilder, Debug, PartialEq)]
struct GenericWithCustomConverters<T>
where
T: Clone + std::fmt::Debug,
{
#[builder(required)]
data: T,
#[builder(converter = |items: Vec<T>| items.into_iter().take(3).collect())]
limited: Vec<T>,
}
#[test]
fn test_generic_custom_converters() {
let instance = GenericWithCustomConverters::<String>::builder()
.data("test".to_string())
.limited(vec![
"a".to_string(),
"b".to_string(),
"c".to_string(),
"d".to_string(),
])
.build();
assert_eq!(instance.data, "test".to_string());
assert_eq!(instance.limited.len(), 3); }
#[derive(TypeStateBuilder)]
struct OptionalFieldWithCustomConverter {
#[builder(converter = |value: i32| Some(value * 10))]
multiplied: Option<i32>,
}
#[test]
fn test_optional_field_with_custom_converter() {
let instance = OptionalFieldWithCustomConverter::builder()
.multiplied(5) .build();
assert_eq!(instance.multiplied, Some(50));
let instance2 = OptionalFieldWithCustomConverter::builder().build();
assert_eq!(instance2.multiplied, None);
}
#[derive(TypeStateBuilder)]
struct EdgeCases {
#[builder(converter = |input: Vec<String>| input.into_iter().collect::<Vec<_>>())]
identity: Vec<String>,
}
#[test]
fn test_edge_cases() {
let instance = EdgeCases::builder()
.identity(vec!["test".to_string()])
.build();
assert_eq!(instance.identity, vec!["test".to_string()]);
}