Search code examples
rustrust-proc-macros

Rust proc_macro_derive: How do I check if a field is of a primitive type, like boolean?


I'm trying to filter out all the fields of a struct that are of type bool. But the syn::Type enum doesn't seem to have a case for it, or I'm reading the definitions incorrectly:

pub enum Type {
    Array(TypeArray),
    BareFn(TypeBareFn),
    Group(TypeGroup),
    ImplTrait(TypeImplTrait),
    Infer(TypeInfer),
    Macro(TypeMacro),
    Never(TypeNever),
    Paren(TypeParen),
    Path(TypePath),
    Ptr(TypePtr),
    Reference(TypeReference),
    Slice(TypeSlice),
    TraitObject(TypeTraitObject),
    Tuple(TypeTuple),
    Verbatim(TokenStream),
    // some variants omitted
}

I looked trough syn::Types source, to check which variants where ommited, but that didn't bring me any further. Here's what I have until now:

#[proc_macro_derive(Creator)]
pub fn derive_creator(_item: TokenStream) -> TokenStream {
    let item = parse_macro_input!(_item as syn::DeriveInput);
    let item_ident = item.ident;

    let fields = if let syn::Data::Struct(syn::DataStruct {
        fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
        ..
    }) = item.data
    {
        named
    } else {
        panic!("You can derive Creator only on a struct!")
    };

    let bool_fields = fields.iter().filter(|field| 
        match field.ty {
            // case when field type is bool => true
            _ => false
        }
    );
    unimplemented!()
}

Am I going down the wrong path? Or is this simply not possible? Or am I missing something?


Solution

  • I feel like there might be a cleaner way (without having to clone and allocate a string), but in the past I've done something like:

    match field.ty {
        Type::Path(type_path) if type_path.clone().into_token_stream().to_string() == "bool" => {
            true
        }
        _ => false
    }
    

    You might be able to define the bool type once and then compare it for equality:

    let bool_ty = Type::Path(TypePath {
        qself: None,
        path: Path::from(Ident::new("bool", Span::call_site())),
    });
    
    if field.ty == bool_ty {
        // it's a bool
    }
    

    But I'm not sure if a difference in the span would affect equality. Span appears to not implement PartialEq, so my guess is that this is ok.*


    *Edits to clarify this are welcome.