From bd4bf0322d773bb4e9caaccf9327957443541581 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 5 Mar 2026 09:27:00 +0000 Subject: [PATCH 1/6] feat: Use `macrotest` to test the expanded macro code --- .github/workflows/test-lang-rust-ci.yml | 7 + Cargo.lock | 24 ++ avro_derive/Cargo.toml | 1 + avro_derive/src/enums/plain.rs | 7 +- avro_derive/src/lib.rs | 380 +----------------- avro_derive/tests/expand.rs | 27 ++ ...o_3687_basic_enum_with_default.expanded.rs | 59 +++ .../avro_3687_basic_enum_with_default.rs | 28 ++ ...o_3709_record_field_attributes.expanded.rs | 178 ++++++++ .../avro_3709_record_field_attributes.rs | 32 ++ ...ro_rs_207_rename_all_attribute.expanded.rs | 180 +++++++++ .../avro_rs_207_rename_all_attribute.rs | 32 ++ ...attr_over_rename_all_attribute.expanded.rs | 133 ++++++ ...7_rename_attr_over_rename_all_attribute.rs | 26 ++ .../expanded/avro_rs_501_basic.expanded.rs | 180 +++++++++ .../tests/expanded/avro_rs_501_basic.rs | 32 ++ .../avro_rs_501_namespace.expanded.rs | 133 ++++++ .../tests/expanded/avro_rs_501_namespace.rs | 25 ++ .../avro_rs_501_reference.expanded.rs | 139 +++++++ .../tests/expanded/avro_rs_501_reference.rs | 24 ++ ...ro_rs_501_struct_with_optional.expanded.rs | 115 ++++++ .../avro_rs_501_struct_with_optional.rs | 23 ++ avro_derive/tests/expanded/mod.rs | 28 ++ ...avro_3687_basic_enum_with_default_twice.rs | 30 ++ ..._3687_basic_enum_with_default_twice.stderr | 19 + .../tests/ui/avro_rs_501_non_basic_enum.rs | 28 ++ .../ui/avro_rs_501_non_basic_enum.stderr | 5 + .../tests/ui/avro_rs_501_tuple_struct.rs | 23 ++ .../tests/ui/avro_rs_501_tuple_struct.stderr | 5 + .../tests/ui/avro_rs_501_unit_struct.rs | 23 ++ .../tests/ui/avro_rs_501_unit_struct.stderr | 5 + 31 files changed, 1577 insertions(+), 374 deletions(-) create mode 100644 avro_derive/tests/expand.rs create mode 100644 avro_derive/tests/expanded/avro_3687_basic_enum_with_default.expanded.rs create mode 100644 avro_derive/tests/expanded/avro_3687_basic_enum_with_default.rs create mode 100644 avro_derive/tests/expanded/avro_3709_record_field_attributes.expanded.rs create mode 100644 avro_derive/tests/expanded/avro_3709_record_field_attributes.rs create mode 100644 avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.expanded.rs create mode 100644 avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.rs create mode 100644 avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.expanded.rs create mode 100644 avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.rs create mode 100644 avro_derive/tests/expanded/avro_rs_501_basic.expanded.rs create mode 100644 avro_derive/tests/expanded/avro_rs_501_basic.rs create mode 100644 avro_derive/tests/expanded/avro_rs_501_namespace.expanded.rs create mode 100644 avro_derive/tests/expanded/avro_rs_501_namespace.rs create mode 100644 avro_derive/tests/expanded/avro_rs_501_reference.expanded.rs create mode 100644 avro_derive/tests/expanded/avro_rs_501_reference.rs create mode 100644 avro_derive/tests/expanded/avro_rs_501_struct_with_optional.expanded.rs create mode 100644 avro_derive/tests/expanded/avro_rs_501_struct_with_optional.rs create mode 100644 avro_derive/tests/expanded/mod.rs create mode 100644 avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.rs create mode 100644 avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.stderr create mode 100644 avro_derive/tests/ui/avro_rs_501_non_basic_enum.rs create mode 100644 avro_derive/tests/ui/avro_rs_501_non_basic_enum.stderr create mode 100644 avro_derive/tests/ui/avro_rs_501_tuple_struct.rs create mode 100644 avro_derive/tests/ui/avro_rs_501_tuple_struct.stderr create mode 100644 avro_derive/tests/ui/avro_rs_501_unit_struct.rs create mode 100644 avro_derive/tests/ui/avro_rs_501_unit_struct.stderr diff --git a/.github/workflows/test-lang-rust-ci.yml b/.github/workflows/test-lang-rust-ci.yml index 431ff7c8..92e8b676 100644 --- a/.github/workflows/test-lang-rust-ci.yml +++ b/.github/workflows/test-lang-rust-ci.yml @@ -93,6 +93,13 @@ jobs: path: ~/.cargo-${{ matrix.rust }}/cargo-rdme key: cargo-rdme- + - name: Cache cargo-expand + if: matrix.rust == 'stable' + uses: actions/cache@v5 + with: + path: ~/.cargo-${{ matrix.rust }}/cargo-expand + key: cargo-expand- + # Check if the doc cumment in avro/src/lib.rs and avro/README.md are in sync. - name: Run cargo-rdme # The result is environment independent so one test pattern is enough. diff --git a/Cargo.lock b/Cargo.lock index bc2d38af..3accbf67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,7 @@ version = "0.22.0" dependencies = [ "apache-avro", "darling", + "macrotest", "pretty_assertions", "proc-macro2", "proptest", @@ -537,6 +538,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -785,6 +792,23 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "macrotest" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd198afd908012e57564b66e43e7d4d19056cec7e6232e9e6d54a1798622f81d" +dependencies = [ + "diff", + "fastrand", + "glob", + "prettyplease", + "serde", + "serde_derive", + "serde_json", + "syn", + "toml", +] + [[package]] name = "md-5" version = "0.10.6" diff --git a/avro_derive/Cargo.toml b/avro_derive/Cargo.toml index c86f9d66..c8495da5 100644 --- a/avro_derive/Cargo.toml +++ b/avro_derive/Cargo.toml @@ -41,6 +41,7 @@ uuid = { workspace = true } [dev-dependencies] apache-avro = { default-features = false, path = "../avro", features = ["derive"] } +macrotest = { version = "1.2.1", default-features = false } pretty_assertions = { workspace = true } proptest = { default-features = false, version = "1.10.0", features = ["std"] } rustversion = "1.0.22" diff --git a/avro_derive/src/enums/plain.rs b/avro_derive/src/enums/plain.rs index 89de59ce..2832c088 100644 --- a/avro_derive/src/enums/plain.rs +++ b/avro_derive/src/enums/plain.rs @@ -46,15 +46,14 @@ pub fn schema_def( }; symbols.push(name); } - let full_schema_name = &container_attrs.name; Ok(quote! { - ::apache_avro::schema::Schema::Enum(apache_avro::schema::EnumSchema { - name: ::apache_avro::schema::Name::new(#full_schema_name).expect(&format!("Unable to parse enum name for schema {}", #full_schema_name)[..]), + ::apache_avro::schema::Schema::Enum(::apache_avro::schema::EnumSchema { + name, aliases: #enum_aliases, doc: #doc, symbols: vec![#(#symbols.to_owned()),*], default: #default, - attributes: Default::default(), + attributes: ::std::collections::BTreeMap::new(), }) }) } else { diff --git a/avro_derive/src/lib.rs b/avro_derive/src/lib.rs index 52de29b9..c5003a95 100644 --- a/avro_derive/src/lib.rs +++ b/avro_derive/src/lib.rs @@ -95,7 +95,7 @@ fn derive_avro_schema(input: DeriveInput) -> Result input.ident, &input.generics, inner, - quote! { None }, + quote! { ::std::option::Option::None }, named_type_options.default, )) } @@ -195,7 +195,7 @@ fn get_struct_schema_def( continue; } let default_value = match field_attrs.default { - FieldDefault::Disabled => quote! { None }, + FieldDefault::Disabled => quote! { ::std::option::Option::None }, FieldDefault::Trait => type_to_field_default_expr(&field.ty)?, FieldDefault::Value(default_value) => { let _: serde_json::Value = serde_json::from_str(&default_value[..]) @@ -206,7 +206,7 @@ fn get_struct_schema_def( )] })?; quote! { - Some(::serde_json::from_str(#default_value).expect(format!("Invalid JSON: {:?}", #default_value).as_str())) + ::std::option::Option::Some(::serde_json::from_str(#default_value).expect("Unreachable! This parsed at compile time!")) } } }; @@ -219,7 +219,7 @@ fn get_struct_schema_def( default: #default_value, aliases: #aliases, schema: #schema_expr, - custom_attributes: Default::default(), + custom_attributes: ::std::collections::BTreeMap::new(), }); }); } @@ -248,23 +248,23 @@ fn get_struct_schema_def( let schema_def = quote! { { - let mut schema_fields = Vec::with_capacity(#minimum_fields); + let mut schema_fields = ::std::vec::Vec::with_capacity(#minimum_fields); #(#record_field_exprs)* let schema_field_set: ::std::collections::HashSet<_> = schema_fields.iter().map(|rf| &rf.name).collect(); assert_eq!(schema_fields.len(), schema_field_set.len(), "Duplicate field names found: {schema_fields:?}"); let name = ::apache_avro::schema::Name::new(#full_schema_name).expect(&format!("Unable to parse struct name for schema {}", #full_schema_name)[..]); - let lookup: std::collections::BTreeMap = schema_fields + let lookup: ::std::collections::BTreeMap = schema_fields .iter() .enumerate() .map(|(position, field)| (field.name.to_owned(), position)) .collect(); - ::apache_avro::schema::Schema::Record(apache_avro::schema::RecordSchema { + ::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema { name, aliases: #record_aliases, doc: #record_doc, fields: schema_fields, lookup, - attributes: Default::default(), + attributes: ::std::collections::BTreeMap::new(), }) } }; @@ -455,8 +455,8 @@ fn to_compile_errors(errors: Vec) -> proc_macro2::TokenStream { fn preserve_optional(op: Option) -> TokenStream { match op { - Some(tt) => quote! {Some(#tt.into())}, - None => quote! {None}, + Some(tt) => quote! {::std::option::Option::Some(#tt.into())}, + None => quote! {::std::option::Option::None}, } } @@ -489,370 +489,10 @@ mod tests { use super::*; use pretty_assertions::assert_eq; - #[test] - fn basic_case() { - let test_struct = quote! { - struct A { - a: i32, - b: String - } - }; - - match syn::parse2::(test_struct) { - Ok(input) => { - assert!(derive_avro_schema(input).is_ok()) - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - - #[test] - fn tuple_struct_unsupported() { - let test_tuple_struct = quote! { - struct B (i32, String); - }; - - match syn::parse2::(test_tuple_struct) { - Ok(input) => { - assert!(derive_avro_schema(input).is_err()) - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - - #[test] - fn unit_struct_unsupported() { - let test_tuple_struct = quote! { - struct AbsoluteUnit; - }; - - match syn::parse2::(test_tuple_struct) { - Ok(input) => { - assert!(derive_avro_schema(input).is_err()) - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - - #[test] - fn struct_with_optional() { - let struct_with_optional = quote! { - struct Test4 { - a : Option - } - }; - match syn::parse2::(struct_with_optional) { - Ok(input) => { - assert!(derive_avro_schema(input).is_ok()) - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - - #[test] - fn test_basic_enum() { - let basic_enum = quote! { - enum Basic { - A, - B, - C, - D - } - }; - match syn::parse2::(basic_enum) { - Ok(input) => { - assert!(derive_avro_schema(input).is_ok()) - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - - #[test] - fn avro_3687_basic_enum_with_default() { - let basic_enum = quote! { - enum Basic { - #[default] - A, - B, - C, - D - } - }; - match syn::parse2::(basic_enum) { - Ok(input) => { - let derived = derive_avro_schema(input); - assert!(derived.is_ok()); - assert_eq!(derived.unwrap().to_string(), quote! { - #[automatically_derived] - impl ::apache_avro::AvroSchemaComponent for Basic { - fn get_schema_in_ctxt( - named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, - enclosing_namespace: ::apache_avro::schema::NamespaceRef - ) -> ::apache_avro::schema::Schema { - let name = ::apache_avro::schema::Name::new_with_enclosing_namespace("Basic", enclosing_namespace) - .expect(concat!("Unable to parse schema name ", "Basic")); - if named_schemas.contains(&name) { - ::apache_avro::schema::Schema::Ref { name } - } else { - let enclosing_namespace = name.namespace(); - named_schemas.insert(name.clone()); - ::apache_avro::schema::Schema::Enum(apache_avro::schema::EnumSchema { - name: ::apache_avro::schema::Name::new("Basic").expect( - &format!("Unable to parse enum name for schema {}", "Basic")[..] - ), - aliases: ::std::option::Option::None, - doc: None, - symbols: vec![ - "A".to_owned(), - "B".to_owned(), - "C".to_owned(), - "D".to_owned() - ], - default: Some("A".into()), - attributes: Default::default(), - }) - } - } - - fn get_record_fields_in_ctxt( - named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, - enclosing_namespace: ::apache_avro::schema::NamespaceRef - ) -> ::std::option::Option <::std::vec::Vec<::apache_avro::schema::RecordField>> { - None - } - - fn field_default () -> ::std::option::Option<::serde_json::Value> { - ::std::option::Option::None - } - } - }.to_string()); - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - - #[test] - fn avro_3687_basic_enum_with_default_twice() { - let non_basic_enum = quote! { - enum Basic { - #[default] - A, - B, - #[default] - C, - D - } - }; - match syn::parse2::(non_basic_enum) { - Ok(input) => match derive_avro_schema(input) { - Ok(_) => { - panic!("Should not be able to derive schema for enum with multiple defaults") - } - Err(errors) => { - assert_eq!(errors.len(), 1); - assert_eq!( - errors[0].to_string(), - r#"Multiple defaults defined: ["A", "C"]"# - ); - } - }, - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - - #[test] - fn test_non_basic_enum() { - let non_basic_enum = quote! { - enum Basic { - A(i32), - B, - C, - D - } - }; - match syn::parse2::(non_basic_enum) { - Ok(input) => { - assert!(derive_avro_schema(input).is_err()) - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - - #[test] - fn test_namespace() { - let test_struct = quote! { - #[avro(namespace = "namespace.testing")] - struct A { - a: i32, - b: String - } - }; - - match syn::parse2::(test_struct) { - Ok(input) => { - let schema_token_stream = derive_avro_schema(input); - assert!(&schema_token_stream.is_ok()); - assert!( - schema_token_stream - .unwrap() - .to_string() - .contains("namespace.testing") - ) - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - - #[test] - fn test_reference() { - let test_reference_struct = quote! { - struct A<'a> { - a: &'a Vec, - b: &'static str - } - }; - - match syn::parse2::(test_reference_struct) { - Ok(input) => { - assert!(derive_avro_schema(input).is_ok()) - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - #[test] fn test_trait_cast() { assert_eq!(type_to_schema_expr(&syn::parse2::(quote!{i32}).unwrap()).unwrap().to_string(), quote!{::get_schema_in_ctxt(named_schemas, enclosing_namespace)}.to_string()); assert_eq!(type_to_schema_expr(&syn::parse2::(quote!{Vec}).unwrap()).unwrap().to_string(), quote!{ as :: apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace)}.to_string()); assert_eq!(type_to_schema_expr(&syn::parse2::(quote!{AnyType}).unwrap()).unwrap().to_string(), quote!{::get_schema_in_ctxt(named_schemas, enclosing_namespace)}.to_string()); } - - #[test] - fn test_avro_3709_record_field_attributes() { - let test_struct = quote! { - struct A { - #[serde(alias = "a1", alias = "a2", rename = "a3")] - #[avro(doc = "a doc", default = "123")] - a: i32 - } - }; - - match syn::parse2::(test_struct) { - Ok(input) => { - let schema_res = derive_avro_schema(input); - let expected_token_stream = r#"# [automatically_derived] impl :: apache_avro :: AvroSchemaComponent for A { fn get_schema_in_ctxt (named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: NamespaceRef) -> :: apache_avro :: schema :: Schema { let name = :: apache_avro :: schema :: Name :: new_with_enclosing_namespace ("A" , enclosing_namespace) . expect (concat ! ("Unable to parse schema name " , "A")) ; if named_schemas . contains (& name) { :: apache_avro :: schema :: Schema :: Ref { name } } else { let enclosing_namespace = name . namespace () ; named_schemas . insert (name . clone ()) ; { let mut schema_fields = Vec :: with_capacity (1usize) ; schema_fields . push (:: apache_avro :: schema :: RecordField { name : "a3" . to_string () , doc : Some ("a doc" . into ()) , default : Some (:: serde_json :: from_str ("123") . expect (format ! ("Invalid JSON: {:?}" , "123") . as_str ())) , aliases : vec ! ["a1" . try_into () . expect ("Alias is invalid") , "a2" . try_into () . expect ("Alias is invalid")] , schema : < i32 as :: apache_avro :: AvroSchemaComponent > :: get_schema_in_ctxt (named_schemas , enclosing_namespace) , custom_attributes : Default :: default () , }) ; let schema_field_set : :: std :: collections :: HashSet < _ > = schema_fields . iter () . map (| rf | & rf . name) . collect () ; assert_eq ! (schema_fields . len () , schema_field_set . len () , "Duplicate field names found: {schema_fields:?}") ; let name = :: apache_avro :: schema :: Name :: new ("A") . expect (& format ! ("Unable to parse struct name for schema {}" , "A") [..]) ; let lookup : std :: collections :: BTreeMap < String , usize > = schema_fields . iter () . enumerate () . map (| (position , field) | (field . name . to_owned () , position)) . collect () ; :: apache_avro :: schema :: Schema :: Record (apache_avro :: schema :: RecordSchema { name , aliases : :: std :: option :: Option :: None , doc : None , fields : schema_fields , lookup , attributes : Default :: default () , }) } } } fn get_record_fields_in_ctxt (named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: NamespaceRef) -> :: std :: option :: Option < :: std :: vec :: Vec < :: apache_avro :: schema :: RecordField >> { let mut schema_fields = Vec :: with_capacity (1usize) ; schema_fields . push (:: apache_avro :: schema :: RecordField { name : "a3" . to_string () , doc : Some ("a doc" . into ()) , default : Some (:: serde_json :: from_str ("123") . expect (format ! ("Invalid JSON: {:?}" , "123") . as_str ())) , aliases : vec ! ["a1" . try_into () . expect ("Alias is invalid") , "a2" . try_into () . expect ("Alias is invalid")] , schema : < i32 as :: apache_avro :: AvroSchemaComponent > :: get_schema_in_ctxt (named_schemas , enclosing_namespace) , custom_attributes : Default :: default () , }) ; Some (schema_fields) } fn field_default () -> :: std :: option :: Option < :: serde_json :: Value > { :: std :: option :: Option :: None } }"#; - let schema_token_stream = schema_res.unwrap().to_string(); - assert_eq!(schema_token_stream, expected_token_stream); - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - - let test_enum = quote! { - enum A { - #[serde(rename = "A3")] - Item1, - } - }; - - match syn::parse2::(test_enum) { - Ok(input) => { - let schema_res = derive_avro_schema(input); - let expected_token_stream = r#"# [automatically_derived] impl :: apache_avro :: AvroSchemaComponent for A { fn get_schema_in_ctxt (named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: NamespaceRef) -> :: apache_avro :: schema :: Schema { let name = :: apache_avro :: schema :: Name :: new_with_enclosing_namespace ("A" , enclosing_namespace) . expect (concat ! ("Unable to parse schema name " , "A")) ; if named_schemas . contains (& name) { :: apache_avro :: schema :: Schema :: Ref { name } } else { let enclosing_namespace = name . namespace () ; named_schemas . insert (name . clone ()) ; :: apache_avro :: schema :: Schema :: Enum (apache_avro :: schema :: EnumSchema { name : :: apache_avro :: schema :: Name :: new ("A") . expect (& format ! ("Unable to parse enum name for schema {}" , "A") [..]) , aliases : :: std :: option :: Option :: None , doc : None , symbols : vec ! ["A3" . to_owned ()] , default : None , attributes : Default :: default () , }) } } fn get_record_fields_in_ctxt (named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: NamespaceRef) -> :: std :: option :: Option < :: std :: vec :: Vec < :: apache_avro :: schema :: RecordField >> { None } fn field_default () -> :: std :: option :: Option < :: serde_json :: Value > { :: std :: option :: Option :: None } }"#; - let schema_token_stream = schema_res.unwrap().to_string(); - assert_eq!(schema_token_stream, expected_token_stream); - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - - #[test] - fn test_avro_rs_207_rename_all_attribute() { - let test_struct = quote! { - #[serde(rename_all="SCREAMING_SNAKE_CASE")] - struct A { - item: i32, - double_item: i32 - } - }; - - match syn::parse2::(test_struct) { - Ok(input) => { - let schema_res = derive_avro_schema(input); - let expected_token_stream = r#"# [automatically_derived] impl :: apache_avro :: AvroSchemaComponent for A { fn get_schema_in_ctxt (named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: NamespaceRef) -> :: apache_avro :: schema :: Schema { let name = :: apache_avro :: schema :: Name :: new_with_enclosing_namespace ("A" , enclosing_namespace) . expect (concat ! ("Unable to parse schema name " , "A")) ; if named_schemas . contains (& name) { :: apache_avro :: schema :: Schema :: Ref { name } } else { let enclosing_namespace = name . namespace () ; named_schemas . insert (name . clone ()) ; { let mut schema_fields = Vec :: with_capacity (2usize) ; schema_fields . push (:: apache_avro :: schema :: RecordField { name : "ITEM" . to_string () , doc : None , default : < i32 as :: apache_avro :: AvroSchemaComponent > :: field_default () , aliases : :: std :: vec :: Vec :: new () , schema : < i32 as :: apache_avro :: AvroSchemaComponent > :: get_schema_in_ctxt (named_schemas , enclosing_namespace) , custom_attributes : Default :: default () , }) ; schema_fields . push (:: apache_avro :: schema :: RecordField { name : "DOUBLE_ITEM" . to_string () , doc : None , default : < i32 as :: apache_avro :: AvroSchemaComponent > :: field_default () , aliases : :: std :: vec :: Vec :: new () , schema : < i32 as :: apache_avro :: AvroSchemaComponent > :: get_schema_in_ctxt (named_schemas , enclosing_namespace) , custom_attributes : Default :: default () , }) ; let schema_field_set : :: std :: collections :: HashSet < _ > = schema_fields . iter () . map (| rf | & rf . name) . collect () ; assert_eq ! (schema_fields . len () , schema_field_set . len () , "Duplicate field names found: {schema_fields:?}") ; let name = :: apache_avro :: schema :: Name :: new ("A") . expect (& format ! ("Unable to parse struct name for schema {}" , "A") [..]) ; let lookup : std :: collections :: BTreeMap < String , usize > = schema_fields . iter () . enumerate () . map (| (position , field) | (field . name . to_owned () , position)) . collect () ; :: apache_avro :: schema :: Schema :: Record (apache_avro :: schema :: RecordSchema { name , aliases : :: std :: option :: Option :: None , doc : None , fields : schema_fields , lookup , attributes : Default :: default () , }) } } } fn get_record_fields_in_ctxt (named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: NamespaceRef) -> :: std :: option :: Option < :: std :: vec :: Vec < :: apache_avro :: schema :: RecordField >> { let mut schema_fields = Vec :: with_capacity (2usize) ; schema_fields . push (:: apache_avro :: schema :: RecordField { name : "ITEM" . to_string () , doc : None , default : < i32 as :: apache_avro :: AvroSchemaComponent > :: field_default () , aliases : :: std :: vec :: Vec :: new () , schema : < i32 as :: apache_avro :: AvroSchemaComponent > :: get_schema_in_ctxt (named_schemas , enclosing_namespace) , custom_attributes : Default :: default () , }) ; schema_fields . push (:: apache_avro :: schema :: RecordField { name : "DOUBLE_ITEM" . to_string () , doc : None , default : < i32 as :: apache_avro :: AvroSchemaComponent > :: field_default () , aliases : :: std :: vec :: Vec :: new () , schema : < i32 as :: apache_avro :: AvroSchemaComponent > :: get_schema_in_ctxt (named_schemas , enclosing_namespace) , custom_attributes : Default :: default () , }) ; Some (schema_fields) } fn field_default () -> :: std :: option :: Option < :: serde_json :: Value > { :: std :: option :: Option :: None } }"#; - let schema_token_stream = schema_res.unwrap().to_string(); - assert_eq!(schema_token_stream, expected_token_stream); - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - - let test_enum = quote! { - #[serde(rename_all="SCREAMING_SNAKE_CASE")] - enum B { - Item, - DoubleItem, - } - }; - - match syn::parse2::(test_enum) { - Ok(input) => { - let schema_res = derive_avro_schema(input); - let expected_token_stream = r#"# [automatically_derived] impl :: apache_avro :: AvroSchemaComponent for B { fn get_schema_in_ctxt (named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: NamespaceRef) -> :: apache_avro :: schema :: Schema { let name = :: apache_avro :: schema :: Name :: new_with_enclosing_namespace ("B" , enclosing_namespace) . expect (concat ! ("Unable to parse schema name " , "B")) ; if named_schemas . contains (& name) { :: apache_avro :: schema :: Schema :: Ref { name } } else { let enclosing_namespace = name . namespace () ; named_schemas . insert (name . clone ()) ; :: apache_avro :: schema :: Schema :: Enum (apache_avro :: schema :: EnumSchema { name : :: apache_avro :: schema :: Name :: new ("B") . expect (& format ! ("Unable to parse enum name for schema {}" , "B") [..]) , aliases : :: std :: option :: Option :: None , doc : None , symbols : vec ! ["ITEM" . to_owned () , "DOUBLE_ITEM" . to_owned ()] , default : None , attributes : Default :: default () , }) } } fn get_record_fields_in_ctxt (named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: NamespaceRef) -> :: std :: option :: Option < :: std :: vec :: Vec < :: apache_avro :: schema :: RecordField >> { None } fn field_default () -> :: std :: option :: Option < :: serde_json :: Value > { :: std :: option :: Option :: None } }"#; - let schema_token_stream = schema_res.unwrap().to_string(); - assert_eq!(schema_token_stream, expected_token_stream); - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } - - #[test] - fn test_avro_rs_207_rename_attr_has_priority_over_rename_all_attribute() { - let test_struct = quote! { - #[serde(rename_all="SCREAMING_SNAKE_CASE")] - struct A { - item: i32, - #[serde(rename="DoubleItem")] - double_item: i32 - } - }; - - match syn::parse2::(test_struct) { - Ok(input) => { - let schema_res = derive_avro_schema(input); - let expected_token_stream = r#"# [automatically_derived] impl :: apache_avro :: AvroSchemaComponent for A { fn get_schema_in_ctxt (named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: NamespaceRef) -> :: apache_avro :: schema :: Schema { let name = :: apache_avro :: schema :: Name :: new_with_enclosing_namespace ("A" , enclosing_namespace) . expect (concat ! ("Unable to parse schema name " , "A")) ; if named_schemas . contains (& name) { :: apache_avro :: schema :: Schema :: Ref { name } } else { let enclosing_namespace = name . namespace () ; named_schemas . insert (name . clone ()) ; { let mut schema_fields = Vec :: with_capacity (2usize) ; schema_fields . push (:: apache_avro :: schema :: RecordField { name : "ITEM" . to_string () , doc : None , default : < i32 as :: apache_avro :: AvroSchemaComponent > :: field_default () , aliases : :: std :: vec :: Vec :: new () , schema : < i32 as :: apache_avro :: AvroSchemaComponent > :: get_schema_in_ctxt (named_schemas , enclosing_namespace) , custom_attributes : Default :: default () , }) ; schema_fields . push (:: apache_avro :: schema :: RecordField { name : "DoubleItem" . to_string () , doc : None , default : < i32 as :: apache_avro :: AvroSchemaComponent > :: field_default () , aliases : :: std :: vec :: Vec :: new () , schema : < i32 as :: apache_avro :: AvroSchemaComponent > :: get_schema_in_ctxt (named_schemas , enclosing_namespace) , custom_attributes : Default :: default () , }) ; let schema_field_set : :: std :: collections :: HashSet < _ > = schema_fields . iter () . map (| rf | & rf . name) . collect () ; assert_eq ! (schema_fields . len () , schema_field_set . len () , "Duplicate field names found: {schema_fields:?}") ; let name = :: apache_avro :: schema :: Name :: new ("A") . expect (& format ! ("Unable to parse struct name for schema {}" , "A") [..]) ; let lookup : std :: collections :: BTreeMap < String , usize > = schema_fields . iter () . enumerate () . map (| (position , field) | (field . name . to_owned () , position)) . collect () ; :: apache_avro :: schema :: Schema :: Record (apache_avro :: schema :: RecordSchema { name , aliases : :: std :: option :: Option :: None , doc : None , fields : schema_fields , lookup , attributes : Default :: default () , }) } } } fn get_record_fields_in_ctxt (named_schemas : & mut :: std :: collections :: HashSet < :: apache_avro :: schema :: Name > , enclosing_namespace : :: apache_avro :: schema :: NamespaceRef) -> :: std :: option :: Option < :: std :: vec :: Vec < :: apache_avro :: schema :: RecordField >> { let mut schema_fields = Vec :: with_capacity (2usize) ; schema_fields . push (:: apache_avro :: schema :: RecordField { name : "ITEM" . to_string () , doc : None , default : < i32 as :: apache_avro :: AvroSchemaComponent > :: field_default () , aliases : :: std :: vec :: Vec :: new () , schema : < i32 as :: apache_avro :: AvroSchemaComponent > :: get_schema_in_ctxt (named_schemas , enclosing_namespace) , custom_attributes : Default :: default () , }) ; schema_fields . push (:: apache_avro :: schema :: RecordField { name : "DoubleItem" . to_string () , doc : None , default : < i32 as :: apache_avro :: AvroSchemaComponent > :: field_default () , aliases : :: std :: vec :: Vec :: new () , schema : < i32 as :: apache_avro :: AvroSchemaComponent > :: get_schema_in_ctxt (named_schemas , enclosing_namespace) , custom_attributes : Default :: default () , }) ; Some (schema_fields) } fn field_default () -> :: std :: option :: Option < :: serde_json :: Value > { :: std :: option :: Option :: None } }"#; - let schema_token_stream = schema_res.unwrap().to_string(); - assert_eq!(schema_token_stream, expected_token_stream); - } - Err(error) => panic!( - "Failed to parse as derive input when it should be able to. Error: {error:?}" - ), - }; - } } diff --git a/avro_derive/tests/expand.rs b/avro_derive/tests/expand.rs new file mode 100644 index 00000000..fd6071a0 --- /dev/null +++ b/avro_derive/tests/expand.rs @@ -0,0 +1,27 @@ +// 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. + +// By including the (unexpended) modules, we can also test that the +// generated code is valid. +mod expanded; + +/// These tests only run on nightly as the output can change per compiler version. +#[rustversion::attr(not(nightly), ignore)] +#[test] +pub fn expand() { + macrotest::expand("tests/expanded/avro_*.rs"); +} diff --git a/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.expanded.rs b/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.expanded.rs new file mode 100644 index 00000000..62fcff7f --- /dev/null +++ b/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.expanded.rs @@ -0,0 +1,59 @@ +use apache_avro::AvroSchema; +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +enum Basic { + #[default] + A, + B, + C, + D, +} +#[automatically_derived] +impl ::apache_avro::AvroSchemaComponent for Basic { + fn get_schema_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::apache_avro::schema::Schema { + let name = ::apache_avro::schema::Name::new_with_enclosing_namespace( + "Basic", + enclosing_namespace, + ) + .expect("Unable to parse schema name Basic"); + if named_schemas.contains(&name) { + ::apache_avro::schema::Schema::Ref { + name, + } + } else { + let enclosing_namespace = name.namespace(); + named_schemas.insert(name.clone()); + ::apache_avro::schema::Schema::Enum(::apache_avro::schema::EnumSchema { + name, + aliases: ::std::option::Option::None, + doc: ::std::option::Option::None, + symbols: ::alloc::boxed::box_assume_init_into_vec_unsafe( + ::alloc::intrinsics::write_box_via_move( + ::alloc::boxed::Box::new_uninit(), + ["A".to_owned(), "B".to_owned(), "C".to_owned(), "D".to_owned()], + ), + ), + default: ::std::option::Option::Some("A".into()), + attributes: ::std::collections::BTreeMap::new(), + }) + } + } + fn get_record_fields_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> { + ::std::option::Option::None + } + fn field_default() -> ::std::option::Option<::serde_json::Value> { + ::std::option::Option::None + } +} +#[automatically_derived] +impl ::core::default::Default for Basic { + #[inline] + fn default() -> Basic { + Self::A + } +} diff --git a/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.rs b/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.rs new file mode 100644 index 00000000..21f26175 --- /dev/null +++ b/avro_derive/tests/expanded/avro_3687_basic_enum_with_default.rs @@ -0,0 +1,28 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema, Default)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +enum Basic { + #[default] + A, + B, + C, + D, +} diff --git a/avro_derive/tests/expanded/avro_3709_record_field_attributes.expanded.rs b/avro_derive/tests/expanded/avro_3709_record_field_attributes.expanded.rs new file mode 100644 index 00000000..e07401bd --- /dev/null +++ b/avro_derive/tests/expanded/avro_3709_record_field_attributes.expanded.rs @@ -0,0 +1,178 @@ +use apache_avro::AvroSchema; +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +struct A { + #[serde(alias = "a1", alias = "a2", rename = "a3")] + #[avro(doc = "a doc", default = "123")] + a: i32, +} +#[automatically_derived] +impl ::apache_avro::AvroSchemaComponent for A { + fn get_schema_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::apache_avro::schema::Schema { + let name = ::apache_avro::schema::Name::new_with_enclosing_namespace( + "A", + enclosing_namespace, + ) + .expect("Unable to parse schema name A"); + if named_schemas.contains(&name) { + ::apache_avro::schema::Schema::Ref { + name, + } + } else { + let enclosing_namespace = name.namespace(); + named_schemas.insert(name.clone()); + { + let mut schema_fields = ::std::vec::Vec::with_capacity(1usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "a3".to_string(), + doc: ::std::option::Option::Some("a doc".into()), + default: ::std::option::Option::Some( + ::serde_json::from_str("123") + .expect("Unreachable! This parsed at compile time!"), + ), + aliases: ::alloc::boxed::box_assume_init_into_vec_unsafe( + ::alloc::intrinsics::write_box_via_move( + ::alloc::boxed::Box::new_uninit(), + [ + "a1".try_into().expect("Alias is invalid"), + "a2".try_into().expect("Alias is invalid"), + ], + ), + ), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + let schema_field_set: ::std::collections::HashSet<_> = schema_fields + .iter() + .map(|rf| &rf.name) + .collect(); + match (&schema_fields.len(), &schema_field_set.len()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( + kind, + &*left_val, + &*right_val, + ::core::option::Option::Some( + format_args!( + "Duplicate field names found: {0:?}", schema_fields, + ), + ), + ); + } + } + }; + let name = ::apache_avro::schema::Name::new("A") + .expect( + &::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "Unable to parse struct name for schema {0}", "A", + ), + ) + })[..], + ); + let lookup: ::std::collections::BTreeMap = schema_fields + .iter() + .enumerate() + .map(|(position, field)| (field.name.to_owned(), position)) + .collect(); + ::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema { + name, + aliases: ::std::option::Option::None, + doc: ::std::option::Option::None, + fields: schema_fields, + lookup, + attributes: ::std::collections::BTreeMap::new(), + }) + } + } + } + fn get_record_fields_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> { + let mut schema_fields = Vec::with_capacity(1usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "a3".to_string(), + doc: ::std::option::Option::Some("a doc".into()), + default: ::std::option::Option::Some( + ::serde_json::from_str("123") + .expect("Unreachable! This parsed at compile time!"), + ), + aliases: ::alloc::boxed::box_assume_init_into_vec_unsafe( + ::alloc::intrinsics::write_box_via_move( + ::alloc::boxed::Box::new_uninit(), + [ + "a1".try_into().expect("Alias is invalid"), + "a2".try_into().expect("Alias is invalid"), + ], + ), + ), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + Some(schema_fields) + } + fn field_default() -> ::std::option::Option<::serde_json::Value> { + ::std::option::Option::None + } +} +enum B { + #[serde(rename = "A3")] + Item1, +} +#[automatically_derived] +impl ::apache_avro::AvroSchemaComponent for B { + fn get_schema_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::apache_avro::schema::Schema { + let name = ::apache_avro::schema::Name::new_with_enclosing_namespace( + "B", + enclosing_namespace, + ) + .expect("Unable to parse schema name B"); + if named_schemas.contains(&name) { + ::apache_avro::schema::Schema::Ref { + name, + } + } else { + let enclosing_namespace = name.namespace(); + named_schemas.insert(name.clone()); + ::apache_avro::schema::Schema::Enum(::apache_avro::schema::EnumSchema { + name, + aliases: ::std::option::Option::None, + doc: ::std::option::Option::None, + symbols: ::alloc::boxed::box_assume_init_into_vec_unsafe( + ::alloc::intrinsics::write_box_via_move( + ::alloc::boxed::Box::new_uninit(), + ["A3".to_owned()], + ), + ), + default: ::std::option::Option::None, + attributes: ::std::collections::BTreeMap::new(), + }) + } + } + fn get_record_fields_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> { + ::std::option::Option::None + } + fn field_default() -> ::std::option::Option<::serde_json::Value> { + ::std::option::Option::None + } +} diff --git a/avro_derive/tests/expanded/avro_3709_record_field_attributes.rs b/avro_derive/tests/expanded/avro_3709_record_field_attributes.rs new file mode 100644 index 00000000..240d71ad --- /dev/null +++ b/avro_derive/tests/expanded/avro_3709_record_field_attributes.rs @@ -0,0 +1,32 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +struct A { + #[serde(alias = "a1", alias = "a2", rename = "a3")] + #[avro(doc = "a doc", default = "123")] + a: i32, +} + +#[derive(AvroSchema)] +enum B { + #[serde(rename = "A3")] + Item1, +} diff --git a/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.expanded.rs b/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.expanded.rs new file mode 100644 index 00000000..78617fdb --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.expanded.rs @@ -0,0 +1,180 @@ +use apache_avro::AvroSchema; +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +struct A { + item: i32, + double_item: i32, +} +#[automatically_derived] +impl ::apache_avro::AvroSchemaComponent for A { + fn get_schema_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::apache_avro::schema::Schema { + let name = ::apache_avro::schema::Name::new_with_enclosing_namespace( + "A", + enclosing_namespace, + ) + .expect("Unable to parse schema name A"); + if named_schemas.contains(&name) { + ::apache_avro::schema::Schema::Ref { + name, + } + } else { + let enclosing_namespace = name.namespace(); + named_schemas.insert(name.clone()); + { + let mut schema_fields = ::std::vec::Vec::with_capacity(2usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "ITEM".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "DOUBLE_ITEM".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + let schema_field_set: ::std::collections::HashSet<_> = schema_fields + .iter() + .map(|rf| &rf.name) + .collect(); + match (&schema_fields.len(), &schema_field_set.len()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( + kind, + &*left_val, + &*right_val, + ::core::option::Option::Some( + format_args!( + "Duplicate field names found: {0:?}", schema_fields, + ), + ), + ); + } + } + }; + let name = ::apache_avro::schema::Name::new("A") + .expect( + &::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "Unable to parse struct name for schema {0}", "A", + ), + ) + })[..], + ); + let lookup: ::std::collections::BTreeMap = schema_fields + .iter() + .enumerate() + .map(|(position, field)| (field.name.to_owned(), position)) + .collect(); + ::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema { + name, + aliases: ::std::option::Option::None, + doc: ::std::option::Option::None, + fields: schema_fields, + lookup, + attributes: ::std::collections::BTreeMap::new(), + }) + } + } + } + fn get_record_fields_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> { + let mut schema_fields = Vec::with_capacity(2usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "ITEM".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "DOUBLE_ITEM".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + Some(schema_fields) + } + fn field_default() -> ::std::option::Option<::serde_json::Value> { + ::std::option::Option::None + } +} +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +enum B { + Item, + DoubleItem, +} +#[automatically_derived] +impl ::apache_avro::AvroSchemaComponent for B { + fn get_schema_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::apache_avro::schema::Schema { + let name = ::apache_avro::schema::Name::new_with_enclosing_namespace( + "B", + enclosing_namespace, + ) + .expect("Unable to parse schema name B"); + if named_schemas.contains(&name) { + ::apache_avro::schema::Schema::Ref { + name, + } + } else { + let enclosing_namespace = name.namespace(); + named_schemas.insert(name.clone()); + ::apache_avro::schema::Schema::Enum(::apache_avro::schema::EnumSchema { + name, + aliases: ::std::option::Option::None, + doc: ::std::option::Option::None, + symbols: ::alloc::boxed::box_assume_init_into_vec_unsafe( + ::alloc::intrinsics::write_box_via_move( + ::alloc::boxed::Box::new_uninit(), + ["ITEM".to_owned(), "DOUBLE_ITEM".to_owned()], + ), + ), + default: ::std::option::Option::None, + attributes: ::std::collections::BTreeMap::new(), + }) + } + } + fn get_record_fields_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> { + ::std::option::Option::None + } + fn field_default() -> ::std::option::Option<::serde_json::Value> { + ::std::option::Option::None + } +} diff --git a/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.rs b/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.rs new file mode 100644 index 00000000..6657f603 --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_207_rename_all_attribute.rs @@ -0,0 +1,32 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +struct A { + item: i32, + double_item: i32, +} + +#[derive(AvroSchema)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +enum B { + Item, + DoubleItem, +} diff --git a/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.expanded.rs b/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.expanded.rs new file mode 100644 index 00000000..6568d955 --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.expanded.rs @@ -0,0 +1,133 @@ +use apache_avro::AvroSchema; +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +struct A { + item: i32, + #[serde(rename = "DoubleItem")] + double_item: i32, +} +#[automatically_derived] +impl ::apache_avro::AvroSchemaComponent for A { + fn get_schema_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::apache_avro::schema::Schema { + let name = ::apache_avro::schema::Name::new_with_enclosing_namespace( + "A", + enclosing_namespace, + ) + .expect("Unable to parse schema name A"); + if named_schemas.contains(&name) { + ::apache_avro::schema::Schema::Ref { + name, + } + } else { + let enclosing_namespace = name.namespace(); + named_schemas.insert(name.clone()); + { + let mut schema_fields = ::std::vec::Vec::with_capacity(2usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "ITEM".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "DoubleItem".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + let schema_field_set: ::std::collections::HashSet<_> = schema_fields + .iter() + .map(|rf| &rf.name) + .collect(); + match (&schema_fields.len(), &schema_field_set.len()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( + kind, + &*left_val, + &*right_val, + ::core::option::Option::Some( + format_args!( + "Duplicate field names found: {0:?}", schema_fields, + ), + ), + ); + } + } + }; + let name = ::apache_avro::schema::Name::new("A") + .expect( + &::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "Unable to parse struct name for schema {0}", "A", + ), + ) + })[..], + ); + let lookup: ::std::collections::BTreeMap = schema_fields + .iter() + .enumerate() + .map(|(position, field)| (field.name.to_owned(), position)) + .collect(); + ::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema { + name, + aliases: ::std::option::Option::None, + doc: ::std::option::Option::None, + fields: schema_fields, + lookup, + attributes: ::std::collections::BTreeMap::new(), + }) + } + } + } + fn get_record_fields_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> { + let mut schema_fields = Vec::with_capacity(2usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "ITEM".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "DoubleItem".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + Some(schema_fields) + } + fn field_default() -> ::std::option::Option<::serde_json::Value> { + ::std::option::Option::None + } +} diff --git a/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.rs b/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.rs new file mode 100644 index 00000000..283b2230 --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_207_rename_attr_over_rename_all_attribute.rs @@ -0,0 +1,26 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +struct A { + item: i32, + #[serde(rename = "DoubleItem")] + double_item: i32, +} diff --git a/avro_derive/tests/expanded/avro_rs_501_basic.expanded.rs b/avro_derive/tests/expanded/avro_rs_501_basic.expanded.rs new file mode 100644 index 00000000..52b6a842 --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_501_basic.expanded.rs @@ -0,0 +1,180 @@ +use apache_avro::AvroSchema; +struct A { + a: i32, + b: String, +} +#[automatically_derived] +impl ::apache_avro::AvroSchemaComponent for A { + fn get_schema_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::apache_avro::schema::Schema { + let name = ::apache_avro::schema::Name::new_with_enclosing_namespace( + "A", + enclosing_namespace, + ) + .expect("Unable to parse schema name A"); + if named_schemas.contains(&name) { + ::apache_avro::schema::Schema::Ref { + name, + } + } else { + let enclosing_namespace = name.namespace(); + named_schemas.insert(name.clone()); + { + let mut schema_fields = ::std::vec::Vec::with_capacity(2usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "a".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "b".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + let schema_field_set: ::std::collections::HashSet<_> = schema_fields + .iter() + .map(|rf| &rf.name) + .collect(); + match (&schema_fields.len(), &schema_field_set.len()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( + kind, + &*left_val, + &*right_val, + ::core::option::Option::Some( + format_args!( + "Duplicate field names found: {0:?}", schema_fields, + ), + ), + ); + } + } + }; + let name = ::apache_avro::schema::Name::new("A") + .expect( + &::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "Unable to parse struct name for schema {0}", "A", + ), + ) + })[..], + ); + let lookup: ::std::collections::BTreeMap = schema_fields + .iter() + .enumerate() + .map(|(position, field)| (field.name.to_owned(), position)) + .collect(); + ::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema { + name, + aliases: ::std::option::Option::None, + doc: ::std::option::Option::None, + fields: schema_fields, + lookup, + attributes: ::std::collections::BTreeMap::new(), + }) + } + } + } + fn get_record_fields_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> { + let mut schema_fields = Vec::with_capacity(2usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "a".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "b".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + Some(schema_fields) + } + fn field_default() -> ::std::option::Option<::serde_json::Value> { + ::std::option::Option::None + } +} +enum Basic { + A, + B, + C, + D, +} +#[automatically_derived] +impl ::apache_avro::AvroSchemaComponent for Basic { + fn get_schema_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::apache_avro::schema::Schema { + let name = ::apache_avro::schema::Name::new_with_enclosing_namespace( + "Basic", + enclosing_namespace, + ) + .expect("Unable to parse schema name Basic"); + if named_schemas.contains(&name) { + ::apache_avro::schema::Schema::Ref { + name, + } + } else { + let enclosing_namespace = name.namespace(); + named_schemas.insert(name.clone()); + ::apache_avro::schema::Schema::Enum(::apache_avro::schema::EnumSchema { + name, + aliases: ::std::option::Option::None, + doc: ::std::option::Option::None, + symbols: ::alloc::boxed::box_assume_init_into_vec_unsafe( + ::alloc::intrinsics::write_box_via_move( + ::alloc::boxed::Box::new_uninit(), + ["A".to_owned(), "B".to_owned(), "C".to_owned(), "D".to_owned()], + ), + ), + default: ::std::option::Option::None, + attributes: ::std::collections::BTreeMap::new(), + }) + } + } + fn get_record_fields_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> { + ::std::option::Option::None + } + fn field_default() -> ::std::option::Option<::serde_json::Value> { + ::std::option::Option::None + } +} diff --git a/avro_derive/tests/expanded/avro_rs_501_basic.rs b/avro_derive/tests/expanded/avro_rs_501_basic.rs new file mode 100644 index 00000000..ea90024e --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_501_basic.rs @@ -0,0 +1,32 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema)] +struct A { + a: i32, + b: String, +} + +#[derive(AvroSchema)] +enum Basic { + A, + B, + C, + D, +} diff --git a/avro_derive/tests/expanded/avro_rs_501_namespace.expanded.rs b/avro_derive/tests/expanded/avro_rs_501_namespace.expanded.rs new file mode 100644 index 00000000..fd2c5029 --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_501_namespace.expanded.rs @@ -0,0 +1,133 @@ +use apache_avro::AvroSchema; +#[avro(namespace = "namespace.testing")] +struct A { + a: i32, + b: String, +} +#[automatically_derived] +impl ::apache_avro::AvroSchemaComponent for A { + fn get_schema_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::apache_avro::schema::Schema { + let name = ::apache_avro::schema::Name::new_with_enclosing_namespace( + "namespace.testing.A", + enclosing_namespace, + ) + .expect("Unable to parse schema name namespace.testing.A"); + if named_schemas.contains(&name) { + ::apache_avro::schema::Schema::Ref { + name, + } + } else { + let enclosing_namespace = name.namespace(); + named_schemas.insert(name.clone()); + { + let mut schema_fields = ::std::vec::Vec::with_capacity(2usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "a".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "b".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + let schema_field_set: ::std::collections::HashSet<_> = schema_fields + .iter() + .map(|rf| &rf.name) + .collect(); + match (&schema_fields.len(), &schema_field_set.len()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( + kind, + &*left_val, + &*right_val, + ::core::option::Option::Some( + format_args!( + "Duplicate field names found: {0:?}", schema_fields, + ), + ), + ); + } + } + }; + let name = ::apache_avro::schema::Name::new("namespace.testing.A") + .expect( + &::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "Unable to parse struct name for schema {0}", + "namespace.testing.A", + ), + ) + })[..], + ); + let lookup: ::std::collections::BTreeMap = schema_fields + .iter() + .enumerate() + .map(|(position, field)| (field.name.to_owned(), position)) + .collect(); + ::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema { + name, + aliases: ::std::option::Option::None, + doc: ::std::option::Option::None, + fields: schema_fields, + lookup, + attributes: ::std::collections::BTreeMap::new(), + }) + } + } + } + fn get_record_fields_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> { + let mut schema_fields = Vec::with_capacity(2usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "a".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "b".to_string(), + doc: ::std::option::Option::None, + default: ::field_default(), + aliases: ::std::vec::Vec::new(), + schema: ::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + Some(schema_fields) + } + fn field_default() -> ::std::option::Option<::serde_json::Value> { + ::std::option::Option::None + } +} diff --git a/avro_derive/tests/expanded/avro_rs_501_namespace.rs b/avro_derive/tests/expanded/avro_rs_501_namespace.rs new file mode 100644 index 00000000..ea7c89fd --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_501_namespace.rs @@ -0,0 +1,25 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema)] +#[avro(namespace = "namespace.testing")] +struct A { + a: i32, + b: String, +} diff --git a/avro_derive/tests/expanded/avro_rs_501_reference.expanded.rs b/avro_derive/tests/expanded/avro_rs_501_reference.expanded.rs new file mode 100644 index 00000000..6865b943 --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_501_reference.expanded.rs @@ -0,0 +1,139 @@ +use apache_avro::AvroSchema; +struct A<'a> { + a: &'a Vec, + b: &'static str, +} +#[automatically_derived] +impl<'a> ::apache_avro::AvroSchemaComponent for A<'a> { + fn get_schema_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::apache_avro::schema::Schema { + let name = ::apache_avro::schema::Name::new_with_enclosing_namespace( + "A", + enclosing_namespace, + ) + .expect("Unable to parse schema name A"); + if named_schemas.contains(&name) { + ::apache_avro::schema::Schema::Ref { + name, + } + } else { + let enclosing_namespace = name.namespace(); + named_schemas.insert(name.clone()); + { + let mut schema_fields = ::std::vec::Vec::with_capacity(2usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "a".to_string(), + doc: ::std::option::Option::None, + default: <&'a Vec< + i32, + > as ::apache_avro::AvroSchemaComponent>::field_default(), + aliases: ::std::vec::Vec::new(), + schema: <&'a Vec< + i32, + > as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "b".to_string(), + doc: ::std::option::Option::None, + default: <&'static str as ::apache_avro::AvroSchemaComponent>::field_default(), + aliases: ::std::vec::Vec::new(), + schema: <&'static str as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + let schema_field_set: ::std::collections::HashSet<_> = schema_fields + .iter() + .map(|rf| &rf.name) + .collect(); + match (&schema_fields.len(), &schema_field_set.len()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( + kind, + &*left_val, + &*right_val, + ::core::option::Option::Some( + format_args!( + "Duplicate field names found: {0:?}", schema_fields, + ), + ), + ); + } + } + }; + let name = ::apache_avro::schema::Name::new("A") + .expect( + &::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "Unable to parse struct name for schema {0}", "A", + ), + ) + })[..], + ); + let lookup: ::std::collections::BTreeMap = schema_fields + .iter() + .enumerate() + .map(|(position, field)| (field.name.to_owned(), position)) + .collect(); + ::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema { + name, + aliases: ::std::option::Option::None, + doc: ::std::option::Option::None, + fields: schema_fields, + lookup, + attributes: ::std::collections::BTreeMap::new(), + }) + } + } + } + fn get_record_fields_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> { + let mut schema_fields = Vec::with_capacity(2usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "a".to_string(), + doc: ::std::option::Option::None, + default: <&'a Vec< + i32, + > as ::apache_avro::AvroSchemaComponent>::field_default(), + aliases: ::std::vec::Vec::new(), + schema: <&'a Vec< + i32, + > as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "b".to_string(), + doc: ::std::option::Option::None, + default: <&'static str as ::apache_avro::AvroSchemaComponent>::field_default(), + aliases: ::std::vec::Vec::new(), + schema: <&'static str as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + Some(schema_fields) + } + fn field_default() -> ::std::option::Option<::serde_json::Value> { + ::std::option::Option::None + } +} diff --git a/avro_derive/tests/expanded/avro_rs_501_reference.rs b/avro_derive/tests/expanded/avro_rs_501_reference.rs new file mode 100644 index 00000000..af8b4e24 --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_501_reference.rs @@ -0,0 +1,24 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema)] +struct A<'a> { + a: &'a Vec, + b: &'static str, +} diff --git a/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.expanded.rs b/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.expanded.rs new file mode 100644 index 00000000..4a22c229 --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.expanded.rs @@ -0,0 +1,115 @@ +use apache_avro::AvroSchema; +struct TestOptional { + a: Option, +} +#[automatically_derived] +impl ::apache_avro::AvroSchemaComponent for TestOptional { + fn get_schema_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::apache_avro::schema::Schema { + let name = ::apache_avro::schema::Name::new_with_enclosing_namespace( + "TestOptional", + enclosing_namespace, + ) + .expect("Unable to parse schema name TestOptional"); + if named_schemas.contains(&name) { + ::apache_avro::schema::Schema::Ref { + name, + } + } else { + let enclosing_namespace = name.namespace(); + named_schemas.insert(name.clone()); + { + let mut schema_fields = ::std::vec::Vec::with_capacity(1usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "a".to_string(), + doc: ::std::option::Option::None, + default: as ::apache_avro::AvroSchemaComponent>::field_default(), + aliases: ::std::vec::Vec::new(), + schema: as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + let schema_field_set: ::std::collections::HashSet<_> = schema_fields + .iter() + .map(|rf| &rf.name) + .collect(); + match (&schema_fields.len(), &schema_field_set.len()) { + (left_val, right_val) => { + if !(*left_val == *right_val) { + let kind = ::core::panicking::AssertKind::Eq; + ::core::panicking::assert_failed( + kind, + &*left_val, + &*right_val, + ::core::option::Option::Some( + format_args!( + "Duplicate field names found: {0:?}", schema_fields, + ), + ), + ); + } + } + }; + let name = ::apache_avro::schema::Name::new("TestOptional") + .expect( + &::alloc::__export::must_use({ + ::alloc::fmt::format( + format_args!( + "Unable to parse struct name for schema {0}", + "TestOptional", + ), + ) + })[..], + ); + let lookup: ::std::collections::BTreeMap = schema_fields + .iter() + .enumerate() + .map(|(position, field)| (field.name.to_owned(), position)) + .collect(); + ::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema { + name, + aliases: ::std::option::Option::None, + doc: ::std::option::Option::None, + fields: schema_fields, + lookup, + attributes: ::std::collections::BTreeMap::new(), + }) + } + } + } + fn get_record_fields_in_ctxt( + named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>, + enclosing_namespace: ::apache_avro::schema::NamespaceRef, + ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> { + let mut schema_fields = Vec::with_capacity(1usize); + schema_fields + .push(::apache_avro::schema::RecordField { + name: "a".to_string(), + doc: ::std::option::Option::None, + default: as ::apache_avro::AvroSchemaComponent>::field_default(), + aliases: ::std::vec::Vec::new(), + schema: as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt( + named_schemas, + enclosing_namespace, + ), + custom_attributes: ::std::collections::BTreeMap::new(), + }); + Some(schema_fields) + } + fn field_default() -> ::std::option::Option<::serde_json::Value> { + ::std::option::Option::None + } +} diff --git a/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.rs b/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.rs new file mode 100644 index 00000000..5b9a63f1 --- /dev/null +++ b/avro_derive/tests/expanded/avro_rs_501_struct_with_optional.rs @@ -0,0 +1,23 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema)] +struct TestOptional { + a: Option, +} diff --git a/avro_derive/tests/expanded/mod.rs b/avro_derive/tests/expanded/mod.rs new file mode 100644 index 00000000..268680aa --- /dev/null +++ b/avro_derive/tests/expanded/mod.rs @@ -0,0 +1,28 @@ +// 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. + +#![expect(dead_code, reason = "Only code generation is tested")] + +// Do not include the `.expanded` modules here, as that code won't compile + +mod avro_3687_basic_enum_with_default; +mod avro_3709_record_field_attributes; +mod avro_rs_207_rename_all_attribute; +mod avro_rs_207_rename_attr_over_rename_all_attribute; +mod avro_rs_501_basic; +mod avro_rs_501_namespace; +mod avro_rs_501_reference; +mod avro_rs_501_struct_with_optional; diff --git a/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.rs b/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.rs new file mode 100644 index 00000000..2ba3e44a --- /dev/null +++ b/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.rs @@ -0,0 +1,30 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema, Default)] +enum Basic { + #[default] + A, + B, + #[default] + C, + D +} + +fn main() {} diff --git a/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.stderr b/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.stderr new file mode 100644 index 00000000..14a37988 --- /dev/null +++ b/avro_derive/tests/ui/avro_3687_basic_enum_with_default_twice.stderr @@ -0,0 +1,19 @@ +error: Multiple defaults defined: ["A", "C"] + --> tests/ui/avro_3687_basic_enum_with_default_twice.rs:21:6 + | +21 | enum Basic { + | ^^^^^ + +error: multiple declared defaults + --> tests/ui/avro_3687_basic_enum_with_default_twice.rs:20:22 + | +20 | #[derive(AvroSchema, Default)] + | ^^^^^^^ +... +23 | A, + | - first default +... +26 | C, + | - additional default + | + = note: only one variant can be default diff --git a/avro_derive/tests/ui/avro_rs_501_non_basic_enum.rs b/avro_derive/tests/ui/avro_rs_501_non_basic_enum.rs new file mode 100644 index 00000000..72230d8f --- /dev/null +++ b/avro_derive/tests/ui/avro_rs_501_non_basic_enum.rs @@ -0,0 +1,28 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema)] +enum NonBasic { + A(i32), + B, + C, + D +} + +fn main() {} diff --git a/avro_derive/tests/ui/avro_rs_501_non_basic_enum.stderr b/avro_derive/tests/ui/avro_rs_501_non_basic_enum.stderr new file mode 100644 index 00000000..961253bb --- /dev/null +++ b/avro_derive/tests/ui/avro_rs_501_non_basic_enum.stderr @@ -0,0 +1,5 @@ +error: AvroSchema: derive does not work for enums with non unit structs + --> tests/ui/avro_rs_501_non_basic_enum.rs:21:6 + | +21 | enum NonBasic { + | ^^^^^^^^ diff --git a/avro_derive/tests/ui/avro_rs_501_tuple_struct.rs b/avro_derive/tests/ui/avro_rs_501_tuple_struct.rs new file mode 100644 index 00000000..b697d5ba --- /dev/null +++ b/avro_derive/tests/ui/avro_rs_501_tuple_struct.rs @@ -0,0 +1,23 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema)] +struct B (i32, String); + +fn main() {} diff --git a/avro_derive/tests/ui/avro_rs_501_tuple_struct.stderr b/avro_derive/tests/ui/avro_rs_501_tuple_struct.stderr new file mode 100644 index 00000000..ca0236e9 --- /dev/null +++ b/avro_derive/tests/ui/avro_rs_501_tuple_struct.stderr @@ -0,0 +1,5 @@ +error: AvroSchema derive does not work for tuple structs + --> tests/ui/avro_rs_501_tuple_struct.rs:21:8 + | +21 | struct B (i32, String); + | ^ diff --git a/avro_derive/tests/ui/avro_rs_501_unit_struct.rs b/avro_derive/tests/ui/avro_rs_501_unit_struct.rs new file mode 100644 index 00000000..1b740afb --- /dev/null +++ b/avro_derive/tests/ui/avro_rs_501_unit_struct.rs @@ -0,0 +1,23 @@ +// 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 apache_avro::AvroSchema; + +#[derive(AvroSchema)] +struct AbsoluteUnit; + +fn main() {} diff --git a/avro_derive/tests/ui/avro_rs_501_unit_struct.stderr b/avro_derive/tests/ui/avro_rs_501_unit_struct.stderr new file mode 100644 index 00000000..2a16ef6a --- /dev/null +++ b/avro_derive/tests/ui/avro_rs_501_unit_struct.stderr @@ -0,0 +1,5 @@ +error: AvroSchema derive does not work for unit structs + --> tests/ui/avro_rs_501_unit_struct.rs:21:8 + | +21 | struct AbsoluteUnit; + | ^^^^^^^^^^^^ From bdac65d611ba188900623a563f23991ad6215f72 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 5 Mar 2026 09:37:09 +0000 Subject: [PATCH 2/6] fix: Expanded files are missing license header --- .github/workflows/test-lang-rust-ci.yml | 9 ++++++++- avro_derive/tests/expanded/mod.rs | 1 + licenserc.toml | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-lang-rust-ci.yml b/.github/workflows/test-lang-rust-ci.yml index 92e8b676..4b969ba0 100644 --- a/.github/workflows/test-lang-rust-ci.yml +++ b/.github/workflows/test-lang-rust-ci.yml @@ -94,12 +94,19 @@ jobs: key: cargo-rdme- - name: Cache cargo-expand - if: matrix.rust == 'stable' + if: matrix.rust == 'nightly' uses: actions/cache@v5 with: path: ~/.cargo-${{ matrix.rust }}/cargo-expand key: cargo-expand- + - name: Install cargo-expand + # The result is environment independent so one test pattern is enough. + if: matrix.rust == 'nightly' + run: | + cargo install --root ~/.cargo-${{ matrix.rust }}/cargo-expand --locked cargo-expand + export PATH=$PATH:~/.cargo-${{ matrix.rust }}/cargo-expand/bin + # Check if the doc cumment in avro/src/lib.rs and avro/README.md are in sync. - name: Run cargo-rdme # The result is environment independent so one test pattern is enough. diff --git a/avro_derive/tests/expanded/mod.rs b/avro_derive/tests/expanded/mod.rs index 268680aa..adaa76a8 100644 --- a/avro_derive/tests/expanded/mod.rs +++ b/avro_derive/tests/expanded/mod.rs @@ -1,3 +1,4 @@ +// 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 diff --git a/licenserc.toml b/licenserc.toml index 2c153a79..7ef9fe67 100644 --- a/licenserc.toml +++ b/licenserc.toml @@ -18,6 +18,7 @@ headerPath = "Apache-2.0-ASF.txt" excludes = [ "*.stderr", + "*.expanded.rs", "*.avro", "NOTICE", "README.tpl", From 1d7572d22f93fe25c378ba422dfba2282d6bf247 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 5 Mar 2026 10:08:41 +0000 Subject: [PATCH 3/6] fix: Steps don't share anything --- .github/workflows/test-lang-rust-ci.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-lang-rust-ci.yml b/.github/workflows/test-lang-rust-ci.yml index 4b969ba0..48d18897 100644 --- a/.github/workflows/test-lang-rust-ci.yml +++ b/.github/workflows/test-lang-rust-ci.yml @@ -94,19 +94,11 @@ jobs: key: cargo-rdme- - name: Cache cargo-expand - if: matrix.rust == 'nightly' uses: actions/cache@v5 with: path: ~/.cargo-${{ matrix.rust }}/cargo-expand key: cargo-expand- - - name: Install cargo-expand - # The result is environment independent so one test pattern is enough. - if: matrix.rust == 'nightly' - run: | - cargo install --root ~/.cargo-${{ matrix.rust }}/cargo-expand --locked cargo-expand - export PATH=$PATH:~/.cargo-${{ matrix.rust }}/cargo-expand/bin - # Check if the doc cumment in avro/src/lib.rs and avro/README.md are in sync. - name: Run cargo-rdme # The result is environment independent so one test pattern is enough. @@ -125,7 +117,10 @@ jobs: - name: Rust Test if: matrix.runner.target != 'wasm32-unknown-unknown' - run: RUST_BACKTRACE=1 cargo test --all-features --target ${{ matrix.runner.target }} + run: | + cargo install --root ~/.cargo-${{ matrix.rust }}/cargo-expand --locked cargo-expand + export PATH=$PATH:~/.cargo-${{ matrix.rust }}/cargo-expand/bin + RUST_BACKTRACE=1 cargo test --all-features --target ${{ matrix.runner.target }} - name: Rust Test AVRO-3549 if: matrix.runner.target != 'wasm32-unknown-unknown' From 5dd3e98e15f9d144c5eea85c07a5153722395e2f Mon Sep 17 00:00:00 2001 From: default Date: Thu, 5 Mar 2026 11:58:33 +0000 Subject: [PATCH 4/6] fix: Use `taiki-e/install-action` to install `cargo-rdme` and `cargo-expand` --- .github/workflows/test-lang-rust-ci.yml | 27 ++++++------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test-lang-rust-ci.yml b/.github/workflows/test-lang-rust-ci.yml index 48d18897..85e01c0c 100644 --- a/.github/workflows/test-lang-rust-ci.yml +++ b/.github/workflows/test-lang-rust-ci.yml @@ -61,6 +61,10 @@ jobs: target: aarch64-unknown-linux-gnu steps: + - uses: taiki-e/install-action@v2 + with: + tool: cargo-rdme,cargo-expand + - name: Checkout uses: actions/checkout@v6 @@ -86,27 +90,11 @@ jobs: components: rustfmt targets: ${{ matrix.runner.target }} - - name: Cache cargo-rdme - if: matrix.rust == 'stable' && matrix.runner.target == 'x86_64-unknown-linux-gnu' - uses: actions/cache@v5 - with: - path: ~/.cargo-${{ matrix.rust }}/cargo-rdme - key: cargo-rdme- - - - name: Cache cargo-expand - uses: actions/cache@v5 - with: - path: ~/.cargo-${{ matrix.rust }}/cargo-expand - key: cargo-expand- - # Check if the doc cumment in avro/src/lib.rs and avro/README.md are in sync. - name: Run cargo-rdme # The result is environment independent so one test pattern is enough. if: matrix.rust == 'stable' && matrix.runner.target == 'x86_64-unknown-linux-gnu' - run: | - cargo install --root ~/.cargo-${{ matrix.rust }}/cargo-rdme --locked cargo-rdme - export PATH=$PATH:~/.cargo-${{ matrix.rust }}/cargo-rdme/bin - cargo rdme --check + run: cargo rdme --check - name: Rust Format if: matrix.runner.target != 'wasm32-unknown-unknown' @@ -117,10 +105,7 @@ jobs: - name: Rust Test if: matrix.runner.target != 'wasm32-unknown-unknown' - run: | - cargo install --root ~/.cargo-${{ matrix.rust }}/cargo-expand --locked cargo-expand - export PATH=$PATH:~/.cargo-${{ matrix.rust }}/cargo-expand/bin - RUST_BACKTRACE=1 cargo test --all-features --target ${{ matrix.runner.target }} + run: RUST_BACKTRACE=1 cargo test --all-features --target ${{ matrix.runner.target }} - name: Rust Test AVRO-3549 if: matrix.runner.target != 'wasm32-unknown-unknown' From 01cb0af4d0893411824460638455bf612a5dc017 Mon Sep 17 00:00:00 2001 From: Kriskras99 Date: Thu, 5 Mar 2026 13:06:27 +0100 Subject: [PATCH 5/6] fix: Typo in documentation Co-authored-by: Martin Grigorov --- avro_derive/tests/expand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avro_derive/tests/expand.rs b/avro_derive/tests/expand.rs index fd6071a0..fcd5c5d2 100644 --- a/avro_derive/tests/expand.rs +++ b/avro_derive/tests/expand.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -// By including the (unexpended) modules, we can also test that the +// By including the (unexpanded) modules, we can also test that the // generated code is valid. mod expanded; From b0f1f60811ad6d4c23548e1d452c71face21ad92 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 5 Mar 2026 12:10:04 +0000 Subject: [PATCH 6/6] fix: Only install `cargo-rdme` on `stable-x86_64` and `cargo-expand` on `nightly` --- .github/workflows/test-lang-rust-ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-lang-rust-ci.yml b/.github/workflows/test-lang-rust-ci.yml index 85e01c0c..380366b3 100644 --- a/.github/workflows/test-lang-rust-ci.yml +++ b/.github/workflows/test-lang-rust-ci.yml @@ -62,8 +62,14 @@ jobs: steps: - uses: taiki-e/install-action@v2 + if: matrix.rust == 'nightly' with: - tool: cargo-rdme,cargo-expand + tool: cargo-expand + + - uses: taiki-e/install-action@v2 + if: matrix.rust == 'stable' && matrix.runner.target == 'x86_64-unknown-linux-gnu' + with: + tool: cargo-rdme - name: Checkout uses: actions/checkout@v6