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

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


The following commit(s) were added to refs/heads/master by this push:
     new dd7d8eeae AVRO-3818: [Rust] Let inner named types inherit their 
enclosing namespace if they have no their own namespace (#2405)
dd7d8eeae is described below

commit dd7d8eeaed1fa98803d38bf72ed2a5739d49b208
Author: Kousuke Saruta <[email protected]>
AuthorDate: Tue Aug 1 04:19:00 2023 +0900

    AVRO-3818: [Rust] Let inner named types inherit their enclosing namespace 
if they have no their own namespace (#2405)
    
    * Fix to inherit enclosing namespace.
    
    * AVRO-3818: Fix a minor typo in comment
    
    * AVRO-3818: Fix a minor typo in comment
    
    ---------
    
    Co-authored-by: Martin Grigorov <[email protected]>
---
 lang/rust/avro/src/schema.rs | 195 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 178 insertions(+), 17 deletions(-)

diff --git a/lang/rust/avro/src/schema.rs b/lang/rust/avro/src/schema.rs
index 289817ef8..9ab6ce7ec 100644
--- a/lang/rust/avro/src/schema.rs
+++ b/lang/rust/avro/src/schema.rs
@@ -239,7 +239,10 @@ impl Name {
     }
 
     /// Parse a `serde_json::Value` into a `Name`.
-    pub(crate) fn parse(complex: &Map<String, Value>) -> AvroResult<Self> {
+    pub(crate) fn parse(
+        complex: &Map<String, Value>,
+        enclosing_namespace: &Namespace,
+    ) -> AvroResult<Self> {
         let (name, namespace_from_name) = complex
             .name()
             .map(|name| Name::get_name_and_namespace(name.as_str()).unwrap())
@@ -252,8 +255,12 @@ impl Name {
 
         Ok(Self {
             name: type_name.unwrap_or(name),
-            namespace: namespace_from_name
-                .or_else(|| complex.string("namespace").filter(|ns| 
!ns.is_empty())),
+            namespace: namespace_from_name.or_else(|| {
+                complex
+                    .string("namespace")
+                    .or(enclosing_namespace.clone())
+                    .filter(|ns| !ns.is_empty())
+            }),
         })
     }
 
@@ -320,7 +327,7 @@ impl<'de> Deserialize<'de> for Name {
         Value::deserialize(deserializer).and_then(|value| {
             use serde::de::Error;
             if let Value::Object(json) = value {
-                Name::parse(&json).map_err(Error::custom)
+                Name::parse(&json, &None).map_err(Error::custom)
             } else {
                 Err(Error::custom(format!("Expected a JSON object: 
{value:?}")))
             }
@@ -918,7 +925,7 @@ impl Schema {
         for js in input {
             let schema: Value = 
serde_json::from_str(js).map_err(Error::ParseSchemaJson)?;
             if let Value::Object(inner) = &schema {
-                let name = Name::parse(inner)?;
+                let name = Name::parse(inner, &None)?;
                 let previous_value = input_schemas.insert(name.clone(), 
schema);
                 if previous_value.is_some() {
                     return Err(Error::NameCollision(name.fullname(None)));
@@ -1087,7 +1094,9 @@ impl Parser {
         let fully_qualified_name = 
name.fully_qualified_name(enclosing_namespace);
 
         if self.parsed_schemas.get(&fully_qualified_name).is_some() {
-            return Ok(Schema::Ref { name });
+            return Ok(Schema::Ref {
+                name: fully_qualified_name,
+            });
         }
         if let Some(resolving_schema) = 
self.resolving_schemas.get(&fully_qualified_name) {
             return Ok(resolving_schema.clone());
@@ -1403,11 +1412,12 @@ impl Parser {
             }
         }
 
-        let name = Name::parse(complex)?;
+        let name = Name::parse(complex, enclosing_namespace)?;
         let aliases = fix_aliases_namespace(complex.aliases(), 
&name.namespace);
 
         let mut lookup = BTreeMap::new();
-        let fully_qualified_name = 
name.fully_qualified_name(enclosing_namespace);
+        let fully_qualified_name = name.clone();
+
         self.register_resolving_schema(&fully_qualified_name, &aliases);
 
         let fields: Vec<RecordField> = fields_opt
@@ -1435,7 +1445,7 @@ impl Parser {
         }
 
         let schema = Schema::Record(RecordSchema {
-            name,
+            name: fully_qualified_name.clone(),
             aliases: aliases.clone(),
             doc: complex.doc(),
             fields,
@@ -1478,8 +1488,8 @@ impl Parser {
             }
         }
 
-        let name = Name::parse(complex)?;
-        let fully_qualified_name = 
name.fully_qualified_name(enclosing_namespace);
+        let name = Name::parse(complex, enclosing_namespace)?;
+        let fully_qualified_name = name.clone();
         let aliases = fix_aliases_namespace(complex.aliases(), 
&name.namespace);
 
         let symbols: Vec<String> = symbols_opt
@@ -1518,7 +1528,7 @@ impl Parser {
         }
 
         let schema = Schema::Enum(EnumSchema {
-            name,
+            name: fully_qualified_name.clone(),
             aliases: aliases.clone(),
             doc: complex.doc(),
             symbols,
@@ -1620,12 +1630,12 @@ impl Parser {
             None => Err(Error::GetFixedSizeField),
         }?;
 
-        let name = Name::parse(complex)?;
-        let fully_qualified_name = 
name.fully_qualified_name(enclosing_namespace);
+        let name = Name::parse(complex, enclosing_namespace)?;
+        let fully_qualified_name = name.clone();
         let aliases = fix_aliases_namespace(complex.aliases(), 
&name.namespace);
 
         let schema = Schema::Fixed(FixedSchema {
-            name,
+            name: fully_qualified_name.clone(),
             aliases: aliases.clone(),
             doc,
             size: size as usize,
@@ -2626,7 +2636,7 @@ mod tests {
 
         let schema = Schema::parse_str(schema)?;
         let schema_str = schema.canonical_form();
-        let expected = 
r#"{"name":"office.User","type":"record","fields":[{"name":"details","type":[{"name":"Employee","type":"record","fields":[{"name":"gender","type":{"name":"Gender","type":"enum","symbols":["male","female"]}}]},{"name":"Manager","type":"record","fields":[{"name":"gender","type":"Gender"}]}]}]}"#;
+        let expected = 
r#"{"name":"office.User","type":"record","fields":[{"name":"details","type":[{"name":"office.Employee","type":"record","fields":[{"name":"gender","type":{"name":"office.Gender","type":"enum","symbols":["male","female"]}}]},{"name":"office.Manager","type":"record","fields":[{"name":"gender","type":"office.Gender"}]}]}]}"#;
         assert_eq!(schema_str, expected);
 
         Ok(())
@@ -2676,7 +2686,7 @@ mod tests {
 
         let schema = Schema::parse_str(schema)?;
         let schema_str = schema.canonical_form();
-        let expected = 
r#"{"name":"office.User","type":"record","fields":[{"name":"details","type":[{"name":"Employee","type":"record","fields":[{"name":"id","type":{"name":"EmployeeId","type":"fixed","size":16}}]},{"name":"Manager","type":"record","fields":[{"name":"id","type":"EmployeeId"}]}]}]}"#;
+        let expected = 
r#"{"name":"office.User","type":"record","fields":[{"name":"details","type":[{"name":"office.Employee","type":"record","fields":[{"name":"id","type":{"name":"office.EmployeeId","type":"fixed","size":16}}]},{"name":"office.Manager","type":"record","fields":[{"name":"id","type":"office.EmployeeId"}]}]}]}"#;
         assert_eq!(schema_str, expected);
 
         Ok(())
@@ -4865,4 +4875,155 @@ mod tests {
 
         Ok(())
     }
+
+    #[test]
+    fn test_avro_3818_inherit_enclosing_namespace() -> TestResult {
+        // Enclosing namespace is specified but inner namespaces are not.
+        let schema_str = r#"
+        {
+          "namespace": "my_ns",
+          "type": "record",
+          "name": "my_schema",
+          "fields": [
+            {
+              "name": "f1",
+              "type": {
+                "name": "enum1",
+                "type": "enum",
+                "symbols": ["a"]
+              }
+            },  {
+              "name": "f2",
+              "type": {
+                "name": "fixed1",
+                "type": "fixed",
+                "size": 1
+              }
+            }
+          ]
+        }
+        "#;
+
+        let expected = 
r#"{"name":"my_ns.my_schema","type":"record","fields":[{"name":"f1","type":{"name":"my_ns.enum1","type":"enum","symbols":["a"]}},{"name":"f2","type":{"name":"my_ns.fixed1","type":"fixed","size":1}}]}"#;
+        let schema = Schema::parse_str(schema_str)?;
+        let canonical_form = schema.canonical_form();
+        assert_eq!(canonical_form, expected);
+
+        // Enclosing namespace and inner namespaces are specified
+        // but inner namespaces are ""
+        let schema_str = r#"
+        {
+          "namespace": "my_ns",
+          "type": "record",
+          "name": "my_schema",
+          "fields": [
+            {
+              "name": "f1",
+              "type": {
+                "name": "enum1",
+                "type": "enum",
+                "namespace": "",
+                "symbols": ["a"]
+              }
+            },  {
+              "name": "f2",
+              "type": {
+                "name": "fixed1",
+                "type": "fixed",
+                "namespace": "",
+                "size": 1
+              }
+            }
+          ]
+        }
+        "#;
+
+        let expected = 
r#"{"name":"my_ns.my_schema","type":"record","fields":[{"name":"f1","type":{"name":"enum1","type":"enum","symbols":["a"]}},{"name":"f2","type":{"name":"fixed1","type":"fixed","size":1}}]}"#;
+        let schema = Schema::parse_str(schema_str)?;
+        let canonical_form = schema.canonical_form();
+        assert_eq!(canonical_form, expected);
+
+        // Enclosing namespace is "" and inner non-empty namespaces are 
specified.
+        let schema_str = r#"
+        {
+          "namespace": "",
+          "type": "record",
+          "name": "my_schema",
+          "fields": [
+            {
+              "name": "f1",
+              "type": {
+                "name": "enum1",
+                "type": "enum",
+                "namespace": "f1.ns",
+                "symbols": ["a"]
+              }
+            },  {
+              "name": "f2",
+              "type": {
+                "name": "f2.ns.fixed1",
+                "type": "fixed",
+                "size": 1
+              }
+            }
+          ]
+        }
+        "#;
+
+        let expected = 
r#"{"name":"my_schema","type":"record","fields":[{"name":"f1","type":{"name":"f1.ns.enum1","type":"enum","symbols":["a"]}},{"name":"f2","type":{"name":"f2.ns.fixed1","type":"fixed","size":1}}]}"#;
+        let schema = Schema::parse_str(schema_str)?;
+        let canonical_form = schema.canonical_form();
+        assert_eq!(canonical_form, expected);
+
+        // Nested complex types with non-empty enclosing namespace.
+        let schema_str = r#"
+        {
+          "type": "record",
+          "name": "my_ns.my_schema",
+          "fields": [
+            {
+              "name": "f1",
+              "type": {
+                "name": "inner_record1",
+                "type": "record",
+                "fields": [
+                  {
+                    "name": "f1_1",
+                    "type": {
+                      "name": "enum1",
+                      "type": "enum",
+                      "symbols": ["a"]
+                    }
+                  }
+                ]
+              }
+            },  {
+              "name": "f2",
+                "type": {
+                "name": "inner_record2",
+                "type": "record",
+                "namespace": "inner_ns",
+                "fields": [
+                  {
+                    "name": "f2_1",
+                    "type": {
+                      "name": "enum2",
+                      "type": "enum",
+                      "symbols": ["a"]
+                    }
+                  }
+                ]
+              }
+            }
+          ]
+        }
+        "#;
+
+        let expected = 
r#"{"name":"my_ns.my_schema","type":"record","fields":[{"name":"f1","type":{"name":"my_ns.inner_record1","type":"record","fields":[{"name":"f1_1","type":{"name":"my_ns.enum1","type":"enum","symbols":["a"]}}]}},{"name":"f2","type":{"name":"inner_ns.inner_record2","type":"record","fields":[{"name":"f2_1","type":{"name":"inner_ns.enum2","type":"enum","symbols":["a"]}}]}}]}"#;
+        let schema = Schema::parse_str(schema_str)?;
+        let canonical_form = schema.canonical_form();
+        assert_eq!(canonical_form, expected);
+
+        Ok(())
+    }
 }

Reply via email to