You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by mg...@apache.org on 2022/08/17 10:47:25 UTC
[avro] branch master updated: AVRO-3609: [Rust] Add support for custom attributes (#1829)
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 63c58f3b2 AVRO-3609: [Rust] Add support for custom attributes (#1829)
63c58f3b2 is described below
commit 63c58f3b29d20ba94a6c68d9e4c09816d448bf34
Author: Martin Grigorov <ma...@users.noreply.github.com>
AuthorDate: Wed Aug 17 13:47:18 2022 +0300
AVRO-3609: [Rust] Add support for custom attributes (#1829)
* AVRO-3609: [Rust] Add custom attributes field to Record, Enum and Fixed schemata
Signed-off-by: Martin Tzvetanov Grigorov <mg...@apache.org>
* AVRO-3609: [Rust] Add support for custom attributes
Support parsing custom attributes for Record, Enum and Fixed schemata.
Signed-off-by: Martin Tzvetanov Grigorov <mg...@apache.org>
* AVRO-3609: [Rust] Add support for parsing custom attributes in RecordField
Signed-off-by: Martin Tzvetanov Grigorov <mg...@apache.org>
* AVRO-3609: Fix the build and formatting
Signed-off-by: Martin Tzvetanov Grigorov <mg...@apache.org>
Signed-off-by: Martin Tzvetanov Grigorov <mg...@apache.org>
---
lang/rust/avro/src/decode.rs | 2 +
lang/rust/avro/src/schema.rs | 276 +++++++++++++++++++++++++++--
lang/rust/avro/src/schema_compatibility.rs | 2 +
lang/rust/avro/src/types.rs | 25 ++-
lang/rust/avro/src/writer.rs | 2 +
lang/rust/avro/tests/schema.rs | 2 +
lang/rust/avro_derive/src/lib.rs | 3 +
7 files changed, 294 insertions(+), 18 deletions(-)
diff --git a/lang/rust/avro/src/decode.rs b/lang/rust/avro/src/decode.rs
index cb32ebdb7..4f9e7e945 100644
--- a/lang/rust/avro/src/decode.rs
+++ b/lang/rust/avro/src/decode.rs
@@ -344,6 +344,7 @@ mod tests {
doc: None,
name: Name::new("decimal").unwrap(),
aliases: None,
+ attributes: Default::default(),
});
let schema = Schema::Decimal {
inner,
@@ -370,6 +371,7 @@ mod tests {
name: Name::new("decimal").unwrap(),
aliases: None,
doc: None,
+ attributes: Default::default(),
});
let schema = Schema::Decimal {
inner,
diff --git a/lang/rust/avro/src/schema.rs b/lang/rust/avro/src/schema.rs
index 0a9603fe7..e80f77859 100644
--- a/lang/rust/avro/src/schema.rs
+++ b/lang/rust/avro/src/schema.rs
@@ -107,6 +107,7 @@ pub enum Schema {
doc: Documentation,
fields: Vec<RecordField>,
lookup: BTreeMap<String, usize>,
+ attributes: BTreeMap<String, Value>,
},
/// An `enum` Avro schema.
Enum {
@@ -114,6 +115,7 @@ pub enum Schema {
aliases: Aliases,
doc: Documentation,
symbols: Vec<String>,
+ attributes: BTreeMap<String, Value>,
},
/// A `fixed` Avro schema.
Fixed {
@@ -121,6 +123,7 @@ pub enum Schema {
aliases: Aliases,
doc: Documentation,
size: usize,
+ attributes: BTreeMap<String, Value>,
},
/// Logical type which represents `Decimal` values. The underlying type is serialized and
/// deserialized as `Schema::Bytes` or `Schema::Fixed`.
@@ -340,12 +343,12 @@ impl<'de> Deserialize<'de> for Name {
where
D: serde::de::Deserializer<'de>,
{
- serde_json::Value::deserialize(deserializer).and_then(|value| {
+ Value::deserialize(deserializer).and_then(|value| {
use serde::de::Error;
if let Value::Object(json) = value {
- Name::parse(&json).map_err(D::Error::custom)
+ Name::parse(&json).map_err(Error::custom)
} else {
- Err(D::Error::custom(format!(
+ Err(Error::custom(format!(
"Expected a JSON object: {:?}",
value
)))
@@ -575,6 +578,8 @@ pub struct RecordField {
pub order: RecordFieldOrder,
/// Position of the field in the list of `field` of its parent `Schema`
pub position: usize,
+ /// A collection of all unknown fields in the record field.
+ pub custom_attributes: BTreeMap<String, Value>,
}
/// Represents any valid order for a `field` in a `record` Avro schema.
@@ -614,8 +619,21 @@ impl RecordField {
schema,
order,
position,
+ custom_attributes: RecordField::get_field_custom_attributes(field),
})
}
+
+ fn get_field_custom_attributes(field: &Map<String, Value>) -> BTreeMap<String, Value> {
+ let mut custom_attributes: BTreeMap<String, Value> = BTreeMap::new();
+ for (key, value) in field {
+ match key.as_str() {
+ "type" | "name" | "doc" | "default" | "order" | "position" => continue,
+ _ => custom_attributes.insert(key.clone(), value.clone()),
+ };
+ custom_attributes.insert(key.to_string(), value.clone());
+ }
+ custom_attributes
+ }
}
#[derive(Debug, Clone)]
@@ -779,10 +797,21 @@ impl Schema {
parser.parse_list()
}
+ /// Parses an Avro schema from JSON.
pub fn parse(value: &Value) -> AvroResult<Schema> {
let mut parser = Parser::default();
parser.parse(value, &None)
}
+
+ /// Returns the custom attributes (metadata) if the schema supports them.
+ pub fn custom_attributes(&self) -> Option<&BTreeMap<String, Value>> {
+ match self {
+ Schema::Record { attributes, .. }
+ | Schema::Enum { attributes, .. }
+ | Schema::Fixed { attributes, .. } => Some(attributes),
+ _ => None,
+ }
+ }
}
impl Parser {
@@ -1220,12 +1249,29 @@ impl Parser {
doc: complex.doc(),
fields,
lookup,
+ attributes: self.get_custom_attributes(complex, vec!["fields"]),
};
self.register_parsed_schema(&fully_qualified_name, &schema, &aliases);
Ok(schema)
}
+ fn get_custom_attributes(
+ &self,
+ complex: &Map<String, Value>,
+ excluded: Vec<&'static str>,
+ ) -> BTreeMap<String, Value> {
+ let mut custom_attributes: BTreeMap<String, Value> = BTreeMap::new();
+ for attribute in complex.iter() {
+ match attribute.0.as_str() {
+ "type" | "name" | "namespace" | "doc" | "aliases" => continue,
+ candidate if excluded.contains(&candidate) => continue,
+ _ => custom_attributes.insert(attribute.0.clone(), attribute.1.clone()),
+ };
+ }
+ custom_attributes
+ }
+
/// Parse a `serde_json::Value` representing a Avro enum type into a
/// `Schema`.
fn parse_enum(
@@ -1276,6 +1322,7 @@ impl Parser {
aliases: aliases.clone(),
doc: complex.doc(),
symbols,
+ attributes: self.get_custom_attributes(complex, vec!["symbols"]),
};
self.register_parsed_schema(&fully_qualified_name, &schema, &aliases);
@@ -1357,6 +1404,7 @@ impl Parser {
aliases: aliases.clone(),
doc,
size: size as usize,
+ attributes: self.get_custom_attributes(complex, vec!["size"]),
};
self.register_parsed_schema(&fully_qualified_name, &schema, &aliases);
@@ -1556,6 +1604,7 @@ impl Serialize for Schema {
aliases: None,
doc: None,
size: 12,
+ attributes: Default::default(),
};
map.serialize_entry("type", &inner)?;
map.serialize_entry("logicalType", "duration")?;
@@ -1584,11 +1633,11 @@ impl Serialize for RecordField {
/// Parses a **valid** avro schema into the Parsing Canonical Form.
/// https://avro.apache.org/docs/1.8.2/spec.html#Parsing+Canonical+Form+for+Schemas
-fn parsing_canonical_form(schema: &serde_json::Value) -> String {
+fn parsing_canonical_form(schema: &Value) -> String {
match schema {
- serde_json::Value::Object(map) => pcf_map(map),
- serde_json::Value::String(s) => pcf_string(s),
- serde_json::Value::Array(v) => pcf_array(v),
+ Value::Object(map) => pcf_map(map),
+ Value::String(s) => pcf_string(s),
+ Value::Array(v) => pcf_array(v),
json => panic!(
"got invalid JSON value for canonical form of schema: {0}",
json
@@ -1596,7 +1645,7 @@ fn parsing_canonical_form(schema: &serde_json::Value) -> String {
}
}
-fn pcf_map(schema: &Map<String, serde_json::Value>) -> String {
+fn pcf_map(schema: &Map<String, Value>) -> String {
// Look for the namespace variant up front.
let ns = schema.get("namespace").and_then(|v| v.as_str());
let mut fields = Vec::new();
@@ -1604,7 +1653,7 @@ fn pcf_map(schema: &Map<String, serde_json::Value>) -> 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 serde_json::Value::String(s) = v {
+ if let Value::String(s) = v {
return pcf_string(s);
}
}
@@ -1656,7 +1705,7 @@ fn pcf_map(schema: &Map<String, serde_json::Value>) -> String {
format!("{{{}}}", inter)
}
-fn pcf_array(arr: &[serde_json::Value]) -> String {
+fn pcf_array(arr: &[Value]) -> String {
let inter = arr
.iter()
.map(parsing_canonical_form)
@@ -1764,7 +1813,7 @@ pub mod derive {
T: AvroSchemaComponent,
{
fn get_schema() -> Schema {
- T::get_schema_in_ctxt(&mut HashMap::default(), &Option::None)
+ T::get_schema_in_ctxt(&mut HashMap::default(), &None)
}
}
@@ -1897,6 +1946,7 @@ pub mod derive {
mod tests {
use super::*;
use pretty_assertions::assert_eq;
+ use serde_json::json;
#[test]
fn test_invalid_schema() {
@@ -2024,8 +2074,10 @@ mod tests {
),
order: RecordFieldOrder::Ignore,
position: 0,
+ custom_attributes: Default::default(),
}],
lookup: BTreeMap::from_iter(vec![("field_one".to_string(), 0)]),
+ attributes: Default::default(),
};
assert_eq!(schema_c, schema_c_expected);
@@ -2111,8 +2163,10 @@ mod tests {
),
order: RecordFieldOrder::Ignore,
position: 0,
+ custom_attributes: Default::default(),
}],
lookup: BTreeMap::from_iter(vec![("field_one".to_string(), 0)]),
+ attributes: Default::default(),
};
assert_eq!(schema_option_a, schema_option_a_expected);
@@ -2150,6 +2204,7 @@ mod tests {
schema: Schema::Long,
order: RecordFieldOrder::Ascending,
position: 0,
+ custom_attributes: Default::default(),
},
RecordField {
name: "b".to_string(),
@@ -2158,9 +2213,11 @@ mod tests {
schema: Schema::String,
order: RecordFieldOrder::Ascending,
position: 1,
+ custom_attributes: Default::default(),
},
],
lookup,
+ attributes: Default::default(),
};
assert_eq!(parsed, expected);
@@ -2217,6 +2274,7 @@ mod tests {
schema: Schema::String,
order: RecordFieldOrder::Ascending,
position: 0,
+ custom_attributes: Default::default(),
},
RecordField {
name: "children".to_string(),
@@ -2227,14 +2285,18 @@ mod tests {
})),
order: RecordFieldOrder::Ascending,
position: 1,
+ custom_attributes: Default::default(),
},
],
lookup: node_lookup,
+ attributes: Default::default(),
},
order: RecordFieldOrder::Ascending,
position: 0,
+ custom_attributes: Default::default(),
}],
lookup,
+ attributes: Default::default(),
};
assert_eq!(schema, expected);
@@ -2380,6 +2442,7 @@ mod tests {
schema: Schema::Long,
order: RecordFieldOrder::Ascending,
position: 0,
+ custom_attributes: Default::default(),
},
RecordField {
name: "next".to_string(),
@@ -2399,9 +2462,11 @@ mod tests {
),
order: RecordFieldOrder::Ascending,
position: 1,
+ custom_attributes: Default::default(),
},
],
lookup,
+ attributes: Default::default(),
};
assert_eq!(schema, expected);
@@ -2446,6 +2511,7 @@ mod tests {
schema: Schema::Long,
order: RecordFieldOrder::Ascending,
position: 0,
+ custom_attributes: Default::default(),
},
RecordField {
name: "next".to_string(),
@@ -2459,9 +2525,11 @@ mod tests {
},
order: RecordFieldOrder::Ascending,
position: 1,
+ custom_attributes: Default::default(),
},
],
lookup,
+ attributes: Default::default(),
};
assert_eq!(schema, expected);
@@ -2515,9 +2583,11 @@ mod tests {
aliases: None,
doc: None,
symbols: vec!["one".to_string(), "two".to_string(), "three".to_string()],
+ attributes: Default::default(),
},
order: RecordFieldOrder::Ascending,
position: 0,
+ custom_attributes: Default::default(),
},
RecordField {
name: "next".to_string(),
@@ -2531,12 +2601,15 @@ mod tests {
aliases: None,
doc: None,
symbols: vec!["one".to_string(), "two".to_string(), "three".to_string()],
+ attributes: Default::default(),
},
order: RecordFieldOrder::Ascending,
position: 1,
+ custom_attributes: Default::default(),
},
],
lookup,
+ attributes: Default::default(),
};
assert_eq!(schema, expected);
@@ -2590,9 +2663,11 @@ mod tests {
aliases: None,
doc: None,
size: 456,
+ attributes: Default::default(),
},
order: RecordFieldOrder::Ascending,
position: 0,
+ custom_attributes: Default::default(),
},
RecordField {
name: "next".to_string(),
@@ -2606,12 +2681,15 @@ mod tests {
aliases: None,
doc: None,
size: 456,
+ attributes: Default::default(),
},
order: RecordFieldOrder::Ascending,
position: 1,
+ custom_attributes: Default::default(),
},
],
lookup,
+ attributes: Default::default(),
};
assert_eq!(schema, expected);
@@ -2636,6 +2714,7 @@ mod tests {
"clubs".to_owned(),
"hearts".to_owned(),
],
+ attributes: Default::default(),
};
assert_eq!(expected, schema);
@@ -2668,6 +2747,7 @@ mod tests {
aliases: None,
doc: None,
size: 16usize,
+ attributes: Default::default(),
};
assert_eq!(expected, schema);
@@ -2685,6 +2765,7 @@ mod tests {
aliases: None,
doc: Some(String::from("FixedSchema documentation")),
size: 16usize,
+ attributes: Default::default(),
};
assert_eq!(expected, schema);
@@ -3819,4 +3900,177 @@ mod tests {
panic!("Expected Schema::Record");
}
}
+
+ #[test]
+ fn avro_custom_attributes_schema_without_attributes() {
+ let schemata_str = [
+ r#"
+ {
+ "type": "record",
+ "name": "Rec",
+ "doc": "A Record schema without custom attributes",
+ "fields": []
+ }
+ "#,
+ r#"
+ {
+ "type": "enum",
+ "name": "Enum",
+ "doc": "An Enum schema without custom attributes",
+ "symbols": []
+ }
+ "#,
+ r#"
+ {
+ "type": "fixed",
+ "name": "Fixed",
+ "doc": "A Fixed schema without custom attributes",
+ "size": 0
+ }
+ "#,
+ ];
+ for schema_str in schemata_str.iter() {
+ let schema = Schema::parse_str(schema_str).unwrap();
+ assert_eq!(schema.custom_attributes(), Some(&Default::default()));
+ }
+ }
+
+ #[test]
+ fn avro_3609_custom_attributes_schema_with_attributes() {
+ let custom_attrs_suffix = r#"
+ "string_key": "value",
+ "number_key": 1.23,
+ "null_key": null,
+ "array_key": [1, 2, 3],
+ "object_key": {
+ "key": "value"
+ }
+ }
+ "#;
+ let schemata_str = [
+ r#"
+ {
+ "type": "record",
+ "name": "Rec",
+ "namespace": "ns",
+ "doc": "A Record schema with custom attributes",
+ "fields": [],
+ "#,
+ r#"
+ {
+ "type": "enum",
+ "name": "Enum",
+ "namespace": "ns",
+ "doc": "An Enum schema with custom attributes",
+ "symbols": [],
+ "#,
+ r#"
+ {
+ "type": "fixed",
+ "name": "Fixed",
+ "namespace": "ns",
+ "doc": "A Fixed schema with custom attributes",
+ "size": 2,
+ "#,
+ ];
+
+ for schema_str in schemata_str.iter() {
+ let schema =
+ Schema::parse_str(format!("{}{}", schema_str, custom_attrs_suffix).as_str())
+ .unwrap();
+
+ assert_eq!(
+ schema.custom_attributes(),
+ Some(&expected_custom_attibutes())
+ );
+ }
+ }
+
+ fn expected_custom_attibutes() -> BTreeMap<String, Value> {
+ let mut expected_attibutes: BTreeMap<String, Value> = Default::default();
+ expected_attibutes.insert("string_key".to_string(), Value::String("value".to_string()));
+ expected_attibutes.insert("number_key".to_string(), json!(1.23));
+ expected_attibutes.insert("null_key".to_string(), Value::Null);
+ expected_attibutes.insert(
+ "array_key".to_string(),
+ Value::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()));
+ expected_attibutes.insert("object_key".to_string(), json!(object_value));
+ expected_attibutes
+ }
+
+ #[test]
+ fn avro_3609_custom_attributes_record_field_without_attributes() {
+ let schema_str = r#"
+ {
+ "type": "record",
+ "name": "Rec",
+ "doc": "A Record schema without custom attributes",
+ "fields": [
+ {
+ "name": "field_one",
+ "type": "float",
+ "string_key": "value",
+ "number_key": 1.23,
+ "null_key": null,
+ "array_key": [1, 2, 3],
+ "object_key": {
+ "key": "value"
+ }
+ }
+ ]
+ }
+ "#;
+
+ let schema = Schema::parse_str(schema_str).unwrap();
+
+ match schema {
+ Schema::Record { name, fields, .. } => {
+ assert_eq!(name, Name::new("Rec").unwrap());
+ assert_eq!(fields.len(), 1);
+ let field = &fields[0];
+ assert_eq!(&field.name, "field_one");
+ assert_eq!(field.custom_attributes, expected_custom_attibutes());
+ }
+ _ => panic!("Expected Schema::Record"),
+ }
+ }
+
+ // #[test]
+ #[allow(dead_code)]
+ // TODO: Do we want to support serializing the custom attributes?
+ fn avro_3609_custom_attributes_serialize_record_field() {
+ let schema_str = r#"
+ {
+ "type": "record",
+ "name": "Rec",
+ "doc": "A Record schema without custom attributes",
+ "fields": [
+ {
+ "name": "field_one",
+ "type": "float",
+ "string_key": "value",
+ "number_key": 1.23,
+ "null_key": null,
+ "array_key": [1, 2, 3],
+ "object_key": {
+ "key": "value"
+ }
+ }
+ ]
+ }
+ "#;
+
+ let schema = Schema::parse_str(schema_str).unwrap();
+
+ let value = serde_json::to_value(&schema).unwrap();
+ let serialized = serde_json::to_string(&value).unwrap();
+ assert_eq!(
+ r#"{"doc":"A Record schema without custom attributes","fields":[{"name":"field_one","type":"float"}],"name":"Rec","type":"record"}"#,
+ &serialized
+ );
+ assert_eq!(schema, Schema::parse_str(&serialized).unwrap());
+ }
}
diff --git a/lang/rust/avro/src/schema_compatibility.rs b/lang/rust/avro/src/schema_compatibility.rs
index 43fbbbe8d..843a139df 100644
--- a/lang/rust/avro/src/schema_compatibility.rs
+++ b/lang/rust/avro/src/schema_compatibility.rs
@@ -235,6 +235,7 @@ impl SchemaCompatibility {
aliases: _,
doc: _w_doc,
size: w_size,
+ attributes: _,
} = writers_schema
{
if let Schema::Fixed {
@@ -242,6 +243,7 @@ impl SchemaCompatibility {
aliases: _,
doc: _r_doc,
size: r_size,
+ attributes: _,
} = readers_schema
{
return w_name.fullname(None) == r_name.fullname(None)
diff --git a/lang/rust/avro/src/types.rs b/lang/rust/avro/src/types.rs
index 042350b93..e833a26b7 100644
--- a/lang/rust/avro/src/types.rs
+++ b/lang/rust/avro/src/types.rs
@@ -1037,11 +1037,13 @@ mod tests {
schema: Schema::Int,
order: RecordFieldOrder::Ignore,
position: 0,
+ custom_attributes: Default::default(),
}],
lookup: Default::default(),
+ attributes: Default::default(),
},
false,
- "Invalid value: Record([(\"unknown_field_name\", Null)]) for schema: Record { name: Name { name: \"record_name\", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: \"field_name\", doc: None, default: None, schema: Int, order: Ignore, position: 0 }], lookup: {} }. Reason: There is no schema field for field 'unknown_field_name'",
+ "Invalid value: Record([(\"unknown_field_name\", Null)]) for schema: Record { name: Name { name: \"record_name\", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: \"field_name\", doc: None, default: None, schema: Int, order: Ignore, position: 0, custom_attributes: {} }], lookup: {}, attributes: {} }. Reason: There is no schema field for field 'unknown_field_name'",
),
(
Value::Record(vec![("field_name".to_string(), Value::Null)]),
@@ -1058,11 +1060,13 @@ mod tests {
},
order: RecordFieldOrder::Ignore,
position: 0,
+ custom_attributes: Default::default(),
}],
lookup: [("field_name".to_string(), 0)].iter().cloned().collect(),
+ attributes: Default::default(),
},
false,
- "Invalid value: Record([(\"field_name\", Null)]) for schema: Record { name: Name { name: \"record_name\", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: \"field_name\", doc: None, default: None, schema: Ref { name: Name { name: \"missing\", namespace: None } }, order: Ignore, position: 0 }], lookup: {\"field_name\": 0} }. Reason: Unresolved schema reference: 'missing'. Parsed names: []",
+ "Invalid value: Record([(\"field_name\", Null)]) for schema: Record { name: Name { name: \"record_name\", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: \"field_name\", doc: None, default: None, schema: Ref { name: Name { name: \"missing\", namespace: None } }, order: Ignore, position: 0, custom_attributes: {} }], lookup: {\"field_name\": 0}, attributes: {} }. Reason: Unresolved schema reference: 'missing'. Parsed names: []",
),
];
@@ -1088,6 +1092,7 @@ mod tests {
name: Name::new("some_fixed").unwrap(),
aliases: None,
doc: None,
+ attributes: Default::default(),
};
assert!(Value::Fixed(4, vec![0, 0, 0, 0]).validate(&schema));
@@ -1125,6 +1130,7 @@ mod tests {
"diamonds".to_string(),
"clubs".to_string(),
],
+ attributes: Default::default(),
};
assert!(Value::Enum(0, "spades".to_string()).validate(&schema));
@@ -1170,6 +1176,7 @@ mod tests {
"clubs".to_string(),
"spades".to_string(),
],
+ attributes: Default::default(),
};
let value = Value::Enum(0, "spades".to_string());
@@ -1204,6 +1211,7 @@ mod tests {
schema: Schema::Long,
order: RecordFieldOrder::Ascending,
position: 0,
+ custom_attributes: Default::default(),
},
RecordField {
name: "b".to_string(),
@@ -1212,12 +1220,14 @@ mod tests {
schema: Schema::String,
order: RecordFieldOrder::Ascending,
position: 1,
+ custom_attributes: Default::default(),
},
],
lookup: [("a".to_string(), 0), ("b".to_string(), 1)]
.iter()
.cloned()
.collect(),
+ attributes: Default::default(),
};
assert!(Value::Record(vec![
@@ -1237,7 +1247,7 @@ mod tests {
("b".to_string(), Value::String("foo".to_string())),
]);
assert!(!value.validate(&schema));
- assert_logged("Invalid value: Record([(\"a\", Boolean(false)), (\"b\", String(\"foo\"))]) for schema: Record { name: Name { name: \"some_record\", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: \"a\", doc: None, default: None, schema: Long, order: Ascending, position: 0 }, RecordField { name: \"b\", doc: None, default: None, schema: String, order: Ascending, position: 1 }], lookup: {\"a\": 0, \"b\": 1} }. Reason: Unsupported value-schema combination");
+ assert_logged("Invalid value: Record([(\"a\", Boolean(false)), (\"b\", String(\"foo\"))]) for schema: Record { name: Name { name: \"some_record\", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: \"a\", doc: None, default: None, schema: Long, order: Ascending, position: 0, custom_attributes: {} }, RecordField { name: \"b\", doc: None, default: None, schema: String, order: Ascending, position: 1, custom_attributes: {} }], lookup: {\"a\": 0, \"b\": 1}, attr [...]
let value = Value::Record(vec![
("a".to_string(), Value::Long(42i64)),
@@ -1245,7 +1255,7 @@ mod tests {
]);
assert!(!value.validate(&schema));
assert_logged(
- "Invalid value: Record([(\"a\", Long(42)), (\"c\", String(\"foo\"))]) for schema: Record { name: Name { name: \"some_record\", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: \"a\", doc: None, default: None, schema: Long, order: Ascending, position: 0 }, RecordField { name: \"b\", doc: None, default: None, schema: String, order: Ascending, position: 1 }], lookup: {\"a\": 0, \"b\": 1} }. Reason: There is no schema field for field 'c'"
+ "Invalid value: Record([(\"a\", Long(42)), (\"c\", String(\"foo\"))]) for schema: Record { name: Name { name: \"some_record\", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: \"a\", doc: None, default: None, schema: Long, order: Ascending, position: 0, custom_attributes: {} }, RecordField { name: \"b\", doc: None, default: None, schema: String, order: Ascending, position: 1, custom_attributes: {} }], lookup: {\"a\": 0, \"b\": 1}, attributes: {} }. Re [...]
);
let value = Value::Record(vec![
@@ -1255,7 +1265,7 @@ mod tests {
]);
assert!(!value.validate(&schema));
assert_logged(
- r#"Invalid value: Record([("a", Long(42)), ("b", String("foo")), ("c", Null)]) for schema: Record { name: Name { name: "some_record", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: "a", doc: None, default: None, schema: Long, order: Ascending, position: 0 }, RecordField { name: "b", doc: None, default: None, schema: String, order: Ascending, position: 1 }], lookup: {"a": 0, "b": 1} }. Reason: The value's records length (3) is different than the sche [...]
+ r#"Invalid value: Record([("a", Long(42)), ("b", String("foo")), ("c", Null)]) for schema: Record { name: Name { name: "some_record", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: "a", doc: None, default: None, schema: Long, order: Ascending, position: 0, custom_attributes: {} }, RecordField { name: "b", doc: None, default: None, schema: String, order: Ascending, position: 1, custom_attributes: {} }], lookup: {"a": 0, "b": 1}, attributes: {} }. Rea [...]
);
assert!(Value::Map(
@@ -1275,7 +1285,7 @@ mod tests {
)
.validate(&schema));
assert_logged(
- r#"Invalid value: Map({"c": Long(123)}) for schema: Record { name: Name { name: "some_record", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: "a", doc: None, default: None, schema: Long, order: Ascending, position: 0 }, RecordField { name: "b", doc: None, default: None, schema: String, order: Ascending, position: 1 }], lookup: {"a": 0, "b": 1} }. Reason: Field with name '"a"' is not a member of the map items
+ r#"Invalid value: Map({"c": Long(123)}) for schema: Record { name: Name { name: "some_record", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: "a", doc: None, default: None, schema: Long, order: Ascending, position: 0, custom_attributes: {} }, RecordField { name: "b", doc: None, default: None, schema: String, order: Ascending, position: 1, custom_attributes: {} }], lookup: {"a": 0, "b": 1}, attributes: {} }. Reason: Field with name '"a"' is not a mem [...]
Field with name '"b"' is not a member of the map items"#,
);
@@ -1387,7 +1397,8 @@ Field with name '"b"' is not a member of the map items"#,
name: Name::new("decimal").unwrap(),
aliases: None,
size: 20,
- doc: None
+ doc: None,
+ attributes: Default::default(),
})
})
.is_ok());
diff --git a/lang/rust/avro/src/writer.rs b/lang/rust/avro/src/writer.rs
index 2fc42cccf..98ceafe46 100644
--- a/lang/rust/avro/src/writer.rs
+++ b/lang/rust/avro/src/writer.rs
@@ -686,6 +686,7 @@ mod tests {
aliases: None,
doc: None,
size,
+ attributes: Default::default(),
};
let value = vec![0u8; size];
logical_type_test(
@@ -725,6 +726,7 @@ mod tests {
aliases: None,
doc: None,
size: 12,
+ attributes: Default::default(),
};
let value = Value::Duration(Duration::new(
Months::new(256),
diff --git a/lang/rust/avro/tests/schema.rs b/lang/rust/avro/tests/schema.rs
index b4f59e553..044a78e55 100644
--- a/lang/rust/avro/tests/schema.rs
+++ b/lang/rust/avro/tests/schema.rs
@@ -860,6 +860,7 @@ fn test_parse_reused_record_schema_by_fullname() {
doc: _,
ref fields,
lookup: _,
+ attributes: _,
} => {
assert_eq!(name.fullname(None), "test.Weather", "Name does not match!");
@@ -872,6 +873,7 @@ fn test_parse_reused_record_schema_by_fullname() {
ref schema,
order: _,
position: _,
+ custom_attributes: _,
} = fields.get(2).unwrap();
assert_eq!(name, "min_temp");
diff --git a/lang/rust/avro_derive/src/lib.rs b/lang/rust/avro_derive/src/lib.rs
index abba0e195..6f9115056 100644
--- a/lang/rust/avro_derive/src/lib.rs
+++ b/lang/rust/avro_derive/src/lib.rs
@@ -147,6 +147,7 @@ fn get_data_struct_schema_def(
schema: #schema_expr,
order: apache_avro::schema::RecordFieldOrder::Ascending,
position: #position,
+ custom_attributes: Default::default(),
}
});
}
@@ -179,6 +180,7 @@ fn get_data_struct_schema_def(
doc: #record_doc,
fields: schema_fields,
lookup,
+ attributes: Default::default(),
}
})
}
@@ -204,6 +206,7 @@ fn get_data_enum_schema_def(
aliases: #enum_aliases,
doc: #doc,
symbols: vec![#(#symbols.to_owned()),*],
+ attributes: Default::default(),
}
})
} else {