This is an automated email from the ASF dual-hosted git repository.
mgrigorov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/main by this push:
new 090674bf6 AVRO-4029: [Rust] Improve the error messages related to
value resolving (#3075)
090674bf6 is described below
commit 090674bf6ef0ff47c255d0d707d51dad20f5ebc9
Author: Martin Grigorov <[email protected]>
AuthorDate: Fri Aug 9 11:02:47 2024 +0300
AVRO-4029: [Rust] Improve the error messages related to value resolving
(#3075)
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..7afacf0a1 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(error) => {
+ assert_eq!(format!("{error}"), expected_error);
+ }
+ other => {
+ panic!("Expected '{expected_error}', got {other:?}");
+ }
+ }
+ }
+ Ok(())
+ }
}