use proptest::prelude::*;
#[derive(Clone, Debug)]
enum TyModel {
Bool,
I8,
I16,
I32,
I64,
U8,
U16,
U32,
U64,
F32,
F64,
Char,
Str,
Vec(Box<TyModel>),
Opt(Box<TyModel>),
Map(Box<TyModel>, Box<TyModel>),
Tuple(Vec<TyModel>),
}
impl TyModel {
fn rust(&self) -> String {
match self {
TyModel::Bool => "bool".into(),
TyModel::I8 => "i8".into(),
TyModel::I16 => "i16".into(),
TyModel::I32 => "i32".into(),
TyModel::I64 => "i64".into(),
TyModel::U8 => "u8".into(),
TyModel::U16 => "u16".into(),
TyModel::U32 => "u32".into(),
TyModel::U64 => "u64".into(),
TyModel::F32 => "f32".into(),
TyModel::F64 => "f64".into(),
TyModel::Char => "char".into(),
TyModel::Str => "String".into(),
TyModel::Vec(t) => format!("Vec<{}>", t.rust()),
TyModel::Opt(t) => format!("Option<{}>", t.rust()),
TyModel::Map(k, v) => format!("HashMap<{}, {}>", k.rust(), v.rust()),
TyModel::Tuple(es) if es.len() == 1 => format!("({},)", es[0].rust()),
TyModel::Tuple(es) => {
let inner: Vec<_> = es.iter().map(TyModel::rust).collect();
format!("({})", inner.join(", "))
}
}
}
fn wit(&self) -> String {
match self {
TyModel::Bool => "bool".into(),
TyModel::I8 => "s8".into(),
TyModel::I16 => "s16".into(),
TyModel::I32 => "s32".into(),
TyModel::I64 => "s64".into(),
TyModel::U8 => "u8".into(),
TyModel::U16 => "u16".into(),
TyModel::U32 => "u32".into(),
TyModel::U64 => "u64".into(),
TyModel::F32 => "f32".into(),
TyModel::F64 => "f64".into(),
TyModel::Char => "char".into(),
TyModel::Str => "string".into(),
TyModel::Vec(t) => format!("list<{}>", t.wit()),
TyModel::Opt(t) => format!("option<{}>", t.wit()),
TyModel::Map(k, v) => format!("list<tuple<{}, {}>>", k.wit(), v.wit()),
TyModel::Tuple(es) => {
let inner: Vec<_> = es.iter().map(TyModel::wit).collect();
format!("tuple<{}>", inner.join(", "))
}
}
}
}
fn arb_tymodel() -> impl Strategy<Value = TyModel> {
let leaf = prop_oneof![
Just(TyModel::Bool),
Just(TyModel::I8),
Just(TyModel::I16),
Just(TyModel::I32),
Just(TyModel::I64),
Just(TyModel::U8),
Just(TyModel::U16),
Just(TyModel::U32),
Just(TyModel::U64),
Just(TyModel::F32),
Just(TyModel::F64),
Just(TyModel::Char),
Just(TyModel::Str),
];
leaf.prop_recursive(4, 24, 4, |inner| {
prop_oneof![
inner.clone().prop_map(|t| TyModel::Vec(Box::new(t))),
inner.clone().prop_map(|t| TyModel::Opt(Box::new(t))),
(inner.clone(), inner.clone())
.prop_map(|(k, v)| TyModel::Map(Box::new(k), Box::new(v))),
prop::collection::vec(inner.clone(), 1..4).prop_map(TyModel::Tuple),
]
})
}
proptest! {
#[test]
fn rust_type_maps_to_expected_wit(m in arb_tymodel()) {
let src = m.rust();
let ty: syn::Type = syn::parse_str(&src)
.unwrap_or_else(|e| panic!("failed to parse generated type `{src}`: {e}"));
let got = fidius_wit::rust_type_to_wit(&ty)
.unwrap_or_else(|e| panic!("mapping `{src}` failed: {e}"));
prop_assert_eq!(got, m.wit(), "Rust→WIT mapping diverged for `{}`", src);
}
}