This is an automated email from the ASF dual-hosted git repository.

mgrigorov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro.git


The following commit(s) were added to refs/heads/main by this push:
     new 5184f83cb AVRO-3920: Serialize custom attribute in RecordField (#2650)
5184f83cb is described below

commit 5184f83cb7ca11469b45343e3bf9198eaf4de594
Author: ZENOTME <[email protected]>
AuthorDate: Thu Jan 4 20:08:20 2024 +0800

    AVRO-3920: Serialize custom attribute in RecordField (#2650)
    
    * AVRO-3920: [Rust] * serialize custom attr in record field
    
    * AVRO-3920: [Rust] Do not parse "logicalType" as a custom RecordField 
attribute
    
    Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
---
 lang/rust/avro/src/schema.rs    | 78 +++++++++++++++++++++++++++++++++--------
 lang/rust/avro_derive/README.md |  2 +-
 2 files changed, 65 insertions(+), 15 deletions(-)

diff --git a/lang/rust/avro/src/schema.rs b/lang/rust/avro/src/schema.rs
index 787e8596e..f4c063df6 100644
--- a/lang/rust/avro/src/schema.rs
+++ b/lang/rust/avro/src/schema.rs
@@ -709,10 +709,10 @@ impl RecordField {
             doc: field.doc(),
             default,
             aliases,
-            schema,
             order,
             position,
-            custom_attributes: RecordField::get_field_custom_attributes(field),
+            custom_attributes: RecordField::get_field_custom_attributes(field, 
&schema),
+            schema,
         })
     }
 
@@ -765,11 +765,17 @@ impl RecordField {
         Ok(())
     }
 
-    fn get_field_custom_attributes(field: &Map<String, Value>) -> 
BTreeMap<String, Value> {
+    fn get_field_custom_attributes(
+        field: &Map<String, Value>,
+        schema: &Schema,
+    ) -> BTreeMap<String, Value> {
         let mut custom_attributes: BTreeMap<String, Value> = BTreeMap::new();
         for (key, value) in field {
             match key.as_str() {
-                "type" | "name" | "doc" | "default" | "order" | "position" | 
"aliases" => continue,
+                "type" | "name" | "doc" | "default" | "order" | "position" | 
"aliases"
+                | "logicalType" => continue,
+                key if key == "symbols" && matches!(schema, Schema::Enum(_)) 
=> continue,
+                key if key == "size" && matches!(schema, Schema::Fixed(_)) => 
continue,
                 _ => custom_attributes.insert(key.clone(), value.clone()),
             };
         }
@@ -2043,6 +2049,10 @@ impl Serialize for RecordField {
             map.serialize_entry("aliases", aliases)?;
         }
 
+        for attr in &self.custom_attributes {
+            map.serialize_entry(attr.0, attr.1)?;
+        }
+
         map.end()
     }
 }
@@ -4541,26 +4551,26 @@ mod tests {
 
             assert_eq!(
                 schema.custom_attributes(),
-                Some(&expected_custom_attibutes())
+                Some(&expected_custom_attributes())
             );
         }
 
         Ok(())
     }
 
-    fn expected_custom_attibutes() -> BTreeMap<String, Value> {
-        let mut expected_attibutes: BTreeMap<String, Value> = 
Default::default();
-        expected_attibutes.insert("string_key".to_string(), 
Value::String("value".to_string()));
-        expected_attibutes.insert("number_key".to_string(), json!(1.23));
-        expected_attibutes.insert("null_key".to_string(), Value::Null);
-        expected_attibutes.insert(
+    fn expected_custom_attributes() -> BTreeMap<String, Value> {
+        let mut expected_attributes: BTreeMap<String, Value> = 
Default::default();
+        expected_attributes.insert("string_key".to_string(), 
Value::String("value".to_string()));
+        expected_attributes.insert("number_key".to_string(), json!(1.23));
+        expected_attributes.insert("null_key".to_string(), Value::Null);
+        expected_attributes.insert(
             "array_key".to_string(),
             Value::Array(vec![json!(1), json!(2), json!(3)]),
         );
         let mut object_value: HashMap<String, Value> = HashMap::new();
         object_value.insert("key".to_string(), 
Value::String("value".to_string()));
-        expected_attibutes.insert("object_key".to_string(), 
json!(object_value));
-        expected_attibutes
+        expected_attributes.insert("object_key".to_string(), 
json!(object_value));
+        expected_attributes
     }
 
     #[test]
@@ -4590,7 +4600,7 @@ mod tests {
                 assert_eq!(fields.len(), 1);
                 let field = &fields[0];
                 assert_eq!(&field.name, "field_one");
-                assert_eq!(field.custom_attributes, 
expected_custom_attibutes());
+                assert_eq!(field.custom_attributes, 
expected_custom_attributes());
             }
             _ => panic!("Expected Schema::Record"),
         }
@@ -6415,6 +6425,44 @@ mod tests {
         Ok(())
     }
 
+    #[test]
+    fn avro_3920_serialize_record_with_custom_attributes() -> TestResult {
+        let expected = {
+            let mut lookup = BTreeMap::new();
+            lookup.insert("value".to_owned(), 0);
+            Schema::Record(RecordSchema {
+                name: Name {
+                    name: "LongList".to_owned(),
+                    namespace: None,
+                },
+                aliases: Some(vec![Alias::new("LinkedLongs").unwrap()]),
+                doc: None,
+                fields: vec![RecordField {
+                    name: "value".to_string(),
+                    doc: None,
+                    default: None,
+                    aliases: None,
+                    schema: Schema::Long,
+                    order: RecordFieldOrder::Ascending,
+                    position: 0,
+                    custom_attributes: 
BTreeMap::from([("field-id".to_string(), 1.into())]),
+                }],
+                lookup,
+                attributes: BTreeMap::from([("custom-attribute".to_string(), 
"value".into())]),
+            })
+        };
+
+        let value = serde_json::to_value(&expected)?;
+        let serialized = serde_json::to_string(&value)?;
+        assert_eq!(
+            
r#"{"aliases":["LinkedLongs"],"custom-attribute":"value","fields":[{"field-id":1,"name":"value","type":"long"}],"name":"LongList","type":"record"}"#,
+            &serialized
+        );
+        assert_eq!(expected, Schema::parse_str(&serialized)?);
+
+        Ok(())
+    }
+
     #[test]
     fn test_avro_3925_serialize_decimal_inner_fixed() -> TestResult {
         let schema = Schema::Decimal(DecimalSchema {
@@ -6441,6 +6489,7 @@ mod tests {
 }"#;
 
         assert_eq!(serialized_json, expected_json);
+
         Ok(())
     }
 
@@ -6462,6 +6511,7 @@ mod tests {
 }"#;
 
         assert_eq!(serialized_json, expected_json);
+
         Ok(())
     }
 
diff --git a/lang/rust/avro_derive/README.md b/lang/rust/avro_derive/README.md
index 6faa215f5..0098d3897 100644
--- a/lang/rust/avro_derive/README.md
+++ b/lang/rust/avro_derive/README.md
@@ -66,4 +66,4 @@ let encoded = writer.into_inner();
 ```
 
 ### Compatibility Notes
-This module is designed to work in concert with the Serde implemenation. If 
your use case dictates needing to manually convert to a `Value` type in order 
to encode then the derived schema may not be correct.
+This module is designed to work in concert with the Serde implementation. If 
your use case dictates needing to manually convert to a `Value` type in order 
to encode then the derived schema may not be correct.

Reply via email to