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![]),
