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(())
+    }
 }

Reply via email to