Skip to content

Commit

Permalink
Simplify generated struct when only generic on lifetimes + add skip a…
Browse files Browse the repository at this point in the history
…ttribute
  • Loading branch information
Ten0 committed Mar 10, 2024
1 parent 76989fa commit d2b99e1
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 18 deletions.
29 changes: 28 additions & 1 deletion serde_avro_derive/tests/derive_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ fn generics() {
test::<Generics<'_, Bar>>(
r#"{
"type": "record",
"name": "derive_schema.Generics_62462e653c3a8376",
"name": "derive_schema.Generics_b8f49e32140be9d5",
"fields": [
{
"name": "s1",
Expand Down Expand Up @@ -171,3 +171,30 @@ fn generics() {
}"#,
);
}

#[derive(serde_avro_derive::Schema)]
#[allow(unused)]
struct Lifetimes<'a, 'b> {
s: &'a [&'b str],
#[avro_schema(skip)]
z: String,
}

#[test]
fn lifetimes() {
test::<Lifetimes<'_, '_>>(
r#"{
"type": "record",
"name": "derive_schema.Lifetimes",
"fields": [
{
"name": "s",
"type": {
"type": "array",
"items": "string"
}
}
]
}"#,
);
}
2 changes: 1 addition & 1 deletion serde_avro_derive_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
darling = "0.20"
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["visit", "extra-traits"] }
syn = { version = "2", features = ["visit", "visit-mut", "extra-traits"] }

[dev-dependencies]
serde_avro_derive = { path = "../serde_avro_derive" }
Expand Down
58 changes: 42 additions & 16 deletions serde_avro_derive_macros/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,37 @@ use {
syn::{
parse_quote,
visit::{self, Visit},
visit_mut::{self, VisitMut},
Error,
},
};

#[derive(darling::FromDeriveInput)]
#[darling(attributes(avro_schema), supports(struct_named))]
pub(crate) struct SchemaDeriveInput {
pub(super) ident: proc_macro2::Ident,
pub(super) data: darling::ast::Data<(), SchemaDeriveField>,
pub(super) generics: syn::Generics,
ident: proc_macro2::Ident,
data: darling::ast::Data<(), SchemaDeriveField>,
generics: syn::Generics,
}

#[derive(darling::FromField)]
#[darling(attributes(avro_schema))]
pub(crate) struct SchemaDeriveField {
pub(super) ident: Option<proc_macro2::Ident>,
pub(super) ty: syn::Type,
ident: Option<proc_macro2::Ident>,
ty: syn::Type,

skip: darling::util::Flag,
}

pub(crate) fn schema_impl(input: SchemaDeriveInput) -> Result<TokenStream, Error> {
let fields = input
let mut fields = input
.data
.take_struct()
.expect("Supports directive should prevent enums");
fields.fields.retain(|f| !f.skip.is_present());

let ident = &input.ident;
let struct_name = ident.to_string();
let struct_ident = &input.ident;
let struct_name = struct_ident.to_string();
let mut generics = input.generics;

let mut added_where_clause_predicate_for_types: std::collections::HashSet<_> =
Expand Down Expand Up @@ -63,17 +67,29 @@ pub(crate) fn schema_impl(input: SchemaDeriveInput) -> Result<TokenStream, Error
})
.collect::<Vec<_>>();

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

let field_names = fields
.iter()
.map(|f| f.ident.as_ref().map(|i| i.to_string()))
.collect::<Option<Vec<_>>>()
.ok_or_else(|| Error::new(Span::call_site(), "Unnamed fields are not supported"))?;

let has_generics = !generics.params.is_empty();
let (type_lookup, type_lookup_decl): (syn::Type, _) = match has_generics {
false => (parse_quote!(Self), None),
let has_non_lifetime_generics = generics
.params
.iter()
.any(|gp| !matches!(gp, syn::GenericParam::Lifetime(_)));
let (type_lookup, type_lookup_decl): (syn::Type, _) = match has_non_lifetime_generics {
false => {
let type_lookup = if generics.params.is_empty() {
parse_quote!(Self)
} else {
let mut generics_static = generics.clone();
let mut turn_lifetimes_to_static = TurnLifetimesToStatic;
turn_lifetimes_to_static.visit_generics_mut(&mut generics_static);
let (_, ty_generics, _) = generics_static.split_for_impl();
parse_quote!(#struct_ident #ty_generics)
};
(type_lookup, None)
}
true => {
// The struct we are deriving on is generic, but we need the TypeLookup to be
// 'static otherwise it won't implement `Any`, so we need to generate a
Expand All @@ -94,7 +110,7 @@ pub(crate) fn schema_impl(input: SchemaDeriveInput) -> Result<TokenStream, Error
// <Bar as BuildSchema>::TypeLookup,
// <Baz as BuildSchema>::TypeLookup,
// >;
let type_lookup_ident = format_ident!("{ident}TypeLookup");
let type_lookup_ident = format_ident!("{struct_ident}TypeLookup");
let type_params: Vec<syn::Ident> =
(0..fields.len()).map(|i| format_ident!("T{}", i)).collect();
let struct_decl = syn::ItemStruct {
Expand Down Expand Up @@ -148,7 +164,7 @@ pub(crate) fn schema_impl(input: SchemaDeriveInput) -> Result<TokenStream, Error
}
};

let add_type_id_to_fqn = if has_generics {
let add_type_id_to_fqn = if has_non_lifetime_generics {
quote! {
serde_avro_derive::hash_type_id(
&mut struct_name,
Expand All @@ -159,11 +175,13 @@ pub(crate) fn schema_impl(input: SchemaDeriveInput) -> Result<TokenStream, Error
quote! {}
};

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

Ok(quote! {
const _: () = {
use serde_avro_derive::serde_avro_fast::schema;

impl #impl_generics serde_avro_derive::BuildSchema for #ident #ty_generics #where_clause {
impl #impl_generics serde_avro_derive::BuildSchema for #struct_ident #ty_generics #where_clause {
fn append_schema(builder: &mut serde_avro_derive::SchemaBuilder) {
let reserved_schema_key = builder.reserve();
let mut struct_name = module_path!().replace("::", ".");
Expand Down Expand Up @@ -232,3 +250,11 @@ impl Visit<'_> for IsRelevantGeneric<'_> {
visit::visit_const_param(self, v)
}
}

struct TurnLifetimesToStatic;
impl VisitMut for TurnLifetimesToStatic {
fn visit_lifetime_mut(&mut self, i: &mut syn::Lifetime) {
i.ident = format_ident!("static");
visit_mut::visit_lifetime_mut(self, i)
}
}

0 comments on commit d2b99e1

Please sign in to comment.