This is an automated email from the ASF dual-hosted git repository. mgrigorov pushed a commit to branch issue-292-array-items-are-not-custom-attributes in repository https://gitbox.apache.org/repos/asf/avro-rs.git
commit 93622a5100345d12336f7230b7a21a024b751605 Author: Martin Tzvetanov Grigorov <mgrigo...@apache.org> AuthorDate: Tue Sep 30 15:52:10 2025 +0300 fix: Do not store "items" for Schema::Array and "values" for Schema::Map in custom_attributes Fixes: #292 --- avro/src/schema.rs | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 141 insertions(+), 3 deletions(-) diff --git a/avro/src/schema.rs b/avro/src/schema.rs index 833ca30..874fe79 100644 --- a/avro/src/schema.rs +++ b/avro/src/schema.rs @@ -778,6 +778,8 @@ impl RecordField { | "logicalType" => continue, key if key == "symbols" && matches!(schema, Schema::Enum(_)) => continue, key if key == "size" && matches!(schema, Schema::Fixed(_)) => continue, + key if key == "items" && matches!(schema, Schema::Array(_)) => continue, + key if key == "values" && matches!(schema, Schema::Map(_)) => continue, _ => custom_attributes.insert(key.clone(), value.clone()), }; } @@ -2315,11 +2317,10 @@ fn parsing_canonical_form(schema: &Value, defined_names: &mut HashSet<String>) - } fn pcf_map(schema: &Map<String, Value>, defined_names: &mut HashSet<String>) -> String { - // Look for the namespace variant up front. - let ns = schema.get("namespace").and_then(|v| v.as_str()); let typ = schema.get("type").and_then(|v| v.as_str()); - let raw_name = schema.get("name").and_then(|v| v.as_str()); let name = if is_named_type(typ) { + let ns = schema.get("namespace").and_then(|v| v.as_str()); + let raw_name = schema.get("name").and_then(|v| v.as_str()); Some(format!( "{}{}", ns.map_or("".to_string(), |n| { format!("{n}.") }), @@ -7227,4 +7228,141 @@ mod tests { Ok(()) } + + #[test] + fn avro_rs_292_array_items_should_be_ignored_in_custom_attributes() -> TestResult { + let raw_schema = r#"{ + "type": "array", + "items": { + "name": "foo", + "type": "record", + "fields": [ + { + "name": "bar", + "type": "array", + "items": { + "type": "record", + "name": "baz", + "fields": [ + { + "name": "quux", + "type": "int" + } + ] + } + } + ] + } + }"#; + + let schema1 = Schema::parse_str(raw_schema)?; + match &schema1 { + Schema::Array(ArraySchema { items, attributes }) => { + assert!(attributes.is_empty()); + + match **items { + Schema::Record(RecordSchema { + ref name, + aliases: _, + doc: _, + ref fields, + lookup: _, + ref attributes, + }) => { + assert_eq!(name.to_string(), "foo"); + assert_eq!(fields.len(), 1); + assert!(attributes.is_empty()); + + match &fields[0].schema { + Schema::Array(ArraySchema { + items: _, + attributes, + }) => { + assert!(attributes.is_empty()); + } + _ => panic!("Expected ArraySchema. got: {}", &fields[0].schema), + } + } + _ => panic!("Expected RecordSchema. got: {}", &items), + } + } + _ => panic!("Expected ArraySchema. got: {}", &schema1), + } + let canonical_form1 = schema1.canonical_form(); + let schema2 = Schema::parse_str(&canonical_form1)?; + let canonical_form2 = schema2.canonical_form(); + + assert_eq!(canonical_form1, canonical_form2); + + Ok(()) + } + + #[test] + fn avro_rs_292_map_values_should_be_ignored_in_custom_attributes() -> TestResult { + let raw_schema = r#"{ + "type": "array", + "items": { + "name": "foo", + "type": "record", + "fields": [ + { + "name": "bar", + "type": "map", + "values": { + "type": "record", + "name": "baz", + "fields": [ + { + "name": "quux", + "type": "int" + } + ] + } + } + ] + } + }"#; + + let schema1 = Schema::parse_str(raw_schema)?; + match &schema1 { + Schema::Array(ArraySchema { items, attributes }) => { + assert!(attributes.is_empty()); + + match **items { + Schema::Record(RecordSchema { + ref name, + aliases: _, + doc: _, + ref fields, + lookup: _, + ref attributes, + }) => { + assert_eq!(name.to_string(), "foo"); + assert_eq!(fields.len(), 1); + assert!(attributes.is_empty()); + + match &fields[0].schema { + Schema::Map(MapSchema { + types: _, + attributes, + }) => { + assert!(attributes.is_empty()); + } + _ => panic!("Expected MapSchema. got: {}", &fields[0].schema), + } + } + _ => panic!("Expected RecordSchema. got: {}", &items), + } + } + _ => panic!("Expected ArraySchema. got: {}", &schema1), + } + let canonical_form1 = schema1.canonical_form(); + println!("Canonical Form 1: {}", &canonical_form1); + let schema2 = Schema::parse_str(&canonical_form1)?; + let canonical_form2 = schema2.canonical_form(); + + assert_eq!(canonical_form1, canonical_form2); + + Ok(()) + } }