This is an automated email from the ASF dual-hosted git repository. Kriskras99 pushed a commit to branch feat/value_supply_own_name in repository https://gitbox.apache.org/repos/asf/avro-rs.git
commit 7fda1076ab2c2d351f0e65631165d1b5ec8b5e24 Author: Kriskras99 <[email protected]> AuthorDate: Mon Jul 6 22:20:45 2026 +0200 feat: Allow to supply own names when using `Value::resolve` and `Value::Validate` --- avro/src/reader/datum.rs | 2 +- avro/src/schema/parser.rs | 2 +- avro/src/schema/record/field.rs | 4 +- avro/src/schema/union.rs | 6 +-- avro/src/types.rs | 104 +++++++++++++++++++++++++++++++++------- 5 files changed, 93 insertions(+), 25 deletions(-) diff --git a/avro/src/reader/datum.rs b/avro/src/reader/datum.rs index fa692e1..4f0c31e 100644 --- a/avro/src/reader/datum.rs +++ b/avro/src/reader/datum.rs @@ -132,7 +132,7 @@ impl<'s> GenericDatumReader<'s> { pub fn read_value<R: Read>(&self, reader: &mut R) -> AvroResult<Value> { let value = decode_internal(self.writer, self.resolved.get_names(), None, reader)?; if let Some((reader, resolved)) = &self.reader { - value.resolve_internal(reader, resolved.get_names(), None, &None) + value.resolve_internal(reader, resolved.get_names(), None, None) } else { Ok(value) } diff --git a/avro/src/schema/parser.rs b/avro/src/schema/parser.rs index 2310e08..f84766f 100644 --- a/avro/src/schema/parser.rs +++ b/avro/src/schema/parser.rs @@ -662,7 +662,7 @@ impl Parser { if let Some(ref value) = default { let resolved = types::Value::from(value.clone()) - .resolve_enum(&symbols, &Some(value.to_string()), &None) + .resolve_enum(&symbols, &Some(value.to_string()), None) .is_ok(); if !resolved { return Err(Details::GetEnumDefault { diff --git a/avro/src/schema/record/field.rs b/avro/src/schema/record/field.rs index 10f8856..2bd6468 100644 --- a/avro/src/schema/record/field.rs +++ b/avro/src/schema/record/field.rs @@ -146,7 +146,7 @@ impl RecordField { let resolved = schemas.iter().any(|schema| { avro_value .to_owned() - .resolve_internal(schema, names, schema.namespace(), &None) + .resolve_internal(schema, names, schema.namespace(), None) .is_ok() }); @@ -163,7 +163,7 @@ impl RecordField { } } else { let resolved = avro_value - .resolve_internal(field_schema, names, field_schema.namespace(), &None) + .resolve_internal(field_schema, names, field_schema.namespace(), None) .is_ok(); if !resolved { diff --git a/avro/src/schema/union.rs b/avro/src/schema/union.rs index 1561c11..ca9a997 100644 --- a/avro/src/schema/union.rs +++ b/avro/src/schema/union.rs @@ -251,7 +251,7 @@ impl UnionSchema { // TODO: Do this without the clone value .clone() - .resolve_internal(schema, known_schemata, namespace, &None) + .resolve_internal(schema, known_schemata, namespace, None) .ok() .map(|_| (index, schema)) } else { @@ -275,7 +275,7 @@ impl UnionSchema { // TODO: Do this without the clone value .clone() - .resolve_internal(schema, known_schemata, namespace, &None) + .resolve_internal(schema, known_schemata, namespace, None) .is_ok() }) }); @@ -293,7 +293,7 @@ impl UnionSchema { // TODO: Do this without the clone value .clone() - .resolve_internal(schema, known_schemata, namespace, &None) + .resolve_internal(schema, known_schemata, namespace, None) .is_ok() }) } diff --git a/avro/src/types.rs b/avro/src/types.rs index ad0258b..19fee49 100644 --- a/avro/src/types.rs +++ b/avro/src/types.rs @@ -370,6 +370,10 @@ impl TryFrom<Value> for JsonValue { impl Value { /// Validate the value against the given [`Schema`]. /// + /// Note: This will first build a reference map from the schema (see [`ResolvedSchema`]). If this + /// function is called more than once for the same schema, it is recommended to use + /// [`validate_with_names`](Self::validate_with_names) instead. + /// /// See the [Avro specification](https://avro.apache.org/docs/++version++/specification) /// for the full set of rules of schema validation. /// @@ -381,6 +385,13 @@ impl Value { /// Validate the value against the given schemata. /// + /// Note: This will first build a reference map from the schema (see [`ResolvedSchema`]). If this + /// function is called more than once for the same schema, it is recommended to use + /// [`validate_with_names`](Self::validate_with_names) instead. + /// + /// See the [Avro specification](https://avro.apache.org/docs/++version++/specification) + /// for the full set of rules of schema validation. + /// /// # Panics /// Will panic if the schemata contain unresolved references or duplicate schemas. pub fn validate_schemata(&self, schemata: &[&Schema]) -> bool { @@ -404,6 +415,26 @@ impl Value { ) } + /// Validate the value against the given schema using `names` to resolve any references. + /// + /// See the [Avro specification](https://avro.apache.org/docs/++version++/specification) + /// for the full set of rules of schema validation. + pub fn validate_with_names<S: Borrow<Schema> + Debug>( + &self, + schema: &Schema, + names: &HashMap<Name, S>, + ) -> bool { + match self.validate_internal(schema, names, None) { + Some(reason) => { + let log_message = + format!("Invalid value: {self:?} for schema: {schema:?}. Reason: {reason}"); + error!("{log_message}"); + false + } + None => true, + } + } + fn accumulate(accumulator: Option<String>, other: Option<String>) -> Option<String> { match (accumulator, other) { (None, None) => None, @@ -656,30 +687,62 @@ impl Value { } } - /// Attempt to perform schema resolution on the value, with the given - /// [Schema](../schema/enum.Schema.html). + /// Resolve this value into a new schema. + /// + /// Note: This will first build a reference map from the schema (see [`ResolvedSchema`]). If this + /// function is called more than once for the same schema, it is recommended to use + /// [`resolve_with_names`](Self::resolve_with_names) instead. /// /// See [Schema Resolution](https://avro.apache.org/docs/++version++/specification/#schema-resolution) - /// in the Avro specification for the full set of rules of schema - /// resolution. + /// in the Avro specification for the full set of rules of schema resolution. pub fn resolve(self, schema: &Schema) -> AvroResult<Self> { self.resolve_schemata(schema, Vec::with_capacity(0)) } - /// Attempt to perform schema resolution on the value, with the given - /// [Schema](../schema/enum.Schema.html) and set of schemas to use for Refs resolution. + /// Resolve this value into a new schema using `schemata` to resolve any references. + /// + /// `schemata` must contain all referenced schemas in `schema`, including references to parts of + /// `schema` itself. + /// + /// Note: This will first build a reference map from the schema (see [`ResolvedSchema`]). If this + /// function is called more than once for the same schema, it is recommended to use + /// [`resolve_with_names`](Self::resolve_with_names) instead. /// /// See [Schema Resolution](https://avro.apache.org/docs/++version++/specification/#schema-resolution) - /// in the Avro specification for the full set of rules of schema - /// resolution. + /// in the Avro specification for the full set of rules of schema resolution. pub fn resolve_schemata(self, schema: &Schema, schemata: Vec<&Schema>) -> AvroResult<Self> { - let enclosing_namespace = schema.namespace(); let rs = if schemata.is_empty() { ResolvedSchema::try_from(schema)? } else { ResolvedSchema::try_from(schemata)? }; - self.resolve_internal(schema, rs.get_names(), enclosing_namespace, &None) + self.resolve_internal(schema, rs.get_names(), None, None) + } + + /// Resolve this value into a new schema using `names` to resolve any references. + /// + /// See [Schema Resolution](https://avro.apache.org/docs/++version++/specification/#schema-resolution) + /// in the Avro specification for the full set of rules of schema resolution. + /// + /// # Example + /// ``` + /// # use apache_avro::{types::Value, Error, Schema, schema::ResolvedSchema}; + /// # let values: [Value; 0] = []; + /// # let schema = Schema::Null; + /// let resolved_schema = ResolvedSchema::new(&schema)?; + /// + /// for value in values { + /// let resolved = value.resolve_with_names(&schema, resolved_schema.get_names())?; + /// // Do something with the resolved value + /// } + /// # Ok::<(), Error>(()) + /// ``` + pub fn resolve_with_names<S: Borrow<Schema> + Debug>( + self, + schema: &Schema, + names: &HashMap<Name, S>, + ) -> AvroResult<Self> { + self.resolve_internal(schema, names, None, None) } pub(crate) fn resolve_internal<S: Borrow<Schema> + Debug>( @@ -687,7 +750,7 @@ impl Value { schema: &Schema, names: &HashMap<Name, S>, enclosing_namespace: NamespaceRef, - field_default: &Option<JsonValue>, + field_default: Option<&JsonValue>, ) -> AvroResult<Self> { // Check if this schema is a union, and if the reader schema is not. if SchemaKind::from(&self) == SchemaKind::Union @@ -1045,7 +1108,7 @@ impl Value { self, symbols: &[String], enum_default: &Option<String>, - _field_default: &Option<JsonValue>, + _field_default: Option<&JsonValue>, ) -> Result<Self, Error> { let validate_symbol = |symbol: String, symbols: &[String]| { if let Some(index) = symbols.iter().position(|item| item == &symbol) { @@ -1084,7 +1147,7 @@ impl Value { schema: &UnionSchema, names: &HashMap<Name, S>, enclosing_namespace: NamespaceRef, - field_default: &Option<JsonValue>, + field_default: Option<&JsonValue>, ) -> Result<Self, Error> { let v = match self { // Both are unions case. @@ -1115,7 +1178,7 @@ impl Value { Value::Array(items) => Ok(Value::Array( items .into_iter() - .map(|item| item.resolve_internal(schema, names, enclosing_namespace, &None)) + .map(|item| item.resolve_internal(schema, names, enclosing_namespace, None)) .collect::<Result<_, _>>()?, )), other => Err(Details::GetArray { @@ -1138,7 +1201,7 @@ impl Value { .into_iter() .map(|(key, value)| { value - .resolve_internal(schema, names, enclosing_namespace, &None) + .resolve_internal(schema, names, enclosing_namespace, None) .map(|value| (key, value)) }) .collect::<Result<_, _>>()?, @@ -1183,7 +1246,7 @@ impl Value { }) => Value::try_from(value.clone())?.resolve_enum( symbols, default, - &field.default.clone(), + field.default.as_ref(), )?, Schema::Union(ref union_schema) => { let first = &union_schema.variants()[0]; @@ -1198,7 +1261,7 @@ impl Value { first, names, enclosing_namespace, - &field.default, + field.default.as_ref(), )?, ), ), @@ -1212,7 +1275,12 @@ impl Value { }, }; value - .resolve_internal(&field.schema, names, enclosing_namespace, &field.default) + .resolve_internal( + &field.schema, + names, + enclosing_namespace, + field.default.as_ref(), + ) .map(|value| (field.name.clone(), value)) }) .collect::<Result<Vec<_>, _>>()?;
