blob: 891c43ff0ced70f8d36098423c88281c584aa040 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use crate::attributes::{FieldOptions, NamedTypeOptions};
use crate::fields::{
field_to_field_default_expr, field_to_record_fields_expr, field_to_schema_expr,
named_fields_to_record_fields, unnamed_fields_to_record_fields,
};
use crate::implementation::Implementation;
use crate::utils::{aliases, json_value_expr, preserve_optional};
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::spanned::Spanned;
use syn::{DataStruct, Fields, Generics};
pub fn to_implementation(
input_span: Span,
ident: Ident,
generics: Generics,
container_attrs: NamedTypeOptions,
data: DataStruct,
) -> Result<Implementation, Vec<syn::Error>> {
if container_attrs.transparent {
transparent(input_span, ident, generics, &container_attrs, data)
} else {
normal(ident, generics, container_attrs, data)
}
}
fn normal(
ident: Ident,
generics: Generics,
container_attrs: NamedTypeOptions,
data: DataStruct,
) -> Result<Implementation, Vec<syn::Error>> {
let record_fields_expr = match data.fields {
Fields::Named(fields) => named_fields_to_record_fields(fields, container_attrs.rename_all)?,
Fields::Unnamed(fields) => unnamed_fields_to_record_fields(fields)?,
Fields::Unit => {
quote! {
::std::vec::Vec::new()
}
}
};
let record_doc = preserve_optional(container_attrs.doc.map(|s| quote! { #s.to_string() }));
let record_aliases = aliases(&container_attrs.aliases);
let schema_expr = quote! {
::apache_avro::schema::Schema::Record(
::apache_avro::schema::RecordSchema::builder()
.aliases(#record_aliases)
.maybe_doc(#record_doc)
.fields(#record_fields_expr)
.name(name)
.build()
)
};
let record_fields_expr = quote! {
::std::option::Option::Some(#record_fields_expr)
};
Ok(Implementation::named(
ident,
generics,
&container_attrs.name,
schema_expr,
Some(record_fields_expr),
container_attrs
.default
.map(json_value_expr)
.map(|t| quote! { ::std::option::Option::Some(#t)}),
container_attrs.tests,
))
}
fn transparent(
input_span: Span,
ident: Ident,
generics: Generics,
container_attrs: &NamedTypeOptions,
data: DataStruct,
) -> Result<Implementation, Vec<syn::Error>> {
match data.fields {
Fields::Named(fields_named) => {
let mut found = None;
for field in fields_named.named {
let attrs = FieldOptions::new(&field.attrs, field.span())?;
if attrs.skip {
continue;
}
if found.replace((field, attrs)).is_some() {
return Err(vec![syn::Error::new(
input_span,
"AvroSchema: #[serde(transparent)] is only allowed on structs with one unskipped field",
)]);
}
}
if let Some((field, attrs)) = found {
let field_default_expr = field_to_field_default_expr(&field, attrs.default)?;
Ok(Implementation::unnamed(
ident,
generics,
field_to_schema_expr(&field, &attrs.with)?,
Some(field_to_record_fields_expr(&field, &attrs.with)?),
Some(field_default_expr),
container_attrs.tests,
))
} else {
Err(vec![syn::Error::new(
input_span,
"AvroSchema: #[serde(transparent)] is only allowed on structs with one unskipped field",
)])
}
}
Fields::Unnamed(_) => Err(vec![syn::Error::new(
input_span,
"AvroSchema: derive does not work for tuple structs",
)]),
Fields::Unit => Err(vec![syn::Error::new(
input_span,
"AvroSchema: derive does not work for unit structs",
)]),
}
}