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

kriskras99 pushed a commit to branch feat/default_array_map
in repository https://gitbox.apache.org/repos/asf/avro-rs.git

commit ac975d8a707b4d71f310e5a692b1cd02d1ae6897
Author: default <[email protected]>
AuthorDate: Mon Feb 16 15:26:05 2026 +0000

    feat!: Support `default` field for array and map
---
 avro/src/error.rs         |  15 +-
 avro/src/schema/mod.rs    | 346 +++++++++++++++++++++++++++++++++++++---------
 avro/src/schema/parser.rs |  70 +++++++---
 avro/src/types.rs         |   2 +-
 4 files changed, 346 insertions(+), 87 deletions(-)

diff --git a/avro/src/error.rs b/avro/src/error.rs
index e2dfa28..efe1b7b 100644
--- a/avro/src/error.rs
+++ b/avro/src/error.rs
@@ -431,9 +431,21 @@ pub enum Details {
     #[error("Duplicate enum symbol {0}")]
     EnumSymbolDuplicate(String),
 
-    #[error("Default value for enum must be a string! Got: {0}")]
+    #[error("Default value for an enum must be a string! Got: {0}")]
     EnumDefaultWrongType(serde_json::Value),
 
+    #[error("Default value for an array must be an array! Got: {0}")]
+    ArrayDefaultWrongType(serde_json::Value),
+
+    #[error("Default value for an array must be an array of {0}! Found: 
{1:?}")]
+    ArrayDefaultWrongInnerType(Schema, Value),
+
+    #[error("Default value for a map must be a object! Got: {0}")]
+    MapDefaultWrongType(serde_json::Value),
+
+    #[error("Default value for a map must be a object with (String, {0})! 
Found: (String, {1:?})")]
+    MapDefaultWrongInnerType(Schema, Value),
+
     #[error("No `items` in array")]
     GetArrayItemsField,
 
@@ -446,6 +458,7 @@ pub enum Details {
     #[error("Fixed schema has no `size`")]
     GetFixedSizeField,
 
+    #[deprecated(since = "0.22.0", note = "This error variant is not generated 
anymore")]
     #[error("Fixed schema's default value length ({0}) does not match its size 
({1})")]
     FixedDefaultLenSizeMismatch(usize, u64),
 
diff --git a/avro/src/schema/mod.rs b/avro/src/schema/mod.rs
index 5d9e0ca..ef83121 100644
--- a/avro/src/schema/mod.rs
+++ b/avro/src/schema/mod.rs
@@ -27,14 +27,15 @@ use crate::{
     AvroResult,
     error::{Details, Error},
     schema::{parser::Parser, record::RecordSchemaParseLocation},
-    schema_equality, types,
+    schema_equality,
+    types::{self, Value},
 };
 use digest::Digest;
 use serde::{
     Serialize, Serializer,
-    ser::{SerializeMap, SerializeSeq},
+    ser::{Error as _, SerializeMap, SerializeSeq},
 };
-use serde_json::{Map, Value};
+use serde_json::{Map, Value as JsonValue};
 use std::{
     collections::{BTreeMap, HashMap, HashSet},
     fmt,
@@ -166,13 +167,15 @@ pub enum Schema {
 #[derive(Clone, Debug, PartialEq)]
 pub struct MapSchema {
     pub types: Box<Schema>,
-    pub attributes: BTreeMap<String, Value>,
+    pub default: Option<HashMap<String, Value>>,
+    pub attributes: BTreeMap<String, JsonValue>,
 }
 
 #[derive(Clone, Debug, PartialEq)]
 pub struct ArraySchema {
     pub items: Box<Schema>,
-    pub attributes: BTreeMap<String, Value>,
+    pub default: Option<Vec<Value>>,
+    pub attributes: BTreeMap<String, JsonValue>,
 }
 
 impl PartialEq for Schema {
@@ -265,7 +268,7 @@ pub struct EnumSchema {
     pub default: Option<String>,
     /// The custom attributes of the schema
     #[builder(default = BTreeMap::new())]
-    pub attributes: BTreeMap<String, Value>,
+    pub attributes: BTreeMap<String, JsonValue>,
 }
 
 /// A description of a Fixed schema.
@@ -283,7 +286,7 @@ pub struct FixedSchema {
     pub size: usize,
     /// The custom attributes of the schema
     #[builder(default = BTreeMap::new())]
-    pub attributes: BTreeMap<String, Value>,
+    pub attributes: BTreeMap<String, JsonValue>,
 }
 
 impl FixedSchema {
@@ -459,12 +462,12 @@ impl Schema {
     pub fn parse_list(input: impl IntoIterator<Item = impl AsRef<str>>) -> 
AvroResult<Vec<Schema>> {
         let input = input.into_iter();
         let input_len = input.size_hint().0;
-        let mut input_schemas: HashMap<Name, Value> = 
HashMap::with_capacity(input_len);
+        let mut input_schemas: HashMap<Name, JsonValue> = 
HashMap::with_capacity(input_len);
         let mut input_order: Vec<Name> = Vec::with_capacity(input_len);
         for json in input {
             let json = json.as_ref();
-            let schema: Value = 
serde_json::from_str(json).map_err(Details::ParseSchemaJson)?;
-            if let Value::Object(inner) = &schema {
+            let schema: JsonValue = 
serde_json::from_str(json).map_err(Details::ParseSchemaJson)?;
+            if let JsonValue::Object(inner) = &schema {
                 let name = Name::parse(inner, &None)?;
                 let previous_value = input_schemas.insert(name.clone(), 
schema);
                 if previous_value.is_some() {
@@ -501,12 +504,12 @@ impl Schema {
     ) -> AvroResult<(Schema, Vec<Schema>)> {
         let schemata = schemata.into_iter();
         let schemata_len = schemata.size_hint().0;
-        let mut input_schemas: HashMap<Name, Value> = 
HashMap::with_capacity(schemata_len);
+        let mut input_schemas: HashMap<Name, JsonValue> = 
HashMap::with_capacity(schemata_len);
         let mut input_order: Vec<Name> = Vec::with_capacity(schemata_len);
         for json in schemata {
             let json = json.as_ref();
-            let schema: Value = 
serde_json::from_str(json).map_err(Details::ParseSchemaJson)?;
-            if let Value::Object(inner) = &schema {
+            let schema: JsonValue = 
serde_json::from_str(json).map_err(Details::ParseSchemaJson)?;
+            if let JsonValue::Object(inner) = &schema {
                 let name = Name::parse(inner, &None)?;
                 if let Some(_previous) = input_schemas.insert(name.clone(), 
schema) {
                     return 
Err(Details::NameCollision(name.fullname(None)).into());
@@ -539,20 +542,20 @@ impl Schema {
     }
 
     /// Parses an Avro schema from JSON.
-    pub fn parse(value: &Value) -> AvroResult<Schema> {
+    pub fn parse(value: &JsonValue) -> AvroResult<Schema> {
         let mut parser = Parser::default();
         parser.parse(value, &None)
     }
 
     /// Parses an Avro schema from JSON.
     /// Any `Schema::Ref`s must be known in the `names` map.
-    pub(crate) fn parse_with_names(value: &Value, names: Names) -> 
AvroResult<Schema> {
+    pub(crate) fn parse_with_names(value: &JsonValue, names: Names) -> 
AvroResult<Schema> {
         let mut parser = Parser::new(HashMap::with_capacity(1), 
Vec::with_capacity(1), names);
         parser.parse(value, &None)
     }
 
     /// Returns the custom attributes (metadata) if the schema supports them.
-    pub fn custom_attributes(&self) -> Option<&BTreeMap<String, Value>> {
+    pub fn custom_attributes(&self) -> Option<&BTreeMap<String, JsonValue>> {
         match self {
             Schema::Record(RecordSchema { attributes, .. })
             | Schema::Enum(EnumSchema { attributes, .. })
@@ -644,14 +647,16 @@ impl Schema {
     pub fn map(types: Schema) -> Self {
         Schema::Map(MapSchema {
             types: Box::new(types),
+            default: None,
             attributes: Default::default(),
         })
     }
 
     /// Returns a `Schema::Map` with the given types and custom attributes.
-    pub fn map_with_attributes(types: Schema, attributes: BTreeMap<String, 
Value>) -> Self {
+    pub fn map_with_attributes(types: Schema, attributes: BTreeMap<String, 
JsonValue>) -> Self {
         Schema::Map(MapSchema {
             types: Box::new(types),
+            default: None,
             attributes,
         })
     }
@@ -660,14 +665,16 @@ impl Schema {
     pub fn array(items: Schema) -> Self {
         Schema::Array(ArraySchema {
             items: Box::new(items),
+            default: None,
             attributes: Default::default(),
         })
     }
 
     /// Returns a `Schema::Array` with the given items and custom attributes.
-    pub fn array_with_attributes(items: Schema, attributes: BTreeMap<String, 
Value>) -> Self {
+    pub fn array_with_attributes(items: Schema, attributes: BTreeMap<String, 
JsonValue>) -> Self {
         Schema::Array(ArraySchema {
             items: Box::new(items),
+            default: None,
             attributes,
         })
     }
@@ -748,21 +755,43 @@ impl Serialize for Schema {
             Schema::Double => serializer.serialize_str("double"),
             Schema::Bytes => serializer.serialize_str("bytes"),
             Schema::String => serializer.serialize_str("string"),
-            Schema::Array(inner) => {
-                let mut map = serializer.serialize_map(Some(2 + 
inner.attributes.len()))?;
+            Schema::Array(ArraySchema {
+                items,
+                default,
+                attributes,
+            }) => {
+                let mut map = serializer.serialize_map(Some(
+                    2 + attributes.len() + if default.is_some() { 1 } else { 0 
},
+                ))?;
                 map.serialize_entry("type", "array")?;
-                map.serialize_entry("items", &*inner.items.clone())?;
-                for attr in &inner.attributes {
-                    map.serialize_entry(attr.0, attr.1)?;
+                map.serialize_entry("items", items)?;
+                if let Some(default) = default {
+                    let value = 
JsonValue::try_from(Value::Array(default.clone()))
+                        .map_err(S::Error::custom)?;
+                    map.serialize_entry("default", &value)?;
+                }
+                for (key, value) in attributes {
+                    map.serialize_entry(key, value)?;
                 }
                 map.end()
             }
-            Schema::Map(inner) => {
-                let mut map = serializer.serialize_map(Some(2 + 
inner.attributes.len()))?;
+            Schema::Map(MapSchema {
+                types,
+                default,
+                attributes,
+            }) => {
+                let mut map = serializer.serialize_map(Some(
+                    2 + attributes.len() + if default.is_some() { 1 } else { 0 
},
+                ))?;
                 map.serialize_entry("type", "map")?;
-                map.serialize_entry("values", &*inner.types.clone())?;
-                for attr in &inner.attributes {
-                    map.serialize_entry(attr.0, attr.1)?;
+                map.serialize_entry("values", types)?;
+                if let Some(default) = default {
+                    let value = 
JsonValue::try_from(Value::Map(default.clone()))
+                        .map_err(S::Error::custom)?;
+                    map.serialize_entry("default", &value)?;
+                }
+                for (key, value) in attributes {
+                    map.serialize_entry(key, value)?;
                 }
                 map.end()
             }
@@ -945,16 +974,16 @@ impl Serialize for Schema {
 /// Parses a valid Avro schema into [the Parsing Canonical Form].
 ///
 /// [the Parsing Canonical 
Form](https://avro.apache.org/docs/++version++/specification/#parsing-canonical-form-for-schemas)
-fn parsing_canonical_form(schema: &Value, defined_names: &mut HashSet<String>) 
-> String {
+fn parsing_canonical_form(schema: &JsonValue, defined_names: &mut 
HashSet<String>) -> String {
     match schema {
-        Value::Object(map) => pcf_map(map, defined_names),
-        Value::String(s) => pcf_string(s),
-        Value::Array(v) => pcf_array(v, defined_names),
+        JsonValue::Object(map) => pcf_map(map, defined_names),
+        JsonValue::String(s) => pcf_string(s),
+        JsonValue::Array(v) => pcf_array(v, defined_names),
         json => panic!("got invalid JSON value for canonical form of schema: 
{json}"),
     }
 }
 
-fn pcf_map(schema: &Map<String, Value>, defined_names: &mut HashSet<String>) 
-> String {
+fn pcf_map(schema: &Map<String, JsonValue>, defined_names: &mut 
HashSet<String>) -> String {
     let typ = schema.get("type").and_then(|v| v.as_str());
     let name = if is_named_type(typ) {
         let ns = schema.get("namespace").and_then(|v| v.as_str());
@@ -982,7 +1011,7 @@ fn pcf_map(schema: &Map<String, Value>, defined_names: 
&mut HashSet<String>) ->
         // Reduce primitive types to their simple form. ([PRIMITIVE] rule)
         if schema.len() == 1 && k == "type" {
             // Invariant: function is only callable from a valid schema, so 
this is acceptable.
-            if let Value::String(s) = v {
+            if let JsonValue::String(s) = v {
                 return pcf_string(s);
             }
         }
@@ -1043,7 +1072,7 @@ fn is_named_type(typ: Option<&str>) -> bool {
     )
 }
 
-fn pcf_array(arr: &[Value], defined_names: &mut HashSet<String>) -> String {
+fn pcf_array(arr: &[JsonValue], defined_names: &mut HashSet<String>) -> String 
{
     let inter = arr
         .iter()
         .map(|a| parsing_canonical_form(a, defined_names))
@@ -1426,7 +1455,7 @@ mod tests {
             fields: vec![RecordField {
                 name: "field_one".to_string(),
                 doc: None,
-                default: Some(Value::Null),
+                default: Some(JsonValue::Null),
                 aliases: None,
                 schema: Schema::Union(UnionSchema::new(vec![
                     Schema::Null,
@@ -1474,7 +1503,7 @@ mod tests {
                 RecordField {
                     name: "a".to_string(),
                     doc: None,
-                    default: Some(Value::Number(42i64.into())),
+                    default: Some(JsonValue::Number(42i64.into())),
                     aliases: None,
                     schema: Schema::Long,
                     order: RecordFieldOrder::Ascending,
@@ -2654,17 +2683,20 @@ mod tests {
         Ok(())
     }
 
-    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()));
+    fn expected_custom_attributes() -> BTreeMap<String, JsonValue> {
+        let mut expected_attributes: BTreeMap<String, JsonValue> = 
Default::default();
+        expected_attributes.insert(
+            "string_key".to_string(),
+            JsonValue::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("null_key".to_string(), JsonValue::Null);
         expected_attributes.insert(
             "array_key".to_string(),
-            Value::Array(vec![json!(1), json!(2), json!(3)]),
+            JsonValue::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()));
+        let mut object_value: HashMap<String, JsonValue> = HashMap::new();
+        object_value.insert("key".to_string(), 
JsonValue::String("value".to_string()));
         expected_attributes.insert("object_key".to_string(), 
json!(object_value));
         expected_attributes
     }
@@ -2726,7 +2758,7 @@ mod tests {
                 assert_eq!(fields.len(), 1);
                 let field = &fields[0];
                 assert_eq!(&field.name, "a");
-                assert_eq!(&field.default, &Some(Value::Null));
+                assert_eq!(&field.default, &Some(JsonValue::Null));
                 match &field.schema {
                     Schema::Union(union) => {
                         assert_eq!(union.variants().len(), 2);
@@ -3107,7 +3139,7 @@ mod tests {
             Err(err) => {
                 assert_eq!(
                     err.to_string(),
-                    "Default value for enum must be a string! Got: 123"
+                    "Default value for an enum must be a string! Got: 123"
                 );
             }
             _ => panic!("Expected an error"),
@@ -3918,19 +3950,17 @@ mod tests {
             ]
         }
         "#;
-        let expected = Details::GetDefaultRecordField(
-            "f1".to_string(),
-            "ns.record1".to_string(),
-            r#"{"type":"array","items":"int"}"#.to_string(),
-        )
-        .to_string();
+
         let result = Schema::parse_str(schema_str);
         assert!(result.is_err());
         let err = result
             .map_err(|e| e.to_string())
             .err()
             .unwrap_or_else(|| "unexpected".to_string());
-        assert_eq!(expected, err);
+        assert_eq!(
+            r#"Default value for an array must be an array! Got: "invalid""#,
+            err
+        );
 
         Ok(())
     }
@@ -3952,19 +3982,17 @@ mod tests {
             ]
         }
         "#;
-        let expected = Details::GetDefaultRecordField(
-            "f1".to_string(),
-            "ns.record1".to_string(),
-            r#"{"type":"map","values":"string"}"#.to_string(),
-        )
-        .to_string();
+
         let result = Schema::parse_str(schema_str);
         assert!(result.is_err());
         let err = result
             .map_err(|e| e.to_string())
             .err()
             .unwrap_or_else(|| "unexpected".to_string());
-        assert_eq!(expected, err);
+        assert_eq!(
+            r#"Default value for a map must be a object! Got: "invalid""#,
+            err
+        );
 
         Ok(())
     }
@@ -4280,12 +4308,12 @@ mod tests {
         let attributes = BTreeMap::from([
             ("string_key".into(), "value".into()),
             ("number_key".into(), 1.23.into()),
-            ("null_key".into(), Value::Null),
+            ("null_key".into(), JsonValue::Null),
             (
                 "array_key".into(),
-                Value::Array(vec![1.into(), 2.into(), 3.into()]),
+                JsonValue::Array(vec![1.into(), 2.into(), 3.into()]),
             ),
-            ("object_key".into(), Value::Object(Map::default())),
+            ("object_key".into(), JsonValue::Object(Map::default())),
         ]);
 
         // Test serialize enum attributes
@@ -4873,7 +4901,11 @@ mod tests {
 
         let schema1 = Schema::parse_str(raw_schema)?;
         match &schema1 {
-            Schema::Array(ArraySchema { items, attributes }) => {
+            Schema::Array(ArraySchema {
+                items,
+                default: _,
+                attributes,
+            }) => {
                 assert!(attributes.is_empty());
 
                 match **items {
@@ -4892,6 +4924,7 @@ mod tests {
                         match &fields[0].schema {
                             Schema::Array(ArraySchema {
                                 items: _,
+                                default: _,
                                 attributes,
                             }) => {
                                 assert!(attributes.is_empty());
@@ -4941,7 +4974,11 @@ mod tests {
 
         let schema1 = Schema::parse_str(raw_schema)?;
         match &schema1 {
-            Schema::Array(ArraySchema { items, attributes }) => {
+            Schema::Array(ArraySchema {
+                items,
+                default: _,
+                attributes,
+            }) => {
                 assert!(attributes.is_empty());
 
                 match **items {
@@ -4960,6 +4997,7 @@ mod tests {
                         match &fields[0].schema {
                             Schema::Map(MapSchema {
                                 types: _,
+                                default: _,
                                 attributes,
                             }) => {
                                 assert!(attributes.is_empty());
@@ -5147,7 +5185,7 @@ mod tests {
     }
 
     #[test]
-    fn avro_rs_460_enum_default_in_custom_attributes() -> TestResult {
+    fn avro_rs_460_enum_default_not_in_custom_attributes() -> TestResult {
         let schema = Schema::parse_str(
             r#"{
             "name": "enum_with_default",
@@ -5171,4 +5209,180 @@ mod tests {
 
         Ok(())
     }
+
+    #[test]
+    fn avro_rs_xxx_array_default() -> TestResult {
+        let schema = Schema::parse_str(
+            r#"{
+            "type": "array",
+            "items": "string",
+            "default": []
+        }"#,
+        )?;
+
+        let Schema::Array(array) = schema else {
+            panic!("Expected Schema::Array, got {schema:?}");
+        };
+
+        assert_eq!(array.attributes, BTreeMap::new());
+        assert_eq!(array.default, Some(Vec::new()));
+
+        let json = serde_json::to_string(&Schema::Array(array))?;
+        json.contains(r#""default":[]"#);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_array_default_with_actual_values() -> TestResult {
+        let schema = Schema::parse_str(
+            r#"{
+            "type": "array",
+            "items": "string",
+            "default": ["foo", "bar"]
+        }"#,
+        )?;
+
+        let Schema::Array(array) = schema else {
+            panic!("Expected Schema::Array, got {schema:?}");
+        };
+
+        assert_eq!(array.attributes, BTreeMap::new());
+        assert_eq!(
+            array.default,
+            Some(vec![
+                Value::String("foo".into()),
+                Value::String("bar".into())
+            ])
+        );
+
+        let json = serde_json::to_string(&Schema::Array(array))?;
+        json.contains(r#""default":["foo","bar"]"#);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_array_default_with_invalid_values() -> TestResult {
+        let err = Schema::parse_str(
+            r#"{
+            "type": "array",
+            "items": "string",
+            "default": [false, true]
+        }"#,
+        )
+        .unwrap_err();
+
+        assert_eq!(
+            err.to_string(),
+            "Default value for an array must be an array of String! Found: 
Boolean(false)"
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_array_default_with_mixed_values() -> TestResult {
+        let err = Schema::parse_str(
+            r#"{
+            "type": "array",
+            "items": "string",
+            "default": ["foo", true]
+        }"#,
+        )
+        .unwrap_err();
+
+        assert_eq!(
+            err.to_string(),
+            "Default value for an array must be an array of String! Found: 
Boolean(true)"
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_map_default() -> TestResult {
+        let schema = Schema::parse_str(
+            r#"{
+            "type": "map",
+            "values": "string",
+            "default": {}
+        }"#,
+        )?;
+
+        let Schema::Map(map) = schema else {
+            panic!("Expected Schema::Map, got {schema:?}");
+        };
+
+        assert_eq!(map.attributes, BTreeMap::new());
+        assert_eq!(map.default, Some(HashMap::new()));
+
+        let json = serde_json::to_string(&Schema::Map(map))?;
+        json.contains(r#""default":{}"#);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_map_default_with_actual_values() -> TestResult {
+        let schema = Schema::parse_str(
+            r#"{
+            "type": "map",
+            "values": "string",
+            "default": {"foo": "bar"}
+        }"#,
+        )?;
+
+        let Schema::Map(map) = schema else {
+            panic!("Expected Schema::Map, got {schema:?}");
+        };
+
+        let mut hashmap = HashMap::new();
+        hashmap.insert("foo".to_string(), Value::String("bar".into()));
+        assert_eq!(map.attributes, BTreeMap::new());
+        assert_eq!(map.default, Some(hashmap));
+
+        let json = serde_json::to_string(&Schema::Map(map))?;
+        json.contains(r#""default":{"foo":"bar"}"#);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_map_default_with_invalid_values() -> TestResult {
+        let err = Schema::parse_str(
+            r#"{
+            "type": "map",
+            "values": "string",
+            "default": {"foo": true}
+        }"#,
+        )
+        .unwrap_err();
+
+        assert_eq!(
+            err.to_string(),
+            "Default value for a map must be a object with (String, String)! 
Found: (String, Boolean(true))"
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_xxx_map_default_with_mixed_values() -> TestResult {
+        let err = Schema::parse_str(
+            r#"{
+            "type": "map",
+            "values": "string",
+            "default": {"foo": "bar", "spam": true}
+        }"#,
+        )
+        .unwrap_err();
+
+        assert_eq!(
+            err.to_string(),
+            "Default value for a map must be a object with (String, String)! 
Found: (String, Boolean(true))"
+        );
+
+        Ok(())
+    }
 }
diff --git a/avro/src/schema/parser.rs b/avro/src/schema/parser.rs
index 9500b82..d7adb1d 100644
--- a/avro/src/schema/parser.rs
+++ b/avro/src/schema/parser.rs
@@ -18,9 +18,9 @@
 use crate::error::Details;
 use crate::schema::record::RecordSchemaParseLocation;
 use crate::schema::{
-    Alias, Aliases, DecimalMetadata, DecimalSchema, EnumSchema, FixedSchema, 
Name, Names,
-    Namespace, Precision, RecordField, RecordSchema, Scale, Schema, 
SchemaKind, UnionSchema,
-    UuidSchema,
+    Alias, Aliases, ArraySchema, DecimalMetadata, DecimalSchema, EnumSchema, 
FixedSchema,
+    MapSchema, Name, Names, Namespace, Precision, RecordField, RecordSchema, 
Scale, Schema,
+    SchemaKind, UnionSchema, UuidSchema,
 };
 use crate::types;
 use crate::util::MapHelper;
@@ -704,16 +704,31 @@ impl Parser {
         complex: &Map<String, Value>,
         enclosing_namespace: &Namespace,
     ) -> AvroResult<Schema> {
-        complex
+        let items = complex
             .get("items")
             .ok_or_else(|| Details::GetArrayItemsField.into())
-            .and_then(|items| self.parse(items, enclosing_namespace))
-            .map(|items| {
-                Schema::array_with_attributes(
-                    items,
-                    self.get_custom_attributes(complex, vec!["items"]),
-                )
-            })
+            .and_then(|items| self.parse(items, enclosing_namespace))?;
+        let default = if let Some(default) = complex.get("default").cloned() {
+            if let Value::Array(_) = default {
+                let crate::types::Value::Array(array) = 
crate::types::Value::from(default) else {
+                    unreachable!("JsonValue::Array can only become a 
Value::Array")
+                };
+                // Check that the default type matches the schema type
+                if let Some(value) = array.iter().find(|v| 
!v.validate(&items)) {
+                    return Err(Details::ArrayDefaultWrongInnerType(items, 
value.clone()).into());
+                }
+                Some(array)
+            } else {
+                return Err(Details::ArrayDefaultWrongType(default).into());
+            }
+        } else {
+            None
+        };
+        Ok(Schema::Array(ArraySchema {
+            items: Box::new(items),
+            default,
+            attributes: self.get_custom_attributes(complex, vec!["items", 
"default"]),
+        }))
     }
 
     /// Parse a `serde_json::Value` representing a Avro map type into a 
`Schema`.
@@ -722,16 +737,33 @@ impl Parser {
         complex: &Map<String, Value>,
         enclosing_namespace: &Namespace,
     ) -> AvroResult<Schema> {
-        complex
+        let types = complex
             .get("values")
             .ok_or_else(|| Details::GetMapValuesField.into())
-            .and_then(|items| self.parse(items, enclosing_namespace))
-            .map(|items| {
-                Schema::map_with_attributes(
-                    items,
-                    self.get_custom_attributes(complex, vec!["values"]),
-                )
-            })
+            .and_then(|types| self.parse(types, enclosing_namespace))?;
+
+        let default = if let Some(default) = complex.get("default").cloned() {
+            if let Value::Object(_) = default {
+                let crate::types::Value::Map(map) = 
crate::types::Value::from(default) else {
+                    unreachable!("JsonValue::Object can only become a 
Value::Map")
+                };
+                // Check that the default type matches the schema type
+                if let Some(value) = map.values().find(|v| 
!v.validate(&types)) {
+                    return Err(Details::MapDefaultWrongInnerType(types, 
value.clone()).into());
+                }
+                Some(map)
+            } else {
+                return Err(Details::MapDefaultWrongType(default).into());
+            }
+        } else {
+            None
+        };
+
+        Ok(Schema::Map(MapSchema {
+            types: Box::new(types),
+            default,
+            attributes: self.get_custom_attributes(complex, vec!["values", 
"default"]),
+        }))
     }
 
     /// Parse a `serde_json::Value` representing a Avro union type into a 
`Schema`.
diff --git a/avro/src/types.rs b/avro/src/types.rs
index d070fe5..48d4578 100644
--- a/avro/src/types.rs
+++ b/avro/src/types.rs
@@ -1346,7 +1346,7 @@ mod tests {
                 Value::Array(vec![Value::Boolean(true)]),
                 Schema::array(Schema::Long),
                 false,
-                "Invalid value: Array([Boolean(true)]) for schema: 
Array(ArraySchema { items: Long, attributes: {} }). Reason: Unsupported 
value-schema combination! Value: Boolean(true), schema: Long",
+                "Invalid value: Array([Boolean(true)]) for schema: 
Array(ArraySchema { items: Long, default: None, attributes: {} }). Reason: 
Unsupported value-schema combination! Value: Boolean(true), schema: Long",
             ),
             (
                 Value::Record(vec![]),

Reply via email to