This is an automated email from the ASF dual-hosted git repository. mgrigorov pushed a commit to branch branch-1.11 in repository https://gitbox.apache.org/repos/asf/avro.git
commit 34ba17f1c486f1fd7e801e6a204ad62b78d80ee3 Author: Martin Grigorov <[email protected]> AuthorDate: Fri Jan 21 08:37:30 2022 +0200 AVRO-3315: Rust: Add support to back/cycle reference an alias (#1466) Signed-off-by: Martin Tzvetanov Grigorov <[email protected]> (cherry picked from commit a35a2a5305560da8ccb8eb299498e73c8bafa3bd) --- lang/rust/src/schema.rs | 125 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 115 insertions(+), 10 deletions(-) diff --git a/lang/rust/src/schema.rs b/lang/rust/src/schema.rs index a60e047..ab5fd04 100644 --- a/lang/rust/src/schema.rs +++ b/lang/rust/src/schema.rs @@ -799,6 +799,45 @@ impl Parser { } } + fn register_resolving_schema(&mut self, name: &Name) { + let resolving_schema = Schema::Ref { name: name.clone() }; + self.resolving_schemas + .insert(name.fullname(None), resolving_schema.clone()); + + let namespace = &name.namespace; + + if let Some(ref aliases) = name.aliases { + aliases.iter().for_each(|alias| { + let alias_fullname = match namespace { + Some(ref ns) => format!("{}.{}", ns, alias), + None => alias.clone(), + }; + self.resolving_schemas + .insert(alias_fullname, resolving_schema.clone()); + }); + } + } + + fn register_parsed_schema(&mut self, name: &Name, schema: &Schema) { + self.parsed_schemas + .insert(name.fullname(None), schema.clone()); + self.resolving_schemas.remove(name.fullname(None).as_str()); + + let namespace = &name.namespace; + + if let Some(ref aliases) = name.aliases { + aliases.iter().for_each(|alias| { + let alias_fullname = match namespace { + Some(ref ns) => format!("{}.{}", ns, alias), + None => alias.clone(), + }; + self.parsed_schemas + .insert(alias_fullname.clone(), schema.clone()); + self.resolving_schemas.remove(alias_fullname.as_str()); + }); + } + } + /// Parse a `serde_json::Value` representing a Avro record type into a /// `Schema`. fn parse_record(&mut self, complex: &Map<String, Value>) -> AvroResult<Schema> { @@ -806,9 +845,7 @@ impl Parser { let mut lookup = HashMap::new(); - let resolving_schema = Schema::Ref { name: name.clone() }; - self.resolving_schemas - .insert(name.name.clone(), resolving_schema); + self.register_resolving_schema(&name); let fields: Vec<RecordField> = complex .get("fields") @@ -834,9 +871,7 @@ impl Parser { lookup, }; - self.parsed_schemas - .insert(name.fullname(None), schema.clone()); - self.resolving_schemas.remove(name.name.as_str()); + self.register_parsed_schema(&name, &schema); Ok(schema) } @@ -877,8 +912,9 @@ impl Parser { doc: complex.doc(), symbols, }; - self.parsed_schemas - .insert(name.fullname(None), schema.clone()); + + self.register_parsed_schema(&name, &schema); + Ok(schema) } @@ -932,8 +968,9 @@ impl Parser { doc, size: size as usize, }; - self.parsed_schemas - .insert(name.fullname(None), schema.clone()); + + self.register_parsed_schema(&name, &schema); + Ok(schema) } } @@ -1620,6 +1657,74 @@ mod tests { assert_eq!(schema_str, expected); } + // AVRO-3302 + #[test] + fn test_record_schema_with_currently_parsing_schema_aliases() { + let schema = Schema::parse_str( + r#" + { + "type": "record", + "name": "LongList", + "aliases": ["LinkedLongs"], + "fields" : [ + {"name": "value", "type": "long"}, + {"name": "next", "type": ["null", "LinkedLongs"]} + ] + } + "#, + ) + .unwrap(); + + let mut lookup = HashMap::new(); + lookup.insert("value".to_owned(), 0); + lookup.insert("next".to_owned(), 1); + + let expected = Schema::Record { + name: Name { + name: "LongList".to_owned(), + namespace: None, + aliases: Some(vec!["LinkedLongs".to_owned()]), + }, + doc: None, + fields: vec![ + RecordField { + name: "value".to_string(), + doc: None, + default: None, + schema: Schema::Long, + order: RecordFieldOrder::Ascending, + position: 0, + }, + RecordField { + name: "next".to_string(), + doc: None, + default: None, + schema: Schema::Union( + UnionSchema::new(vec![ + Schema::Null, + Schema::Ref { + name: Name { + name: "LongList".to_owned(), + namespace: None, + aliases: Some(vec!["LinkedLongs".to_owned()]), + }, + }, + ]) + .unwrap(), + ), + order: RecordFieldOrder::Ascending, + position: 1, + }, + ], + lookup, + }; + assert_eq!(schema, expected); + + let canonical_form = &schema.canonical_form(); + let expected = r#"{"name":"LongList","type":"record","fields":[{"name":"value","type":"long"},{"name":"next","type":["null","LongList"]}]}"#; + assert_eq!(canonical_form, &expected); + } + #[test] fn test_enum_schema() { let schema = Schema::parse_str(
