use syn::{GenericParam, Generics, ItemFn};
#[derive(Debug, Clone, Default)]
#[allow(dead_code)]
pub struct GenericInfo {
pub type_params: Vec<String>,
pub lifetime_params: Vec<String>,
pub const_params: Vec<String>,
}
#[allow(dead_code)]
impl GenericInfo {
pub fn from_generics(generics: &Generics) -> Self {
let mut type_params = Vec::new();
let mut lifetime_params = Vec::new();
let mut const_params = Vec::new();
for param in &generics.params {
match param {
GenericParam::Type(type_param) => {
type_params.push(type_param.ident.to_string());
}
GenericParam::Lifetime(lifetime_param) => {
lifetime_params.push(lifetime_param.lifetime.ident.to_string());
}
GenericParam::Const(const_param) => {
const_params.push(const_param.ident.to_string());
}
}
}
Self {
type_params,
lifetime_params,
const_params,
}
}
pub fn is_generic(&self) -> bool {
!self.type_params.is_empty()
|| !self.lifetime_params.is_empty()
|| !self.const_params.is_empty()
}
pub fn is_type_param(&self, name: &str) -> bool {
self.type_params.iter().any(|p| p == name)
}
}
#[allow(dead_code)]
pub fn is_generic_function(func: &ItemFn) -> bool {
!func.sig.generics.params.is_empty()
}
#[cfg(test)]
mod tests {
use super::*;
use syn::parse_quote;
#[test]
fn test_extract_type_params() {
let func: ItemFn = parse_quote! {
fn example<T, U>(t: T, u: U) -> (T, U) {
(t, u)
}
};
let info = GenericInfo::from_generics(&func.sig.generics);
assert_eq!(info.type_params, vec!["T", "U"]);
assert!(info.is_generic());
}
#[test]
fn test_extract_lifetime_params() {
let func: ItemFn = parse_quote! {
fn example<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
x
}
};
let info = GenericInfo::from_generics(&func.sig.generics);
assert_eq!(info.lifetime_params, vec!["a", "b"]);
assert!(info.is_generic());
}
#[test]
fn test_extract_const_params() {
let func: ItemFn = parse_quote! {
fn example<const N: usize>(arr: [i32; N]) -> [i32; N] {
arr
}
};
let info = GenericInfo::from_generics(&func.sig.generics);
assert_eq!(info.const_params, vec!["N"]);
assert!(info.is_generic());
}
#[test]
fn test_mixed_generics() {
let func: ItemFn = parse_quote! {
fn example<'a, T, const N: usize>(x: &'a [T; N]) -> &'a T {
&x[0]
}
};
let info = GenericInfo::from_generics(&func.sig.generics);
assert_eq!(info.lifetime_params, vec!["a"]);
assert_eq!(info.type_params, vec!["T"]);
assert_eq!(info.const_params, vec!["N"]);
}
#[test]
fn test_is_type_param() {
let func: ItemFn = parse_quote! {
fn example<T, U>(t: T, u: U) {}
};
let info = GenericInfo::from_generics(&func.sig.generics);
assert!(info.is_type_param("T"));
assert!(info.is_type_param("U"));
assert!(!info.is_type_param("V"));
}
#[test]
fn test_is_generic_function() {
let func: ItemFn = parse_quote! {
fn example<T>(value: T) -> T { value }
};
assert!(is_generic_function(&func));
let func: ItemFn = parse_quote! {
fn example(value: i32) -> i32 { value }
};
assert!(!is_generic_function(&func));
}
#[test]
fn test_generic_with_where_clause() {
let func: ItemFn = parse_quote! {
fn example<T>(value: T) -> T
where
T: Clone + std::fmt::Debug,
{
value
}
};
let info = GenericInfo::from_generics(&func.sig.generics);
assert_eq!(info.type_params, vec!["T"]);
assert!(is_generic_function(&func));
}
#[test]
fn test_multiple_type_params() {
let func: ItemFn = parse_quote! {
fn example<T, U, V>(t: T, u: U, v: V) -> (T, U, V) {
(t, u, v)
}
};
let info = GenericInfo::from_generics(&func.sig.generics);
assert_eq!(info.type_params.len(), 3);
assert!(info.is_type_param("T"));
assert!(info.is_type_param("U"));
assert!(info.is_type_param("V"));
}
#[test]
fn test_generic_with_default() {
let func: ItemFn = parse_quote! {
fn example<T = i32>(value: T) -> T {
value
}
};
let info = GenericInfo::from_generics(&func.sig.generics);
assert_eq!(info.type_params, vec!["T"]);
}
#[test]
fn test_non_generic_function() {
let func: ItemFn = parse_quote! {
fn example(x: i32, y: i32) -> i32 {
x + y
}
};
let info = GenericInfo::from_generics(&func.sig.generics);
assert!(!info.is_generic());
assert!(info.type_params.is_empty());
assert!(info.lifetime_params.is_empty());
assert!(info.const_params.is_empty());
}
#[test]
fn test_generic_with_trait_bounds() {
let func: ItemFn = parse_quote! {
fn example<T: Clone + Send>(value: T) -> T {
value.clone()
}
};
let info = GenericInfo::from_generics(&func.sig.generics);
assert_eq!(info.type_params, vec!["T"]);
assert!(info.is_generic());
}
}