This is an automated email from the ASF dual-hosted git repository. mgrigorov pushed a commit to branch avro-4029-better-error-messages in repository https://gitbox.apache.org/repos/asf/avro.git
commit 2470ec4f25b5df84395722a3fea3db5efa81a04b Author: Martin Tzvetanov Grigorov <[email protected]> AuthorDate: Wed Aug 7 16:28:55 2024 +0300 AVRO-4029: [Rust] Improve the error messages related to value resolving Signed-off-by: Martin Tzvetanov Grigorov <[email protected]> --- lang/rust/avro/src/decode.rs | 8 +-- lang/rust/avro/src/error.rs | 162 +++++++++++++++++++++---------------------- lang/rust/avro/src/schema.rs | 16 +++++ lang/rust/avro/src/types.rs | 99 ++++++++++++++++++-------- 4 files changed, 170 insertions(+), 115 deletions(-) diff --git a/lang/rust/avro/src/decode.rs b/lang/rust/avro/src/decode.rs index 9c685e9a8..46e3381f3 100644 --- a/lang/rust/avro/src/decode.rs +++ b/lang/rust/avro/src/decode.rs @@ -106,19 +106,19 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>( Schema::Fixed { .. } => { match decode_internal(inner, names, enclosing_namespace, reader)? { Value::Fixed(_, bytes) => Ok(Value::Decimal(Decimal::from(bytes))), - value => Err(Error::FixedValue(value.into())), + value => Err(Error::FixedValue(value)), } } Schema::Bytes => match decode_internal(inner, names, enclosing_namespace, reader)? { Value::Bytes(bytes) => Ok(Value::Decimal(Decimal::from(bytes))), - value => Err(Error::BytesValue(value.into())), + value => Err(Error::BytesValue(value)), }, schema => Err(Error::ResolveDecimalSchema(schema.into())), }, Schema::BigDecimal => { match decode_internal(&Schema::Bytes, names, enclosing_namespace, reader)? { Value::Bytes(bytes) => deserialize_big_decimal(&bytes).map(Value::BigDecimal), - value => Err(Error::BytesValue(value.into())), + value => Err(Error::BytesValue(value)), } } Schema::Uuid => { @@ -138,7 +138,7 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>( reader, )? { Value::String(ref s) => Uuid::from_str(s).map_err(Error::ConvertStrToUuid), - value => Err(Error::GetUuidFromStringValue(value.into())), + value => Err(Error::GetUuidFromStringValue(value)), }; let uuid: Uuid = if len == 16 { diff --git a/lang/rust/avro/src/error.rs b/lang/rust/avro/src/error.rs index c4ec2baa8..d92daa48e 100644 --- a/lang/rust/avro/src/error.rs +++ b/lang/rust/avro/src/error.rs @@ -30,13 +30,13 @@ pub enum Error { BoolValue(u8), #[error("Not a fixed value, required for decimal with fixed schema: {0:?}")] - FixedValue(ValueKind), + FixedValue(Value), #[error("Not a bytes value, required for decimal with bytes schema: {0:?}")] - BytesValue(ValueKind), + BytesValue(Value), #[error("Not a string value, required for uuid: {0:?}")] - GetUuidFromStringValue(ValueKind), + GetUuidFromStringValue(Value), #[error("Two schemas with the same fullname were given: {0:?}")] NameCollision(String), @@ -69,34 +69,34 @@ pub enum Error { #[error("Number of bytes requested for decimal sign extension {requested} is less than the number of bytes needed to decode {needed}")] SignExtend { requested: usize, needed: usize }, - #[error("Failed to read boolean bytes")] + #[error("Failed to read boolean bytes: {0}")] ReadBoolean(#[source] std::io::Error), - #[error("Failed to read bytes")] + #[error("Failed to read bytes: {0}")] ReadBytes(#[source] std::io::Error), - #[error("Failed to read string")] + #[error("Failed to read string: {0}")] ReadString(#[source] std::io::Error), - #[error("Failed to read double")] + #[error("Failed to read double: {0}")] ReadDouble(#[source] std::io::Error), - #[error("Failed to read float")] + #[error("Failed to read float: {0}")] ReadFloat(#[source] std::io::Error), - #[error("Failed to read duration")] + #[error("Failed to read duration: {0}")] ReadDuration(#[source] std::io::Error), - #[error("Failed to read fixed number of bytes: {1}")] + #[error("Failed to read fixed number of bytes '{1}': : {0}")] ReadFixed(#[source] std::io::Error, usize), - #[error("Failed to convert &str to UUID")] + #[error("Failed to convert &str to UUID: {0}")] ConvertStrToUuid(#[source] uuid::Error), #[error("Failed to convert Fixed bytes to UUID. It must be exactly 16 bytes, got {0}")] ConvertFixedToUuid(usize), - #[error("Failed to convert Fixed bytes to UUID")] + #[error("Failed to convert Fixed bytes to UUID: {0}")] ConvertSliceToUuid(#[source] uuid::Error), #[error("Map key is not a string; key type is {0:?}")] @@ -122,26 +122,26 @@ pub enum Error { )] GetScaleWithFixedSize { size: usize, precision: usize }, - #[error("expected UUID, got: {0:?}")] - GetUuid(ValueKind), + #[error("Expected Value::Uuid, got: {0:?}")] + GetUuid(Value), - #[error("expected BigDecimal, got: {0:?}")] - GetBigdecimal(ValueKind), + #[error("Expected Value::BigDecimal, got: {0:?}")] + GetBigDecimal(Value), #[error("Fixed bytes of size 12 expected, got Fixed of size {0}")] GetDecimalFixedBytes(usize), - #[error("Duration expected, got {0:?}")] - ResolveDuration(ValueKind), + #[error("Expected Value::Duration or Value::Fixed(12), got: {0:?}")] + ResolveDuration(Value), - #[error("Decimal expected, got {0:?}")] - ResolveDecimal(ValueKind), + #[error("Expected Value::Decimal, Value::Bytes or Value::Fixed, got: {0:?}")] + ResolveDecimal(Value), #[error("Missing field in record: {0:?}")] GetField(String), #[error("Unable to convert to u8, got {0:?}")] - GetU8(ValueKind), + GetU8(Value), #[error("Precision {precision} too small to hold decimal values with {num_bytes} bytes")] ComparePrecisionAndSize { precision: usize, num_bytes: usize }, @@ -149,65 +149,65 @@ pub enum Error { #[error("Cannot convert length to i32: {1}")] ConvertLengthToI32(#[source] std::num::TryFromIntError, usize), - #[error("Date expected, got {0:?}")] - GetDate(ValueKind), + #[error("Expected Value::Date or Value::Int, got: {0:?}")] + GetDate(Value), - #[error("TimeMillis expected, got {0:?}")] - GetTimeMillis(ValueKind), + #[error("Expected Value::TimeMillis or Value::Int, got: {0:?}")] + GetTimeMillis(Value), - #[error("TimeMicros expected, got {0:?}")] - GetTimeMicros(ValueKind), + #[error("Expected Value::TimeMicros, Value::Long or Value::Int, got: {0:?}")] + GetTimeMicros(Value), - #[error("TimestampMillis expected, got {0:?}")] - GetTimestampMillis(ValueKind), + #[error("Expected Value::TimestampMillis, Value::Long or Value::Int, got: {0:?}")] + GetTimestampMillis(Value), - #[error("TimestampMicros expected, got {0:?}")] - GetTimestampMicros(ValueKind), + #[error("Expected Value::TimestampMicros, Value::Long or Value::Int, got: {0:?}")] + GetTimestampMicros(Value), - #[error("TimestampNanos expected, got {0:?}")] - GetTimestampNanos(ValueKind), + #[error("Expected Value::TimestampNanos, Value::Long or Value::Int, got: {0:?}")] + GetTimestampNanos(Value), - #[error("LocalTimestampMillis expected, got {0:?}")] - GetLocalTimestampMillis(ValueKind), + #[error("Expected Value::LocalTimestampMillis, Value::Long or Value::Int, got: {0:?}")] + GetLocalTimestampMillis(Value), - #[error("LocalTimestampMicros expected, got {0:?}")] - GetLocalTimestampMicros(ValueKind), + #[error("Expected Value::LocalTimestampMicros, Value::Long or Value::Int, got: {0:?}")] + GetLocalTimestampMicros(Value), - #[error("LocalTimestampNanos expected, got {0:?}")] - GetLocalTimestampNanos(ValueKind), + #[error("Expected Value::LocalTimestampNanos, Value::Long or Value::Int, got: {0:?}")] + GetLocalTimestampNanos(Value), - #[error("Null expected, got {0:?}")] - GetNull(ValueKind), + #[error("Expected Value::Null, got: {0:?}")] + GetNull(Value), - #[error("Boolean expected, got {0:?}")] - GetBoolean(ValueKind), + #[error("Expected Value::Boolean, got: {0:?}")] + GetBoolean(Value), - #[error("Int expected, got {0:?}")] - GetInt(ValueKind), + #[error("Expected Value::Int, got: {0:?}")] + GetInt(Value), - #[error("Long expected, got {0:?}")] - GetLong(ValueKind), + #[error("Expected Value::Long or Value::Int, got: {0:?}")] + GetLong(Value), - #[error("Double expected, got {0:?}")] + #[error(r#"Expected Value::Double, Value::Float, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: {0:?}"#)] GetDouble(Value), - #[error("Float expected, got {0:?}")] + #[error(r#"Expected Value::Float, Value::Double, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: {0:?}"#)] GetFloat(Value), - #[error("Bytes expected, got {0:?}")] - GetBytes(ValueKind), + #[error("Expected Value::Bytes, got: {0:?}")] + GetBytes(Value), - #[error("String expected, got {0:?}")] - GetString(ValueKind), + #[error("Expected Value::String, Value::Bytes or Value::Fixed, got: {0:?}")] + GetString(Value), - #[error("Enum expected, got {0:?}")] - GetEnum(ValueKind), + #[error("Expected Value::Enum, got: {0:?}")] + GetEnum(Value), - #[error("Fixed size mismatch, {size} expected, got {n}")] + #[error("Fixed size mismatch, expected: {size}, got: {n}")] CompareFixedSizes { size: usize, n: usize }, - #[error("String expected for fixed, got {0:?}")] - GetStringForFixed(ValueKind), + #[error("String expected for fixed, got: {0:?}")] + GetStringForFixed(Value), #[error("Enum default {symbol:?} is not among allowed symbols {symbols:?}")] GetEnumDefault { @@ -228,21 +228,15 @@ pub enum Error { EmptyUnion, #[error("Array({expected:?}) expected, got {other:?}")] - GetArray { - expected: SchemaKind, - other: ValueKind, - }, + GetArray { expected: SchemaKind, other: Value }, #[error("Map({expected:?}) expected, got {other:?}")] - GetMap { - expected: SchemaKind, - other: ValueKind, - }, + GetMap { expected: SchemaKind, other: Value }, #[error("Record with fields {expected:?} expected, got {other:?}")] GetRecord { expected: Vec<(String, SchemaKind)>, - other: ValueKind, + other: Value, }, #[error("No `name` field")] @@ -377,34 +371,34 @@ pub enum Error { #[error("Fixed schema's default value length ({0}) does not match its size ({1})")] FixedDefaultLenSizeMismatch(usize, u64), - #[error("Failed to compress with flate")] + #[error("Failed to compress with flate: {0}")] DeflateCompress(#[source] std::io::Error), - #[error("Failed to finish flate compressor")] + #[error("Failed to finish flate compressor: {0}")] DeflateCompressFinish(#[source] std::io::Error), - #[error("Failed to decompress with flate")] + #[error("Failed to decompress with flate: {0}")] DeflateDecompress(#[source] std::io::Error), #[cfg(feature = "snappy")] - #[error("Failed to compress with snappy")] + #[error("Failed to compress with snappy: {0}")] SnappyCompress(#[source] snap::Error), #[cfg(feature = "snappy")] - #[error("Failed to get snappy decompression length")] + #[error("Failed to get snappy decompression length: {0}")] GetSnappyDecompressLen(#[source] snap::Error), #[cfg(feature = "snappy")] - #[error("Failed to decompress with snappy")] + #[error("Failed to decompress with snappy: {0}")] SnappyDecompress(#[source] snap::Error), - #[error("Failed to compress with zstd")] + #[error("Failed to compress with zstd: {0}")] ZstdCompress(#[source] std::io::Error), - #[error("Failed to decompress with zstd")] + #[error("Failed to decompress with zstd: {0}")] ZstdDecompress(#[source] std::io::Error), - #[error("Failed to read header")] + #[error("Failed to read header: {0}")] ReadHeader(#[source] std::io::Error), #[error("wrong magic in header")] @@ -419,13 +413,13 @@ pub enum Error { #[error("no metadata in header")] GetHeaderMetadata, - #[error("Failed to read marker bytes")] + #[error("Failed to read marker bytes: {0}")] ReadMarker(#[source] std::io::Error), - #[error("Failed to read block marker bytes")] + #[error("Failed to read block marker bytes: {0}")] ReadBlockMarker(#[source] std::io::Error), - #[error("Read into buffer failed")] + #[error("Read into buffer failed: {0}")] ReadIntoBuf(#[source] std::io::Error), #[error("block marker does not match header marker")] @@ -434,10 +428,10 @@ pub enum Error { #[error("Overflow when decoding integer value")] IntegerOverflow, - #[error("Failed to read bytes for decoding variable length integer")] + #[error("Failed to read bytes for decoding variable length integer: {0}")] ReadVariableIntegerBytes(#[source] std::io::Error), - #[error("Decoded integer out of range for i32: {1}")] + #[error("Decoded integer out of range for i32: {1}: {0}")] ZagI32(#[source] std::num::TryFromIntError, i64), #[error("unable to read block")] @@ -449,13 +443,13 @@ pub enum Error { #[error("Failed to deserialize Avro value into value: {0}")] DeserializeValue(String), - #[error("Failed to write buffer bytes during flush")] + #[error("Failed to write buffer bytes during flush: {0}")] WriteBytes(#[source] std::io::Error), - #[error("Failed to write marker")] + #[error("Failed to write marker: {0}")] WriteMarker(#[source] std::io::Error), - #[error("Failed to convert JSON to string")] + #[error("Failed to convert JSON to string: {0}")] ConvertJsonToString(#[source] serde_json::Error), /// Error while converting float to json value diff --git a/lang/rust/avro/src/schema.rs b/lang/rust/avro/src/schema.rs index c6e1f7032..ad5d11e5e 100644 --- a/lang/rust/avro/src/schema.rs +++ b/lang/rust/avro/src/schema.rs @@ -1489,6 +1489,14 @@ impl Parser { |_| -> AvroResult<Schema> { Ok(Schema::TimestampMicros) }, ); } + "timestamp-nanos" => { + return try_convert_to_logical_type( + "timestamp-nanos", + parse_as_native_complex(complex, self, enclosing_namespace)?, + &[SchemaKind::Long], + |_| -> AvroResult<Schema> { Ok(Schema::TimestampNanos) }, + ); + } "local-timestamp-millis" => { return try_convert_to_logical_type( "local-timestamp-millis", @@ -1505,6 +1513,14 @@ impl Parser { |_| -> AvroResult<Schema> { Ok(Schema::LocalTimestampMicros) }, ); } + "local-timestamp-nanos" => { + return try_convert_to_logical_type( + "local-timestamp-nanos", + parse_as_native_complex(complex, self, enclosing_namespace)?, + &[SchemaKind::Long], + |_| -> AvroResult<Schema> { Ok(Schema::LocalTimestampNanos) }, + ); + } "duration" => { return try_convert_to_logical_type( "duration", diff --git a/lang/rust/avro/src/types.rs b/lang/rust/avro/src/types.rs index 0ce6afc53..d1b93b123 100644 --- a/lang/rust/avro/src/types.rs +++ b/lang/rust/avro/src/types.rs @@ -724,7 +724,7 @@ impl Value { Value::String(ref string) => { Value::Uuid(Uuid::from_str(string).map_err(Error::ConvertStrToUuid)?) } - other => return Err(Error::GetUuid(other.into())), + other => return Err(Error::GetUuid(other)), }) } @@ -732,7 +732,7 @@ impl Value { Ok(match self { bg @ Value::BigDecimal(_) => bg, Value::Bytes(b) => Value::BigDecimal(deserialize_big_decimal(&b).unwrap()), - other => return Err(Error::GetBigdecimal(other.into())), + other => return Err(Error::GetBigDecimal(other)), }) } @@ -748,7 +748,7 @@ impl Value { bytes[8], bytes[9], bytes[10], bytes[11], ])) } - other => return Err(Error::ResolveDuration(other.into())), + other => return Err(Error::ResolveDuration(other)), }) } @@ -794,21 +794,21 @@ impl Value { Ok(Value::Decimal(Decimal::from(bytes))) } } - other => Err(Error::ResolveDecimal(other.into())), + other => Err(Error::ResolveDecimal(other)), } } fn resolve_date(self) -> Result<Self, Error> { match self { Value::Date(d) | Value::Int(d) => Ok(Value::Date(d)), - other => Err(Error::GetDate(other.into())), + other => Err(Error::GetDate(other)), } } fn resolve_time_millis(self) -> Result<Self, Error> { match self { Value::TimeMillis(t) | Value::Int(t) => Ok(Value::TimeMillis(t)), - other => Err(Error::GetTimeMillis(other.into())), + other => Err(Error::GetTimeMillis(other)), } } @@ -816,7 +816,7 @@ impl Value { match self { Value::TimeMicros(t) | Value::Long(t) => Ok(Value::TimeMicros(t)), Value::Int(t) => Ok(Value::TimeMicros(i64::from(t))), - other => Err(Error::GetTimeMicros(other.into())), + other => Err(Error::GetTimeMicros(other)), } } @@ -824,7 +824,7 @@ impl Value { match self { Value::TimestampMillis(ts) | Value::Long(ts) => Ok(Value::TimestampMillis(ts)), Value::Int(ts) => Ok(Value::TimestampMillis(i64::from(ts))), - other => Err(Error::GetTimestampMillis(other.into())), + other => Err(Error::GetTimestampMillis(other)), } } @@ -832,7 +832,7 @@ impl Value { match self { Value::TimestampMicros(ts) | Value::Long(ts) => Ok(Value::TimestampMicros(ts)), Value::Int(ts) => Ok(Value::TimestampMicros(i64::from(ts))), - other => Err(Error::GetTimestampMicros(other.into())), + other => Err(Error::GetTimestampMicros(other)), } } @@ -840,7 +840,7 @@ impl Value { match self { Value::TimestampNanos(ts) | Value::Long(ts) => Ok(Value::TimestampNanos(ts)), Value::Int(ts) => Ok(Value::TimestampNanos(i64::from(ts))), - other => Err(Error::GetTimestampNanos(other.into())), + other => Err(Error::GetTimestampNanos(other)), } } @@ -850,7 +850,7 @@ impl Value { Ok(Value::LocalTimestampMillis(ts)) } Value::Int(ts) => Ok(Value::LocalTimestampMillis(i64::from(ts))), - other => Err(Error::GetLocalTimestampMillis(other.into())), + other => Err(Error::GetLocalTimestampMillis(other)), } } @@ -860,7 +860,7 @@ impl Value { Ok(Value::LocalTimestampMicros(ts)) } Value::Int(ts) => Ok(Value::LocalTimestampMicros(i64::from(ts))), - other => Err(Error::GetLocalTimestampMicros(other.into())), + other => Err(Error::GetLocalTimestampMicros(other)), } } @@ -868,21 +868,21 @@ impl Value { match self { Value::LocalTimestampNanos(ts) | Value::Long(ts) => Ok(Value::LocalTimestampNanos(ts)), Value::Int(ts) => Ok(Value::LocalTimestampNanos(i64::from(ts))), - other => Err(Error::GetLocalTimestampNanos(other.into())), + other => Err(Error::GetLocalTimestampNanos(other)), } } fn resolve_null(self) -> Result<Self, Error> { match self { Value::Null => Ok(Value::Null), - other => Err(Error::GetNull(other.into())), + other => Err(Error::GetNull(other)), } } fn resolve_boolean(self) -> Result<Self, Error> { match self { Value::Boolean(b) => Ok(Value::Boolean(b)), - other => Err(Error::GetBoolean(other.into())), + other => Err(Error::GetBoolean(other)), } } @@ -890,7 +890,7 @@ impl Value { match self { Value::Int(n) => Ok(Value::Int(n)), Value::Long(n) => Ok(Value::Int(n as i32)), - other => Err(Error::GetInt(other.into())), + other => Err(Error::GetInt(other)), } } @@ -898,7 +898,7 @@ impl Value { match self { Value::Int(n) => Ok(Value::Long(i64::from(n))), Value::Long(n) => Ok(Value::Long(n)), - other => Err(Error::GetLong(other.into())), + other => Err(Error::GetLong(other)), } } @@ -951,7 +951,7 @@ impl Value { .map(Value::try_u8) .collect::<Result<Vec<_>, _>>()?, )), - other => Err(Error::GetBytes(other.into())), + other => Err(Error::GetBytes(other)), } } @@ -961,7 +961,7 @@ impl Value { Value::Bytes(bytes) | Value::Fixed(_, bytes) => Ok(Value::String( String::from_utf8(bytes).map_err(Error::ConvertToUtf8)?, )), - other => Err(Error::GetString(other.into())), + other => Err(Error::GetString(other)), } } @@ -982,7 +982,7 @@ impl Value { Err(Error::CompareFixedSizes { size, n: s.len() }) } } - other => Err(Error::GetStringForFixed(other.into())), + other => Err(Error::GetStringForFixed(other)), } } @@ -1018,7 +1018,7 @@ impl Value { match self { Value::Enum(_raw_index, s) => validate_symbol(s, symbols), Value::String(s) => validate_symbol(s, symbols), - other => Err(Error::GetEnum(other.into())), + other => Err(Error::GetEnum(other)), } } @@ -1060,7 +1060,7 @@ impl Value { )), other => Err(Error::GetArray { expected: schema.into(), - other: other.into(), + other, }), } } @@ -1084,7 +1084,7 @@ impl Value { )), other => Err(Error::GetMap { expected: schema.into(), - other: other.into(), + other, }), } } @@ -1103,7 +1103,7 @@ impl Value { .iter() .map(|field| (field.name.clone(), field.schema.clone().into())) .collect(), - other: other.into(), + other, }), }?; @@ -1164,7 +1164,7 @@ impl Value { } } - Err(Error::GetU8(int.into())) + Err(Error::GetU8(int)) } } @@ -3147,7 +3147,7 @@ Field with name '"b"' is not a member of the map items"#, Err(err @ Error::GetDouble(_)) => { assert_eq!( format!("{err:?}"), - r#"Double expected, got String("unknown")"# + r#"Expected Value::Double, Value::Float, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: String("unknown")"# ); } other => { @@ -3165,7 +3165,7 @@ Field with name '"b"' is not a member of the map items"#, Err(err @ Error::GetFloat(_)) => { assert_eq!( format!("{err:?}"), - r#"Float expected, got String("unknown")"# + r#"Expected Value::Float, Value::Double, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: String("unknown")"# ); } other => { @@ -3174,4 +3174,49 @@ Field with name '"b"' is not a member of the map items"#, } Ok(()) } + + #[test] + fn avro_4029_resolve_from_unsupported_err() -> TestResult { + let data: Vec<(&str, Value, &str)> = vec!( + (r#"{ "name": "NAME", "type": "int" }"#, Value::Float(123_f32), "Expected Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "fixed", "size": 3 }"#, Value::Float(123_f32), "String expected for fixed, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "bytes" }"#, Value::Float(123_f32), "Expected Value::Bytes, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "string", "logicalType": "uuid" }"#, Value::String("abc-1234".into()), "Failed to convert &str to UUID: invalid group count: expected 5, found 2"), + (r#"{ "name": "NAME", "type": "string", "logicalType": "uuid" }"#, Value::Float(123_f32), "Expected Value::Uuid, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "bytes", "logicalType": "big-decimal" }"#, Value::Float(123_f32), "Expected Value::BigDecimal, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "fixed", "size": 12, "logicalType": "duration" }"#, Value::Float(123_f32), "Expected Value::Duration or Value::Fixed(12), got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 3 }"#, Value::Float(123_f32), "Expected Value::Decimal, Value::Bytes or Value::Fixed, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "bytes" }"#, Value::Array(vec!(Value::Long(256_i64))), "Unable to convert to u8, got Int(256)"), + (r#"{ "name": "NAME", "type": "int", "logicalType": "date" }"#, Value::Float(123_f32), "Expected Value::Date or Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "int", "logicalType": "time-millis" }"#, Value::Float(123_f32), "Expected Value::TimeMillis or Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "long", "logicalType": "time-micros" }"#, Value::Float(123_f32), "Expected Value::TimeMicros, Value::Long or Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "long", "logicalType": "timestamp-millis" }"#, Value::Float(123_f32), "Expected Value::TimestampMillis, Value::Long or Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "long", "logicalType": "timestamp-micros" }"#, Value::Float(123_f32), "Expected Value::TimestampMicros, Value::Long or Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "long", "logicalType": "timestamp-nanos" }"#, Value::Float(123_f32), "Expected Value::TimestampNanos, Value::Long or Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "long", "logicalType": "local-timestamp-millis" }"#, Value::Float(123_f32), "Expected Value::LocalTimestampMillis, Value::Long or Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "long", "logicalType": "local-timestamp-micros" }"#, Value::Float(123_f32), "Expected Value::LocalTimestampMicros, Value::Long or Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "long", "logicalType": "local-timestamp-nanos" }"#, Value::Float(123_f32), "Expected Value::LocalTimestampNanos, Value::Long or Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "null" }"#, Value::Float(123_f32), "Expected Value::Null, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "boolean" }"#, Value::Float(123_f32), "Expected Value::Boolean, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "int" }"#, Value::Float(123_f32), "Expected Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "long" }"#, Value::Float(123_f32), "Expected Value::Long or Value::Int, got: Float(123.0)"), + (r#"{ "name": "NAME", "type": "float" }"#, Value::Boolean(false), r#"Expected Value::Float, Value::Double, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: Boolean(false)"#), + (r#"{ "name": "NAME", "type": "double" }"#, Value::Boolean(false), r#"Expected Value::Double, Value::Float, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: Boolean(false)"#), + (r#"{ "name": "NAME", "type": "string" }"#, Value::Boolean(false), "Expected Value::String, Value::Bytes or Value::Fixed, got: Boolean(false)"), + (r#"{ "name": "NAME", "type": "enum", "symbols": ["one", "two"] }"#, Value::Boolean(false), "Expected Value::Enum, got: Boolean(false)"), + ); + + for (schema_str, value, expected_error) in data { + let schema = Schema::parse_str(schema_str)?; + match value.resolve(&schema) { + Err(ref error) => { + assert_eq!(format!("{error}"), format!("{expected_error}")); + } + other => { + panic!("Expected {expected_error}, got {other:?}"); + } + } + } + Ok(()) + } }
