enum_dispatch/
supported_generics.rspub enum SupportedGenericArg {
Inferred,
Identifier(proc_macro2::Ident),
ConstChar(syn::LitChar),
ConstByte(syn::LitByte),
ConstInt(syn::LitInt),
ConstBool(syn::LitBool),
}
pub enum UnsupportedGenericArg {
NonIdentifierType,
NonIntegralConstGenericType,
Lifetime,
Constraint,
AssocType,
AssocConst,
Unknown,
}
impl std::fmt::Display for UnsupportedGenericArg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NonIdentifierType => write!(f, "Generic types in #[enum_dispatch(...)] must be identifiers"),
Self::NonIntegralConstGenericType => write!(f, "Non-integral const generic types in #[enum_dispatch(...)] are not supported"),
Self::Lifetime => write!(f, "Lifetime generics in #[enum_dispatch(...)] are not supported"),
Self::AssocType => write!(f, "Generic associated types in #[enum_dispatch(...)] are not supported"),
Self::AssocConst => write!(f, "Generic associated constants in #[enum_dispatch(...)] are not supported"),
Self::Constraint => write!(f, "Generic trait constraints in #[enum_dispatch(...)] are not supported"),
Self::Unknown => write!(f, "Unsupported generic argument syntax in #[enum_dispatch(...)]"),
}
}
}
const SUPPORTED_CONST_GENERIC_TYPES: &[&str] = &[
"u8",
"i8",
"u16",
"i16",
"u32",
"i32",
"u64",
"i64",
"u128",
"i128",
"usize",
"isize",
"char",
"bool",
];
pub fn num_supported_generics(g: &syn::Generics) -> usize {
let type_generics = g.type_params().count();
let const_generics = g.const_params().filter(|p| {
if let syn::Type::Path(syn::TypePath { qself: None, path }) = &p.ty {
for supported_type in SUPPORTED_CONST_GENERIC_TYPES {
if path.is_ident(supported_type) {
return true;
}
}
}
false
}).count();
type_generics + const_generics
}
pub fn convert_to_supported_generic(generic_arg: &syn::GenericArgument) -> Result<SupportedGenericArg, (UnsupportedGenericArg, proc_macro2::Span)> {
use syn::spanned::Spanned as _;
let span = generic_arg.span();
match generic_arg {
syn::GenericArgument::Type(syn::Type::Path(t)) if t.qself.is_none() => {
if let Some(ident) = t.path.get_ident() {
Ok(SupportedGenericArg::Identifier(ident.clone()))
} else {
Err((UnsupportedGenericArg::NonIdentifierType, span))
}
}
syn::GenericArgument::Type(syn::Type::Infer(_)) => Ok(SupportedGenericArg::Inferred),
syn::GenericArgument::Type(_) => Err((UnsupportedGenericArg::NonIdentifierType, span)),
syn::GenericArgument::Const(syn::Expr::Lit(syn::ExprLit { attrs: _, lit })) => {
match lit {
syn::Lit::Byte(b) => Ok(SupportedGenericArg::ConstByte(b.clone())),
syn::Lit::Char(c) => Ok(SupportedGenericArg::ConstChar(c.clone())),
syn::Lit::Int(i) => Ok(SupportedGenericArg::ConstInt(i.clone())),
syn::Lit::Bool(b) => Ok(SupportedGenericArg::ConstBool(b.clone())),
_ => Err((UnsupportedGenericArg::NonIntegralConstGenericType, span)),
}
}
syn::GenericArgument::Const(_) => Err((UnsupportedGenericArg::NonIntegralConstGenericType, span)),
syn::GenericArgument::Lifetime(_) => Err((UnsupportedGenericArg::Lifetime, span)),
syn::GenericArgument::Constraint(_) => Err((UnsupportedGenericArg::Constraint, span)),
syn::GenericArgument::AssocType(_) => Err((UnsupportedGenericArg::AssocType, span)),
syn::GenericArgument::AssocConst(_) => Err((UnsupportedGenericArg::AssocConst, span)),
_ => Err((UnsupportedGenericArg::Unknown, span)),
}
}