forked from mirrors/gecko-dev
247 lines
6.8 KiB
Rust
247 lines
6.8 KiB
Rust
use proc_macro2::TokenStream;
|
|
use syn::{self, DeriveInput};
|
|
|
|
type Fields = syn::punctuated::Punctuated<syn::Field, syn::token::Comma>;
|
|
|
|
fn derive_trait<F>(
|
|
input: &DeriveInput,
|
|
trait_name: TokenStream,
|
|
generics: &syn::Generics,
|
|
body: F
|
|
) -> TokenStream
|
|
where
|
|
F: FnOnce() -> TokenStream,
|
|
{
|
|
let struct_name = &input.ident;
|
|
|
|
let (impl_generics, _, where_clause) = generics.split_for_impl();
|
|
let (_, ty_generics, _) = input.generics.split_for_impl();
|
|
|
|
let body = body();
|
|
quote! {
|
|
impl #impl_generics #trait_name for #struct_name #ty_generics #where_clause {
|
|
#body
|
|
}
|
|
}
|
|
}
|
|
|
|
fn derive_simple_trait<F>(
|
|
input: &DeriveInput,
|
|
trait_name: TokenStream,
|
|
t: &syn::TypeParam,
|
|
body: F,
|
|
) -> TokenStream
|
|
where
|
|
F: FnOnce() -> TokenStream,
|
|
{
|
|
let mut generics = input.generics.clone();
|
|
generics
|
|
.make_where_clause()
|
|
.predicates
|
|
.push(parse_quote!(#t: #trait_name));
|
|
derive_trait(input, trait_name, &generics, body)
|
|
}
|
|
|
|
fn each_field_except_unit<F>(
|
|
fields: &Fields,
|
|
unit: &syn::Field,
|
|
mut field_expr: F,
|
|
) -> TokenStream
|
|
where
|
|
F: FnMut(&syn::Ident) -> TokenStream,
|
|
{
|
|
fields.iter().filter(|f| f.ident != unit.ident).fold(quote! {}, |body, field| {
|
|
let name = field.ident.as_ref().unwrap();
|
|
let expr = field_expr(name);
|
|
quote! {
|
|
#body
|
|
#expr
|
|
}
|
|
})
|
|
}
|
|
|
|
|
|
fn derive_struct_body<F>(
|
|
fields: &Fields,
|
|
unit: &syn::Field,
|
|
mut field_expr: F,
|
|
) -> TokenStream
|
|
where
|
|
F: FnMut(&syn::Ident) -> TokenStream,
|
|
{
|
|
let body = each_field_except_unit(fields, unit, |name| {
|
|
let expr = field_expr(name);
|
|
quote! {
|
|
#name: #expr,
|
|
}
|
|
});
|
|
|
|
let unit_name = unit.ident.as_ref().unwrap();
|
|
quote! {
|
|
Self {
|
|
#body
|
|
#unit_name: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn clone_impl(input: &DeriveInput, fields: &Fields, unit: &syn::Field, t: &syn::TypeParam) -> TokenStream {
|
|
derive_simple_trait(input, quote! { Clone }, t, || {
|
|
let body = derive_struct_body(fields, unit, |name| {
|
|
quote! { self.#name.clone() }
|
|
});
|
|
quote! {
|
|
fn clone(&self) -> Self {
|
|
#body
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
fn copy_impl(input: &DeriveInput, t: &syn::TypeParam) -> TokenStream {
|
|
derive_simple_trait(input, quote!{ Copy }, t, || quote! {})
|
|
}
|
|
|
|
fn eq_impl(input: &DeriveInput, t: &syn::TypeParam) -> TokenStream {
|
|
derive_simple_trait(input, quote!{ ::core::cmp::Eq }, t, || quote! {})
|
|
}
|
|
|
|
fn partialeq_impl(input: &DeriveInput, fields: &Fields, unit: &syn::Field, t: &syn::TypeParam) -> TokenStream {
|
|
derive_simple_trait(input, quote!{ ::core::cmp::PartialEq }, t, || {
|
|
let body = each_field_except_unit(fields, unit, |name| {
|
|
quote! { && self.#name == other.#name }
|
|
});
|
|
|
|
quote! {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
true #body
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
fn hash_impl(input: &DeriveInput, fields: &Fields, unit: &syn::Field, t: &syn::TypeParam) -> TokenStream {
|
|
derive_simple_trait(input, quote!{ ::core::hash::Hash }, t, || {
|
|
let body = each_field_except_unit(fields, unit, |name| {
|
|
quote! { self.#name.hash(h); }
|
|
});
|
|
|
|
quote! {
|
|
fn hash<H: ::core::hash::Hasher>(&self, h: &mut H) {
|
|
#body
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
fn serde_impl(
|
|
input: &DeriveInput,
|
|
fields: &Fields,
|
|
unit: &syn::Field,
|
|
t: &syn::TypeParam,
|
|
) -> TokenStream {
|
|
let deserialize_impl = {
|
|
let mut generics = input.generics.clone();
|
|
generics.params.insert(0, parse_quote!('de));
|
|
generics
|
|
.make_where_clause()
|
|
.predicates
|
|
.push(parse_quote!(#t: ::serde::Deserialize<'de>));
|
|
derive_trait(input, quote!{ ::serde::Deserialize<'de> }, &generics, || {
|
|
let tuple = each_field_except_unit(fields, unit, |name| {
|
|
quote! { #name, }
|
|
});
|
|
let body = derive_struct_body(fields, unit, |name| quote! { #name });
|
|
quote! {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: ::serde::Deserializer<'de>,
|
|
{
|
|
let (#tuple) = ::serde::Deserialize::deserialize(deserializer)?;
|
|
Ok(#body)
|
|
}
|
|
}
|
|
})
|
|
};
|
|
|
|
let serialize_impl = derive_simple_trait(input, quote! { ::serde::Serialize }, t, || {
|
|
let tuple = each_field_except_unit(fields, unit, |name| {
|
|
quote! { &self.#name, }
|
|
});
|
|
quote! {
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: ::serde::Serializer,
|
|
{
|
|
(#tuple).serialize(serializer)
|
|
}
|
|
}
|
|
});
|
|
|
|
quote! {
|
|
#[cfg(feature = "serde")]
|
|
#serialize_impl
|
|
#[cfg(feature = "serde")]
|
|
#deserialize_impl
|
|
}
|
|
}
|
|
|
|
pub fn derive(input: DeriveInput) -> TokenStream {
|
|
let s = match input.data {
|
|
syn::Data::Struct(ref s) => s,
|
|
_ => panic!("Need to derive this on a struct"),
|
|
};
|
|
|
|
let fields = match s.fields {
|
|
syn::Fields::Named(ref named) => &named.named,
|
|
_ => panic!("Need to use named fields"),
|
|
};
|
|
|
|
assert!(!fields.is_empty());
|
|
|
|
let unit_field = fields.last().unwrap();
|
|
assert_eq!(
|
|
unit_field.value().ident.as_ref().unwrap().to_string(),
|
|
"_unit",
|
|
"You need to have a _unit field to derive this trait",
|
|
);
|
|
|
|
assert!(match unit_field.value().vis {
|
|
syn::Visibility::Public(..) => true,
|
|
_ => false,
|
|
}, "Unit field should be public");
|
|
|
|
assert!(input.attrs.iter().filter_map(|attr| attr.interpret_meta()).any(|attr| {
|
|
match attr {
|
|
syn::Meta::Word(..) |
|
|
syn::Meta::NameValue(..) => false,
|
|
syn::Meta::List(ref list) => {
|
|
list.ident == "repr" && list.nested.iter().any(|meta| {
|
|
match *meta {
|
|
syn::NestedMeta::Meta(syn::Meta::Word(ref w)) => w == "C",
|
|
_ => false,
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}), "struct should be #[repr(C)]");
|
|
|
|
let type_param =
|
|
input.generics.type_params().next().cloned().expect("Need a T");
|
|
|
|
let clone = clone_impl(&input, fields, unit_field.value(), &type_param);
|
|
let copy = copy_impl(&input, &type_param);
|
|
let serde = serde_impl(&input, fields, unit_field.value(), &type_param);
|
|
let eq = eq_impl(&input, &type_param);
|
|
let partialeq = partialeq_impl(&input, fields, unit_field.value(), &type_param);
|
|
let hash = hash_impl(&input, fields, unit_field.value(), &type_param);
|
|
|
|
quote! {
|
|
#clone
|
|
#copy
|
|
#serde
|
|
#eq
|
|
#partialeq
|
|
#hash
|
|
}
|
|
}
|