Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion lang/rust/avro_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ struct NamedTypeOptions {
namespace: Option<String>,
#[darling(default)]
doc: Option<String>,
#[darling(multiple)]
alias: Vec<String>,
}

#[proc_macro_derive(AvroSchema, attributes(avro))]
Expand All @@ -64,6 +66,7 @@ fn derive_avro_schema(input: &mut DeriveInput) -> Result<TokenStream, Vec<syn::E
named_type_options
.doc
.or_else(|| extract_outer_doc(&input.attrs)),
named_type_options.alias,
s,
input.ident.span(),
)?,
Expand Down Expand Up @@ -104,6 +107,7 @@ fn derive_avro_schema(input: &mut DeriveInput) -> Result<TokenStream, Vec<syn::E
fn get_data_struct_schema_def(
full_schema_name: &str,
record_doc: Option<String>,
aliases: Vec<String>,
s: &syn::DataStruct,
error_span: Span,
) -> Result<TokenStream, Vec<syn::Error>> {
Expand Down Expand Up @@ -143,6 +147,7 @@ fn get_data_struct_schema_def(
}
}
let record_doc = preserve_optional(record_doc);
let record_aliases = preserve_vec(aliases);
Ok(quote! {
let schema_fields = vec![#(#record_field_exprs),*];
let name = apache_avro::schema::Name::new(#full_schema_name).expect(&format!("Unable to parse struct name for schema {}", #full_schema_name)[..]);
Expand All @@ -152,7 +157,7 @@ fn get_data_struct_schema_def(
.collect();
apache_avro::schema::Schema::Record {
name,
aliases: None,
aliases: #record_aliases,
doc: #record_doc,
fields: schema_fields,
lookup,
Expand Down Expand Up @@ -277,6 +282,15 @@ fn preserve_optional(op: Option<impl quote::ToTokens>) -> TokenStream {
}
}

fn preserve_vec(op: Vec<impl quote::ToTokens>) -> TokenStream {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just copied your code and adapted! :-)

let items: Vec<TokenStream> = op.iter().map(|tt| quote! {#tt.into()}).collect();
if items.is_empty() {
quote! {None}
} else {
quote! {Some(vec![#(#items),*])}
}
}

fn darling_to_syn(e: darling::Error) -> Vec<syn::Error> {
let msg = format!("{}", e);
let token_errors = e.write_errors();
Expand Down
74 changes: 74 additions & 0 deletions lang/rust/avro_derive/tests/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,4 +1063,78 @@ mod test_derive {
serde_assert(TestBasicWithU32 { a: u32::MIN });
serde_assert(TestBasicWithU32 { a: 1_u32 });
}

#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
#[avro(alias = "a", alias = "b", alias = "c")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wonder if this would work with:

#[avro(alias = "a")]
#[avro(alias = "b")]
#[avro(alias = "c")]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does - 05cf780

struct TestBasicWithAliases {
a: i32,
}

#[test]
fn test_basic_with_aliases() {
let schema = r#"
{
"type":"record",
"name":"TestBasicWithAliases",
"aliases":["a", "b", "c"],
"fields":[
{
"name":"a",
"type":"int"
}
]
}
"#;
let schema = Schema::parse_str(schema).unwrap();
if let Schema::Record { name, aliases, .. } = TestBasicWithAliases::get_schema() {
assert_eq!("TestBasicWithAliases", name.fullname(None));
assert_eq!(
Some(vec!["a".to_owned(), "b".to_owned(), "c".to_owned()]),
aliases
);
} else {
panic!("TestBasicWithAliases schema must be a record schema")
}
assert_eq!(schema, TestBasicWithAliases::get_schema());

serde_assert(TestBasicWithAliases { a: i32::MAX });
}

#[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
#[avro(alias = "d")]
#[avro(alias = "e")]
#[avro(alias = "f")]
struct TestBasicWithAliases2 {
a: i32,
}

#[test]
fn test_basic_with_aliases2() {
let schema = r#"
{
"type":"record",
"name":"TestBasicWithAliases2",
"aliases":["d", "e", "f"],
"fields":[
{
"name":"a",
"type":"int"
}
]
}
"#;
let schema = Schema::parse_str(schema).unwrap();
if let Schema::Record { name, aliases, .. } = TestBasicWithAliases2::get_schema() {
assert_eq!("TestBasicWithAliases2", name.fullname(None));
assert_eq!(
Some(vec!["d".to_owned(), "e".to_owned(), "f".to_owned()]),
aliases
);
} else {
panic!("TestBasicWithAliases2 schema must be a record schema")
}
assert_eq!(schema, TestBasicWithAliases2::get_schema());

serde_assert(TestBasicWithAliases2 { a: i32::MAX });
}
}