Skip to content

Commit 2919bc1

Browse files
committed
Add testcase
1 parent 0ececcd commit 2919bc1

1 file changed

Lines changed: 101 additions & 0 deletions

File tree

crates/vespera_macro/src/parser/schema.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,6 +1644,107 @@ mod tests {
16441644
assert_eq!(substituted, ty);
16451645
}
16461646

1647+
#[rstest]
1648+
// Direct generic param substitution
1649+
#[case("T", &["T"], &["String"], "String")]
1650+
// Vec<T> substitution
1651+
#[case("Vec<T>", &["T"], &["String"], "Vec < String >")]
1652+
// Option<T> substitution
1653+
#[case("Option<T>", &["T"], &["i32"], "Option < i32 >")]
1654+
// Nested: Vec<Option<T>>
1655+
#[case("Vec<Option<T>>", &["T"], &["String"], "Vec < Option < String > >")]
1656+
// Deeply nested: Option<Vec<Option<T>>>
1657+
#[case("Option<Vec<Option<T>>>", &["T"], &["bool"], "Option < Vec < Option < bool > > >")]
1658+
// Multiple generic params
1659+
#[case("HashMap<K, V>", &["K", "V"], &["String", "i32"], "HashMap < String , i32 >")]
1660+
// Generic param not in list (unchanged)
1661+
#[case("Vec<U>", &["T"], &["String"], "Vec < U >")]
1662+
// Non-generic type (unchanged)
1663+
#[case("String", &["T"], &["i32"], "String")]
1664+
// Reference type: &T
1665+
#[case("&T", &["T"], &["String"], "& String")]
1666+
// Mutable reference: &mut T
1667+
#[case("&mut T", &["T"], &["i32"], "& mut i32")]
1668+
// Slice type: [T]
1669+
#[case("[T]", &["T"], &["String"], "[String]")]
1670+
// Array type: [T; 5]
1671+
#[case("[T; 5]", &["T"], &["u8"], "[u8 ; 5]")]
1672+
// Tuple type: (T, U)
1673+
#[case("(T, U)", &["T", "U"], &["String", "i32"], "(String , i32)")]
1674+
// Complex nested tuple
1675+
#[case("(Vec<T>, Option<U>)", &["T", "U"], &["String", "bool"], "(Vec < String > , Option < bool >)")]
1676+
// Reference to Vec<T>
1677+
#[case("&Vec<T>", &["T"], &["String"], "& Vec < String >")]
1678+
// Multi-segment path (no substitution for crate::Type)
1679+
#[case("std::vec::Vec<T>", &["T"], &["String"], "std :: vec :: Vec < String >")]
1680+
fn test_substitute_type_comprehensive(
1681+
#[case] input: &str,
1682+
#[case] params: &[&str],
1683+
#[case] concrete: &[&str],
1684+
#[case] expected: &str,
1685+
) {
1686+
let ty: Type = syn::parse_str(input).unwrap();
1687+
let generic_params: Vec<String> = params.iter().map(|s| s.to_string()).collect();
1688+
let concrete_types: Vec<Type> = concrete.iter().map(|s| syn::parse_str(s).unwrap()).collect();
1689+
let concrete_refs: Vec<&Type> = concrete_types.iter().collect();
1690+
1691+
let result = substitute_type(&ty, &generic_params, &concrete_refs);
1692+
let result_str = quote::quote!(#result).to_string();
1693+
1694+
assert_eq!(result_str, expected, "Input: {}", input);
1695+
}
1696+
1697+
#[test]
1698+
fn test_substitute_type_empty_path_segments() {
1699+
// Create a TypePath with empty segments
1700+
let ty = Type::Path(syn::TypePath {
1701+
qself: None,
1702+
path: syn::Path {
1703+
leading_colon: None,
1704+
segments: syn::punctuated::Punctuated::new(),
1705+
},
1706+
});
1707+
let concrete: Type = syn::parse_str("String").unwrap();
1708+
let result = substitute_type(&ty, &[String::from("T")], &[&concrete]);
1709+
// Should return the original type unchanged
1710+
assert_eq!(result, ty);
1711+
}
1712+
1713+
#[test]
1714+
fn test_substitute_type_with_lifetime_generic_argument() {
1715+
// Test type with lifetime: Cow<'static, T>
1716+
// The lifetime argument should be preserved while T is substituted
1717+
let ty: Type = syn::parse_str("std::borrow::Cow<'static, T>").unwrap();
1718+
let concrete: Type = syn::parse_str("String").unwrap();
1719+
let result = substitute_type(&ty, &[String::from("T")], &[&concrete]);
1720+
let result_str = quote::quote!(#result).to_string();
1721+
// Lifetime 'static should be preserved, T should be substituted
1722+
assert_eq!(result_str, "std :: borrow :: Cow < 'static , String >");
1723+
}
1724+
1725+
#[test]
1726+
fn test_substitute_type_parenthesized_args() {
1727+
// Fn(T) -> U style (parenthesized arguments)
1728+
// This tests the `other => other.clone()` branch for PathArguments
1729+
let ty: Type = syn::parse_str("fn(T) -> U").unwrap();
1730+
let concrete_t: Type = syn::parse_str("String").unwrap();
1731+
let concrete_u: Type = syn::parse_str("i32").unwrap();
1732+
let result = substitute_type(&ty, &[String::from("T"), String::from("U")], &[&concrete_t, &concrete_u]);
1733+
// Type::BareFn doesn't go through the Path branch, falls to _ => ty.clone()
1734+
assert_eq!(result, ty);
1735+
}
1736+
1737+
#[test]
1738+
fn test_substitute_type_path_without_angle_brackets() {
1739+
// Test path with parenthesized arguments: Fn(T) -> U as a trait
1740+
let ty: Type = syn::parse_str("dyn Fn(T) -> U").unwrap();
1741+
let concrete_t: Type = syn::parse_str("String").unwrap();
1742+
let concrete_u: Type = syn::parse_str("i32").unwrap();
1743+
let result = substitute_type(&ty, &[String::from("T"), String::from("U")], &[&concrete_t, &concrete_u]);
1744+
// Type::TraitObject falls to _ => ty.clone()
1745+
assert_eq!(result, ty);
1746+
}
1747+
16471748
#[rstest]
16481749
#[case("&i32")]
16491750
#[case("std::string::String")]

0 commit comments

Comments
 (0)