cubecl-macros-internal 0.11.3

Internal procedural macros for CubeCL
Documentation
use darling::{
    FromDeriveInput, FromField, FromVariant,
    ast::{Data, Fields},
    util::Flag,
};
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::{DeriveInput, Expr, Generics, Ident, Path, Type};

#[derive(FromDeriveInput)]
#[darling(attributes(type_hash))]
pub struct TypeHash {
    pub ident: Ident,
    pub generics: Generics,
    pub data: Data<TypeHashVariant, TypeHashField>,
}

#[derive(FromVariant)]
#[darling(attributes(type_hash))]
pub struct TypeHashVariant {
    pub discriminant: Option<Expr>,
    pub fields: Fields<TypeHashField>,
}

#[derive(FromField)]
#[darling(attributes(type_hash))]
pub struct TypeHashField {
    pub ident: Option<Ident>,
    pub ty: Type,

    pub r#as: Option<Path>,
    pub skip: Flag,
    pub foreign_type: Flag,
}

pub fn type_hash_impl(input: DeriveInput) -> TokenStream {
    let type_hash = TypeHash::from_derive_input(&input).unwrap();
    match type_hash.data {
        Data::Enum(data) => type_hash_enum(&type_hash.ident, &type_hash.generics, &data),
        Data::Struct(fields) => type_hash_struct(&type_hash.ident, &type_hash.generics, &fields),
    }
}

fn type_hash_struct(
    ident: &Ident,
    generics: &Generics,
    data: &Fields<TypeHashField>,
) -> TokenStream {
    let name = ident.to_string();
    let fields = data.iter().flat_map(write_field_hash);
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
    quote! {
        impl #impl_generics crate::TypeHash for #ident #ty_generics #where_clause {
            fn write_hash(hasher: &mut impl core::hash::Hasher) {
                hasher.write(#name.as_bytes());
                #(#fields)*
            }
        }
    }
}

fn type_hash_enum(ident: &Ident, generics: &Generics, data: &[TypeHashVariant]) -> TokenStream {
    let name = ident.to_string();
    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
    let variants = data.iter().flat_map(|v| {
        v.discriminant
            .iter()
            .map(|discriminant| quote! [core::hash::Hash::hash(&(#discriminant as isize), hasher);])
            .chain(v.fields.iter().map(write_field_hash))
    });
    quote! {
        impl #impl_generics crate::TypeHash for #ident #ty_generics #where_clause{
            fn write_hash(hasher: &mut impl core::hash::Hasher) {
                hasher.write(#name.as_bytes());
                #(#variants)*
            }
        }
    }
}

fn write_field_hash(field: &TypeHashField) -> TokenStream {
    let field_name = field
        .ident
        .as_ref()
        .map(|name| {
            let name = name.to_token_stream().to_string();
            quote! { hasher.write(#name.as_bytes()); }
        })
        .unwrap_or_default();
    if let Some(ty) = &field.r#as {
        quote! {
            #field_name
            <#ty as crate::TypeHash>::write_hash(hasher);
        }
    } else if field.skip.is_present() {
        TokenStream::new()
    } else if field.foreign_type.is_present() {
        let type_str = field.ty.to_token_stream().to_string();
        quote! {
            #field_name
            hasher.write(#type_str.as_bytes());
        }
    } else {
        let field_type = &field.ty;
        quote! {
            #field_name
            <#field_type as crate::TypeHash>::write_hash(hasher);
        }
    }
}