blob: 3e31b967f1a9c6de7cdaece06b1bc76d25fe3ffd [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//! # Custom schema equality comparators
//!
//! The library provides two implementations of schema equality comparators:
//! 1. `StructFieldEq` (default) - compares the schemas structurally, may slightly deviate from the specification.
//! 2. `SpecificationEq` - compares the schemas by serializing them to their canonical form and comparing
//! the resulting JSON.
//!
//! To use a custom comparator, you need to implement the `SchemataEq` trait and set it using the
//! `set_schemata_equality_comparator` function:
//!
//! ```
//! use apache_avro::{AvroResult, Schema};
//! use apache_avro::schema::Namespace;
//! use apache_avro::schema_equality::{SchemataEq, set_schemata_equality_comparator};
//!
//! #[derive(Debug)]
//! struct MyCustomSchemataEq;
//!
//! impl SchemataEq for MyCustomSchemataEq {
//! fn compare(&self, schema_one: &Schema, schema_two: &Schema) -> bool {
//! todo!()
//! }
//! }
//!
//! // don't parse any schema before registering the custom comparator!
//!
//! set_schemata_equality_comparator(Box::new(MyCustomSchemataEq));
//!
//! // ... use the library
//! ```
//! **Note**: the library allows to set a comparator only once per the application lifetime!
//! If the application parses schemas before setting a comparator, the default comparator will be
//! registered and used!
use crate::schema::{InnerDecimalSchema, UuidSchema};
use crate::{
Schema,
schema::{
ArraySchema, DecimalSchema, EnumSchema, FixedSchema, MapSchema, RecordField, RecordSchema,
UnionSchema,
},
};
use log::debug;
use std::{fmt::Debug, sync::OnceLock};
/// A trait that compares two schemata for equality.
///
/// To register a custom one use [`set_schemata_equality_comparator`].
pub trait SchemataEq: Debug + Send + Sync {
/// Compares two schemata for equality.
fn compare(&self, schema_one: &Schema, schema_two: &Schema) -> bool;
}
/// Compares two schemas according to the Avro specification by using [their canonical forms].
///
/// [their canonical forms](https://avro.apache.org/docs/1.11.1/specification/#parsing-canonical-form-for-schemas)
#[derive(Debug)]
pub struct SpecificationEq;
impl SchemataEq for SpecificationEq {
fn compare(&self, schema_one: &Schema, schema_two: &Schema) -> bool {
schema_one.canonical_form() == schema_two.canonical_form()
}
}
/// Compares [the canonical forms] of two schemas for equality field by field.
///
/// This means that attributes like `aliases`, `doc`, `default` and `logicalType` are ignored.
///
/// [the canonical forms](https://avro.apache.org/docs/1.11.1/specification/#parsing-canonical-form-for-schemas)
#[derive(Debug)]
pub struct StructFieldEq {
/// Whether to include custom attributes in the comparison.
///
/// The custom attributes are not used to construct the canonical form of the schema!
pub include_attributes: bool,
}
impl SchemataEq for StructFieldEq {
#[rustfmt::skip]
fn compare(&self, schema_one: &Schema, schema_two: &Schema) -> bool {
if schema_one.name() != schema_two.name() {
return false;
}
if self.include_attributes
&& schema_one.custom_attributes() != schema_two.custom_attributes()
{
return false;
}
match (schema_one, schema_two) {
(Schema::Null, Schema::Null) => true,
(Schema::Null, _) => false,
(Schema::Boolean, Schema::Boolean) => true,
(Schema::Boolean, _) => false,
(Schema::Int, Schema::Int) => true,
(Schema::Int, _) => false,
(Schema::Long, Schema::Long) => true,
(Schema::Long, _) => false,
(Schema::Float, Schema::Float) => true,
(Schema::Float, _) => false,
(Schema::Double, Schema::Double) => true,
(Schema::Double, _) => false,
(Schema::Bytes, Schema::Bytes) => true,
(Schema::Bytes, _) => false,
(Schema::String, Schema::String) => true,
(Schema::String, _) => false,
(Schema::BigDecimal, Schema::BigDecimal) => true,
(Schema::BigDecimal, _) => false,
(Schema::Date, Schema::Date) => true,
(Schema::Date, _) => false,
(Schema::TimeMicros, Schema::TimeMicros) => true,
(Schema::TimeMicros, _) => false,
(Schema::TimeMillis, Schema::TimeMillis) => true,
(Schema::TimeMillis, _) => false,
(Schema::TimestampMicros, Schema::TimestampMicros) => true,
(Schema::TimestampMicros, _) => false,
(Schema::TimestampMillis, Schema::TimestampMillis) => true,
(Schema::TimestampMillis, _) => false,
(Schema::TimestampNanos, Schema::TimestampNanos) => true,
(Schema::TimestampNanos, _) => false,
(Schema::LocalTimestampMicros, Schema::LocalTimestampMicros) => true,
(Schema::LocalTimestampMicros, _) => false,
(Schema::LocalTimestampMillis, Schema::LocalTimestampMillis) => true,
(Schema::LocalTimestampMillis, _) => false,
(Schema::LocalTimestampNanos, Schema::LocalTimestampNanos) => true,
(Schema::LocalTimestampNanos, _) => false,
(
Schema::Record(RecordSchema { fields: fields_one, ..}),
Schema::Record(RecordSchema { fields: fields_two, ..})
) => {
self.compare_fields(fields_one, fields_two)
}
(Schema::Record(_), _) => false,
(
Schema::Enum(EnumSchema { symbols: symbols_one, ..}),
Schema::Enum(EnumSchema { symbols: symbols_two, .. })
) => {
symbols_one == symbols_two
}
(Schema::Enum(_), _) => false,
(
Schema::Fixed(FixedSchema { size: size_one, ..}),
Schema::Fixed(FixedSchema { size: size_two, .. })
) => {
size_one == size_two
}
(Schema::Fixed(_), _) => false,
(
Schema::Union(UnionSchema { schemas: schemas_one, ..}),
Schema::Union(UnionSchema { schemas: schemas_two, .. })
) => {
schemas_one.len() == schemas_two.len()
&& schemas_one
.iter()
.zip(schemas_two.iter())
.all(|(s1, s2)| self.compare(s1, s2))
}
(Schema::Union(_), _) => false,
(
Schema::Decimal(DecimalSchema { precision: precision_one, scale: scale_one, inner: inner_one }),
Schema::Decimal(DecimalSchema { precision: precision_two, scale: scale_two, inner: inner_two })
) => {
precision_one == precision_two && scale_one == scale_two && match (inner_one, inner_two) {
(InnerDecimalSchema::Bytes, InnerDecimalSchema::Bytes) => true,
(InnerDecimalSchema::Fixed(FixedSchema { size: size_one, .. }), InnerDecimalSchema::Fixed(FixedSchema { size: size_two, ..})) => {
size_one == size_two
}
_ => false,
}
}
(Schema::Decimal(_), _) => false,
(Schema::Uuid(UuidSchema::Bytes), Schema::Uuid(UuidSchema::Bytes)) => true,
(Schema::Uuid(UuidSchema::Bytes), _) => false,
(Schema::Uuid(UuidSchema::String), Schema::Uuid(UuidSchema::String)) => true,
(Schema::Uuid(UuidSchema::String), _) => false,
(Schema::Uuid(UuidSchema::Fixed(FixedSchema { size: size_one, ..})), Schema::Uuid(UuidSchema::Fixed(FixedSchema { size: size_two, ..}))) => {
size_one == size_two
},
(Schema::Uuid(UuidSchema::Fixed(_)), _) => false,
(
Schema::Array(ArraySchema { items: items_one, ..}),
Schema::Array(ArraySchema { items: items_two, ..})
) => {
self.compare(items_one, items_two)
}
(Schema::Array(_), _) => false,
(Schema::Duration(FixedSchema { size: size_one, ..}), Schema::Duration(FixedSchema { size: size_two, ..})) => size_one == size_two,
(Schema::Duration(_), _) => false,
(
Schema::Map(MapSchema { types: types_one, ..}),
Schema::Map(MapSchema { types: types_two, ..})
) => {
self.compare(types_one, types_two)
}
(Schema::Map(_), _) => false,
(
Schema::Ref { name: name_one },
Schema::Ref { name: name_two }
) => {
name_one == name_two
}
(Schema::Ref { .. }, _) => false,
}
}
}
impl StructFieldEq {
fn compare_fields(&self, fields_one: &[RecordField], fields_two: &[RecordField]) -> bool {
fields_one.len() == fields_two.len()
&& fields_one
.iter()
.zip(fields_two.iter())
.all(|(f1, f2)| f1.name == f2.name && self.compare(&f1.schema, &f2.schema))
}
}
static SCHEMATA_COMPARATOR_ONCE: OnceLock<Box<dyn SchemataEq>> = OnceLock::new();
/// Sets a custom schemata equality comparator.
///
/// Returns a unit if the registration was successful or the already
/// registered comparator if the registration failed.
///
/// **Note**: This function must be called before parsing any schema because this will
/// register the default comparator and the registration is one time only!
pub fn set_schemata_equality_comparator(
comparator: Box<dyn SchemataEq>,
) -> Result<(), Box<dyn SchemataEq>> {
debug!("Setting a custom schemata equality comparator: {comparator:?}.");
SCHEMATA_COMPARATOR_ONCE.set(comparator)
}
pub(crate) fn compare_schemata(schema_one: &Schema, schema_two: &Schema) -> bool {
SCHEMATA_COMPARATOR_ONCE
.get_or_init(|| {
debug!("Going to use the default schemata equality comparator: StructFieldEq.",);
Box::new(StructFieldEq {
include_attributes: false,
})
})
.compare(schema_one, schema_two)
}
#[cfg(test)]
#[allow(non_snake_case)]
mod tests {
use super::*;
use crate::schema::{InnerDecimalSchema, Name};
use apache_avro_test_helper::TestResult;
use serde_json::Value;
use std::collections::BTreeMap;
const SPECIFICATION_EQ: SpecificationEq = SpecificationEq;
const STRUCT_FIELD_EQ: StructFieldEq = StructFieldEq {
include_attributes: false,
};
const STRUCT_FIELD_EQ_WITH_ATTRS: StructFieldEq = StructFieldEq {
include_attributes: true,
};
macro_rules! test_primitives {
($primitive:ident) => {
paste::item! {
#[test]
fn [<test_avro_3939_compare_schemata_$primitive>]() {
let specification_eq_res = SPECIFICATION_EQ.compare(&Schema::$primitive, &Schema::$primitive);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&Schema::$primitive, &Schema::$primitive);
assert_eq!(specification_eq_res, struct_field_eq_res)
}
}
};
}
test_primitives!(Null);
test_primitives!(Boolean);
test_primitives!(Int);
test_primitives!(Long);
test_primitives!(Float);
test_primitives!(Double);
test_primitives!(Bytes);
test_primitives!(String);
test_primitives!(BigDecimal);
test_primitives!(Date);
test_primitives!(TimeMicros);
test_primitives!(TimeMillis);
test_primitives!(TimestampMicros);
test_primitives!(TimestampMillis);
test_primitives!(TimestampNanos);
test_primitives!(LocalTimestampMicros);
test_primitives!(LocalTimestampMillis);
test_primitives!(LocalTimestampNanos);
#[test]
fn avro_rs_382_compare_schemata_duration_equal() {
let schema_one = Schema::Duration(FixedSchema {
name: Name::try_from("name1").expect("Name is valid"),
size: 12,
aliases: None,
doc: None,
attributes: BTreeMap::new(),
});
let schema_two = Schema::Duration(FixedSchema {
name: Name::try_from("name1").expect("Name is valid"),
size: 12,
aliases: None,
doc: None,
attributes: BTreeMap::new(),
});
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert_eq!(specification_eq_res, struct_field_eq_res);
}
#[test]
fn avro_rs_382_compare_schemata_duration_different_names() {
let schema_one = Schema::Duration(FixedSchema {
name: Name::try_from("name1").expect("Name is valid"),
size: 12,
aliases: None,
doc: None,
attributes: BTreeMap::new(),
});
let schema_two = Schema::Duration(FixedSchema {
name: Name::try_from("name2").expect("Name is valid"),
size: 12,
aliases: None,
doc: None,
attributes: BTreeMap::new(),
});
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
assert!(!specification_eq_res);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(!struct_field_eq_res);
}
#[test]
fn avro_rs_382_compare_schemata_duration_different_attributes() {
let schema_one = Schema::Duration(FixedSchema {
name: Name::try_from("name1").expect("Name is valid"),
size: 12,
aliases: None,
doc: None,
attributes: vec![(String::from("attr1"), serde_json::Value::Bool(true))]
.into_iter()
.collect(),
});
let schema_two = Schema::Duration(FixedSchema {
name: Name::try_from("name1").expect("Name is valid"),
size: 12,
aliases: None,
doc: None,
attributes: BTreeMap::new(),
});
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
assert!(specification_eq_res);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(struct_field_eq_res);
let struct_field_eq_with_attrs_res =
STRUCT_FIELD_EQ_WITH_ATTRS.compare(&schema_one, &schema_two);
assert!(!struct_field_eq_with_attrs_res);
}
#[test]
fn avro_rs_382_compare_schemata_duration_different_sizes() {
let schema_one = Schema::Duration(FixedSchema {
name: Name::try_from("name1").expect("Name is valid"),
size: 8,
aliases: None,
doc: None,
attributes: BTreeMap::new(),
});
let schema_two = Schema::Duration(FixedSchema {
name: Name::try_from("name1").expect("Name is valid"),
size: 12,
aliases: None,
doc: None,
attributes: BTreeMap::new(),
});
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
assert!(!specification_eq_res);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(!struct_field_eq_res);
}
#[test]
fn test_avro_3939_compare_named_schemata_with_different_names() {
let schema_one = Schema::Ref {
name: Name::try_from("name1").expect("Name is valid"),
};
let schema_two = Schema::Ref {
name: Name::try_from("name2").expect("Name is valid"),
};
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
assert!(!specification_eq_res);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(!struct_field_eq_res);
assert_eq!(specification_eq_res, struct_field_eq_res);
}
#[test]
fn test_avro_3939_compare_schemata_not_including_attributes() {
let schema_one = Schema::map(Schema::Boolean)
.attributes(BTreeMap::from_iter([(
"key1".to_string(),
Value::Bool(true),
)]))
.build();
let schema_two = Schema::map(Schema::Boolean)
.attributes(BTreeMap::from_iter([(
"key2".to_string(),
Value::Bool(true),
)]))
.build();
// STRUCT_FIELD_EQ does not include attributes !
assert!(STRUCT_FIELD_EQ.compare(&schema_one, &schema_two));
}
#[test]
fn test_avro_3939_compare_schemata_including_attributes() {
let struct_field_eq = StructFieldEq {
include_attributes: true,
};
let schema_one = Schema::map(Schema::Boolean)
.attributes(BTreeMap::from_iter([(
"key1".to_string(),
Value::Bool(true),
)]))
.build();
let schema_two = Schema::map(Schema::Boolean)
.attributes(BTreeMap::from_iter([(
"key2".to_string(),
Value::Bool(true),
)]))
.build();
assert!(!struct_field_eq.compare(&schema_one, &schema_two));
}
#[test]
fn test_avro_3939_compare_map_schemata() {
let schema_one = Schema::map(Schema::Boolean).build();
assert!(!SPECIFICATION_EQ.compare(&schema_one, &Schema::Boolean));
assert!(!STRUCT_FIELD_EQ.compare(&schema_one, &Schema::Boolean));
let schema_two = Schema::map(Schema::Boolean).build();
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(
specification_eq_res,
"SpecificationEq: Equality of two Schema::Map failed!"
);
assert!(
struct_field_eq_res,
"StructFieldEq: Equality of two Schema::Map failed!"
);
assert_eq!(specification_eq_res, struct_field_eq_res);
}
#[test]
fn test_avro_3939_compare_array_schemata() {
let schema_one = Schema::array(Schema::Boolean).build();
assert!(!SPECIFICATION_EQ.compare(&schema_one, &Schema::Boolean));
assert!(!STRUCT_FIELD_EQ.compare(&schema_one, &Schema::Boolean));
let schema_two = Schema::array(Schema::Boolean).build();
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(
specification_eq_res,
"SpecificationEq: Equality of two Schema::Array failed!"
);
assert!(
struct_field_eq_res,
"StructFieldEq: Equality of two Schema::Array failed!"
);
assert_eq!(specification_eq_res, struct_field_eq_res);
}
#[test]
fn test_avro_3939_compare_decimal_schemata() {
let schema_one = Schema::Decimal(DecimalSchema {
precision: 10,
scale: 2,
inner: InnerDecimalSchema::Bytes,
});
assert!(!SPECIFICATION_EQ.compare(&schema_one, &Schema::Boolean));
assert!(!STRUCT_FIELD_EQ.compare(&schema_one, &Schema::Boolean));
let schema_two = Schema::Decimal(DecimalSchema {
precision: 10,
scale: 2,
inner: InnerDecimalSchema::Bytes,
});
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(
specification_eq_res,
"SpecificationEq: Equality of two Schema::Decimal failed!"
);
assert!(
struct_field_eq_res,
"StructFieldEq: Equality of two Schema::Decimal failed!"
);
assert_eq!(specification_eq_res, struct_field_eq_res);
}
#[test]
fn test_avro_3939_compare_fixed_schemata() {
let schema_one = Schema::Fixed(FixedSchema {
name: Name::try_from("fixed").expect("Name is valid"),
doc: None,
size: 10,
aliases: None,
attributes: BTreeMap::new(),
});
assert!(!SPECIFICATION_EQ.compare(&schema_one, &Schema::Boolean));
assert!(!STRUCT_FIELD_EQ.compare(&schema_one, &Schema::Boolean));
let schema_two = Schema::Fixed(FixedSchema {
name: Name::try_from("fixed").expect("Name is valid"),
doc: None,
size: 10,
aliases: None,
attributes: BTreeMap::new(),
});
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(
specification_eq_res,
"SpecificationEq: Equality of two Schema::Fixed failed!"
);
assert!(
struct_field_eq_res,
"StructFieldEq: Equality of two Schema::Fixed failed!"
);
assert_eq!(specification_eq_res, struct_field_eq_res);
}
#[test]
fn test_avro_3939_compare_enum_schemata() {
let schema_one = Schema::Enum(EnumSchema {
name: Name::try_from("enum").expect("Name is valid"),
doc: None,
symbols: vec!["A".to_string(), "B".to_string()],
default: None,
aliases: None,
attributes: BTreeMap::new(),
});
assert!(!SPECIFICATION_EQ.compare(&schema_one, &Schema::Boolean));
assert!(!STRUCT_FIELD_EQ.compare(&schema_one, &Schema::Boolean));
let schema_two = Schema::Enum(EnumSchema {
name: Name::try_from("enum").expect("Name is valid"),
doc: None,
symbols: vec!["A".to_string(), "B".to_string()],
default: None,
aliases: None,
attributes: BTreeMap::new(),
});
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(
specification_eq_res,
"SpecificationEq: Equality of two Schema::Enum failed!"
);
assert!(
struct_field_eq_res,
"StructFieldEq: Equality of two Schema::Enum failed!"
);
assert_eq!(specification_eq_res, struct_field_eq_res);
}
#[test]
fn test_avro_3939_compare_ref_schemata() {
let schema_one = Schema::Ref {
name: Name::try_from("ref").expect("Name is valid"),
};
assert!(!SPECIFICATION_EQ.compare(&schema_one, &Schema::Boolean));
assert!(!STRUCT_FIELD_EQ.compare(&schema_one, &Schema::Boolean));
let schema_two = Schema::Ref {
name: Name::try_from("ref").expect("Name is valid"),
};
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(
specification_eq_res,
"SpecificationEq: Equality of two Schema::Ref failed!"
);
assert!(
struct_field_eq_res,
"StructFieldEq: Equality of two Schema::Ref failed!"
);
assert_eq!(specification_eq_res, struct_field_eq_res);
}
#[test]
fn test_avro_3939_compare_record_schemata() {
let schema_one = Schema::Record(RecordSchema {
name: Name::try_from("record").expect("Name is valid"),
doc: None,
fields: vec![
RecordField::builder()
.name("field".to_string())
.schema(Schema::Boolean)
.build(),
],
aliases: None,
attributes: BTreeMap::new(),
lookup: Default::default(),
});
assert!(!SPECIFICATION_EQ.compare(&schema_one, &Schema::Boolean));
assert!(!STRUCT_FIELD_EQ.compare(&schema_one, &Schema::Boolean));
let schema_two = Schema::Record(RecordSchema {
name: Name::try_from("record").expect("Name is valid"),
doc: None,
fields: vec![
RecordField::builder()
.name("field".to_string())
.schema(Schema::Boolean)
.build(),
],
aliases: None,
attributes: BTreeMap::new(),
lookup: Default::default(),
});
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(
specification_eq_res,
"SpecificationEq: Equality of two Schema::Record failed!"
);
assert!(
struct_field_eq_res,
"StructFieldEq: Equality of two Schema::Record failed!"
);
assert_eq!(specification_eq_res, struct_field_eq_res);
}
#[test]
fn test_avro_3939_compare_union_schemata() -> TestResult {
let schema_one = Schema::Union(UnionSchema::new(vec![Schema::Boolean, Schema::Int])?);
assert!(!SPECIFICATION_EQ.compare(&schema_one, &Schema::Boolean));
assert!(!STRUCT_FIELD_EQ.compare(&schema_one, &Schema::Boolean));
let schema_two = Schema::Union(UnionSchema::new(vec![Schema::Boolean, Schema::Int])?);
let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, &schema_two);
let struct_field_eq_res = STRUCT_FIELD_EQ.compare(&schema_one, &schema_two);
assert!(
specification_eq_res,
"SpecificationEq: Equality of two Schema::Union failed!"
);
assert!(
struct_field_eq_res,
"StructFieldEq: Equality of two Schema::Union failed!"
);
assert_eq!(specification_eq_res, struct_field_eq_res);
Ok(())
}
#[test]
fn test_uuid_compare_uuid() -> TestResult {
let string = Schema::Uuid(UuidSchema::String);
let bytes = Schema::Uuid(UuidSchema::Bytes);
let mut fixed_schema = FixedSchema {
name: Name::new("some_name")?,
aliases: None,
doc: None,
size: 16,
attributes: Default::default(),
};
let fixed = Schema::Uuid(UuidSchema::Fixed(fixed_schema.clone()));
fixed_schema
.attributes
.insert("Something".to_string(), Value::Null);
let fixed_different = Schema::Uuid(UuidSchema::Fixed(fixed_schema));
assert!(SPECIFICATION_EQ.compare(&string, &string));
assert!(STRUCT_FIELD_EQ.compare(&string, &string));
assert!(SPECIFICATION_EQ.compare(&bytes, &bytes));
assert!(STRUCT_FIELD_EQ.compare(&bytes, &bytes));
assert!(SPECIFICATION_EQ.compare(&fixed, &fixed));
assert!(STRUCT_FIELD_EQ.compare(&fixed, &fixed));
assert!(!SPECIFICATION_EQ.compare(&string, &bytes));
assert!(!STRUCT_FIELD_EQ.compare(&string, &bytes));
assert!(!SPECIFICATION_EQ.compare(&bytes, &string));
assert!(!STRUCT_FIELD_EQ.compare(&bytes, &string));
assert!(!SPECIFICATION_EQ.compare(&string, &fixed));
assert!(!STRUCT_FIELD_EQ.compare(&string, &fixed));
assert!(!SPECIFICATION_EQ.compare(&fixed, &string));
assert!(!STRUCT_FIELD_EQ.compare(&fixed, &string));
assert!(!SPECIFICATION_EQ.compare(&bytes, &fixed));
assert!(!STRUCT_FIELD_EQ.compare(&bytes, &fixed));
assert!(!SPECIFICATION_EQ.compare(&fixed, &bytes));
assert!(!STRUCT_FIELD_EQ.compare(&fixed, &bytes));
assert!(SPECIFICATION_EQ.compare(&fixed, &fixed_different));
assert!(STRUCT_FIELD_EQ.compare(&fixed, &fixed_different));
assert!(SPECIFICATION_EQ.compare(&fixed_different, &fixed));
assert!(STRUCT_FIELD_EQ.compare(&fixed_different, &fixed));
let strict = StructFieldEq {
include_attributes: true,
};
assert!(!strict.compare(&fixed, &fixed_different));
assert!(!strict.compare(&fixed_different, &fixed));
Ok(())
}
}