You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by mg...@apache.org on 2022/04/19 17:44:02 UTC
[avro] branch avro-3484-derive-default-value-for-record-field created (now c13ce3045)
This is an automated email from the ASF dual-hosted git repository.
mgrigorov pushed a change to branch avro-3484-derive-default-value-for-record-field
in repository https://gitbox.apache.org/repos/asf/avro.git
at c13ce3045 AVRO-3484: Add support for deriving a default value for a record field
This branch includes the following new commits:
new c13ce3045 AVRO-3484: Add support for deriving a default value for a record field
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
[avro] 01/01: AVRO-3484: Add support for deriving a default value for a record field
Posted by mg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
mgrigorov pushed a commit to branch avro-3484-derive-default-value-for-record-field
in repository https://gitbox.apache.org/repos/asf/avro.git
commit c13ce3045b90e51ef781ad7760a6e9b558759b15
Author: Martin Tzvetanov Grigorov <mg...@apache.org>
AuthorDate: Tue Apr 19 10:27:48 2022 +0300
AVRO-3484: Add support for deriving a default value for a record field
Signed-off-by: Martin Tzvetanov Grigorov <mg...@apache.org>
---
lang/rust/avro_derive/Cargo.toml | 5 +-
lang/rust/avro_derive/src/lib.rs | 18 +++--
lang/rust/avro_derive/tests/derive.rs | 130 ++++++++++++++++++++++++++++++++++
3 files changed, 147 insertions(+), 6 deletions(-)
diff --git a/lang/rust/avro_derive/Cargo.toml b/lang/rust/avro_derive/Cargo.toml
index e16e9ea95..2ba8bc8c0 100644
--- a/lang/rust/avro_derive/Cargo.toml
+++ b/lang/rust/avro_derive/Cargo.toml
@@ -32,10 +32,11 @@ documentation = "https://docs.rs/apache-avro-derive"
proc-macro = true
[dependencies]
-syn = {version= "1.0.91", features=["full", "fold"]}
+darling = "0.14.0"
quote = "1.0.18"
+syn = {version= "1.0.91", features=["full", "fold"]}
proc-macro2 = "1.0.37"
-darling = "0.14.0"
+serde_json = "1.0.79"
[dev-dependencies]
serde = { version = "1.0.136", features = ["derive"] }
diff --git a/lang/rust/avro_derive/src/lib.rs b/lang/rust/avro_derive/src/lib.rs
index 0055249cb..393db8c19 100644
--- a/lang/rust/avro_derive/src/lib.rs
+++ b/lang/rust/avro_derive/src/lib.rs
@@ -30,6 +30,8 @@ use syn::{
struct FieldOptions {
#[darling(default)]
doc: Option<String>,
+ #[darling(default)]
+ default: Option<String>,
}
#[derive(FromAttributes)]
@@ -117,16 +119,24 @@ fn get_data_struct_schema_def(
syn::Fields::Named(ref a) => {
for (position, field) in a.named.iter().enumerate() {
let name = field.ident.as_ref().unwrap().to_string(); // we know everything has a name
- let field_documented =
+ let field_attrs =
FieldOptions::from_attributes(&field.attrs[..]).map_err(darling_to_syn)?;
- let doc = preserve_optional(field_documented.doc);
+ let doc = preserve_optional(field_attrs.doc);
+ let default_value = match field_attrs.default {
+ Some(default_value) => {
+ quote! {
+ Some(serde_json::from_str(#default_value).expect(format!("Invalid JSON: {:?}", #default_value).as_str()))
+ }
+ }
+ None => quote! { None },
+ };
let schema_expr = type_to_schema_expr(&field.ty)?;
let position = position;
record_field_exprs.push(quote! {
apache_avro::schema::RecordField {
name: #name.to_string(),
doc: #doc,
- default: Option::None,
+ default: #default_value,
schema: #schema_expr,
order: apache_avro::schema::RecordFieldOrder::Ascending,
position: #position,
@@ -186,7 +196,7 @@ fn get_data_enum_schema_def(
name: apache_avro::schema::Name::new(#full_schema_name).expect(&format!("Unable to parse enum name for schema {}", #full_schema_name)[..]),
aliases: #enum_aliases,
doc: #doc,
- symbols: vec![#(#symbols.to_owned()),*]
+ symbols: vec![#(#symbols.to_owned()),*],
}
})
} else {
diff --git a/lang/rust/avro_derive/tests/derive.rs b/lang/rust/avro_derive/tests/derive.rs
index 284323a2a..0be2bd4bf 100644
--- a/lang/rust/avro_derive/tests/derive.rs
+++ b/lang/rust/avro_derive/tests/derive.rs
@@ -1209,4 +1209,134 @@ mod test_derive {
serde_assert(TestBasicEnumWithAliases2::B);
}
+
+ #[test]
+ fn test_basic_struct_with_defaults() {
+ #[derive(Debug, Deserialize, Serialize, AvroSchema, Clone, PartialEq)]
+ enum MyEnum {
+ Foo,
+ Bar,
+ Baz,
+ }
+
+ #[derive(Debug, Serialize, Deserialize, AvroSchema, Clone, PartialEq)]
+ struct TestBasicStructWithDefaultValues {
+ #[avro(default = "123")]
+ a: i32,
+ #[avro(default = r#""The default value for 'b'""#)]
+ b: String,
+ #[avro(default = "true")]
+ condition: bool,
+ // no default value for 'c'
+ c: f64,
+ #[avro(default = r#"{"a": 1, "b": 2}"#)]
+ map: HashMap<String, i32>,
+
+ #[avro(default = "[1, 2, 3]")]
+ array: Vec<i32>,
+
+ #[avro(default = r#""Foo""#)]
+ myenum: MyEnum,
+ }
+
+ let schema = r#"
+ {
+ "type":"record",
+ "name":"TestBasicStructWithDefaultValues",
+ "fields": [
+ {
+ "name":"a",
+ "type":"int",
+ "default":123
+ },
+ {
+ "name":"b",
+ "type":"string",
+ "default": "The default value for 'b'"
+ },
+ {
+ "name":"condition",
+ "type":"boolean",
+ "default":true
+ },
+ {
+ "name":"c",
+ "type":"double"
+ },
+ {
+ "name":"map",
+ "type":{
+ "type":"map",
+ "values":"int"
+ },
+ "default": {
+ "a": 1,
+ "b": 2
+ }
+ },
+ {
+ "name":"array",
+ "type":{
+ "type":"array",
+ "items":"int"
+ },
+ "default": [1, 2, 3]
+ },
+ {
+ "name":"myenum",
+ "type":{
+ "type":"enum",
+ "name":"MyEnum",
+ "symbols":["Foo", "Bar", "Baz"]
+ },
+ "default":"Foo"
+ }
+ ]
+ }
+ "#;
+
+ let schema = Schema::parse_str(schema).unwrap();
+ if let Schema::Record { name, fields, .. } = TestBasicStructWithDefaultValues::get_schema()
+ {
+ assert_eq!("TestBasicStructWithDefaultValues", name.fullname(None));
+ use serde_json::json;
+ for field in fields {
+ match field.name.as_str() {
+ "a" => assert_eq!(Some(json!(123_i32)), field.default),
+ "b" => assert_eq!(
+ Some(json!(r#"The default value for 'b'"#.to_owned())),
+ field.default
+ ),
+ "condition" => assert_eq!(Some(json!(true)), field.default),
+ "array" => assert_eq!(Some(json!([1, 2, 3])), field.default),
+ "map" => assert_eq!(
+ Some(json!({
+ "a": 1,
+ "b": 2
+ })),
+ field.default
+ ),
+ "c" => assert_eq!(None, field.default),
+ "myenum" => assert_eq!(Some(json!("Foo")), field.default),
+ _ => panic!("Unexpected field name"),
+ }
+ }
+ } else {
+ panic!("TestBasicStructWithDefaultValues schema must be a record schema")
+ }
+ assert_eq!(schema, TestBasicStructWithDefaultValues::get_schema());
+
+ serde_assert(TestBasicStructWithDefaultValues {
+ a: 321,
+ b: "A custom value for 'b'".to_owned(),
+ condition: false,
+ c: 987.654,
+ map: [("a".to_owned(), 1), ("b".to_owned(), 2)]
+ .iter()
+ .cloned()
+ .collect(),
+ array: vec![4, 5, 6],
+ myenum: MyEnum::Bar,
+ });
+ }
}