unscrupulous_derive/
lib.rs1#![doc = include_str!("../README.md")]
3
4use quote::__private::TokenStream;
5use quote::quote;
6use syn::{parse_macro_input, Data, DeriveInput, Field, Type};
7
8#[proc_macro_derive(Unscrupulous)]
10pub fn derive_unscrupulous(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
11 let input = parse_macro_input!(input as DeriveInput);
13
14 let name = input.ident;
16
17 let assertions = match input.data {
19 Data::Struct(data) => assert_fields(data.fields.iter()),
20 Data::Enum(data) => assert_fields(data.variants.iter().flat_map(|var| var.fields.iter())),
21 Data::Union(data) => assert_fields(data.fields.named.iter()),
22 };
23
24 let (generics, generics_of_ty, where_clause) = input.generics.split_for_impl();
26
27 proc_macro::TokenStream::from(quote! {
29 unsafe impl #generics unscrupulous::Unscrupulous for #name #generics_of_ty #where_clause {}
30
31 #(#assertions)*
32 })
33}
34
35#[allow(single_use_lifetimes)]
37fn assert_fields<'a>(fields: impl Iterator<Item = &'a Field>) -> Vec<TokenStream> {
38 fields.map(|f| ensure_type_is_unscrupulous(&f.ty)).collect()
39}
40
41fn ensure_type_is_unscrupulous(ty: &Type) -> TokenStream {
43 quote! {
44 const _: fn() = || {
45 fn ensure_type_is_unscrupulous<T: unscrupulous::Unscrupulous>() {}
46 ensure_type_is_unscrupulous::<#ty>();
47 };
48 }
49}