This is an automated email from the ASF dual-hosted git repository.
mgrigorov pushed a commit to branch branch-1.12
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/branch-1.12 by this push:
new 5277c1497 AVRO-4024: [Rust] Sync the possible String default values
for floating numbers with Java and C# (#3073)
5277c1497 is described below
commit 5277c1497697eaf4e48b081a2d5b8cbd2101dd05
Author: Martin Grigorov <[email protected]>
AuthorDate: Wed Aug 7 14:42:38 2024 +0300
AVRO-4024: [Rust] Sync the possible String default values for floating
numbers with Java and C# (#3073)
* AVRO-4024: [Rust] Improve the error messages when parsing unknown String
into Value::Float/Double
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
* AVRO-4024: [Rust] Accept only "Nan", "INF", "-INF", "Infinity" and
"-Infinity"
This is what the Java SDK (via Jackson library) supports
(https://github.com/apache/avro/pull/3066).
This is what the C# SDK also would support
(https://github.com/apache/avro/pull/3070)
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
---------
Signed-off-by: Martin Tzvetanov Grigorov <[email protected]>
(cherry picked from commit dffc13a6b3b71bfd6fd01e3041955e65c0697f3b)
---
lang/rust/avro/src/error.rs | 4 +--
lang/rust/avro/src/types.rs | 60 ++++++++++++++++++++++++++++++++++++---------
lang/rust/avro/tests/io.rs | 14 ++++++++---
3 files changed, 61 insertions(+), 17 deletions(-)
diff --git a/lang/rust/avro/src/error.rs b/lang/rust/avro/src/error.rs
index 6e0de0dcb..c4ec2baa8 100644
--- a/lang/rust/avro/src/error.rs
+++ b/lang/rust/avro/src/error.rs
@@ -189,10 +189,10 @@ pub enum Error {
GetLong(ValueKind),
#[error("Double expected, got {0:?}")]
- GetDouble(ValueKind),
+ GetDouble(Value),
#[error("Float expected, got {0:?}")]
- GetFloat(ValueKind),
+ GetFloat(Value),
#[error("Bytes expected, got {0:?}")]
GetBytes(ValueKind),
diff --git a/lang/rust/avro/src/types.rs b/lang/rust/avro/src/types.rs
index 0840c87ae..0ce6afc53 100644
--- a/lang/rust/avro/src/types.rs
+++ b/lang/rust/avro/src/types.rs
@@ -908,11 +908,11 @@ impl Value {
Value::Long(n) => Ok(Value::Float(n as f32)),
Value::Float(x) => Ok(Value::Float(x)),
Value::Double(x) => Ok(Value::Float(x as f32)),
- Value::String(x) => match Self::parse_special_float(&x) {
+ Value::String(ref x) => match Self::parse_special_float(x) {
Some(f) => Ok(Value::Float(f)),
- None => Err(Error::GetFloat(ValueKind::String)),
+ None => Err(Error::GetFloat(self)),
},
- other => Err(Error::GetFloat(other.into())),
+ other => Err(Error::GetFloat(other)),
}
}
@@ -922,21 +922,21 @@ impl Value {
Value::Long(n) => Ok(Value::Double(n as f64)),
Value::Float(x) => Ok(Value::Double(f64::from(x))),
Value::Double(x) => Ok(Value::Double(x)),
- Value::String(x) => match Self::parse_special_float(&x) {
- Some(f) => Ok(Value::Double(f.into())),
- None => Err(Error::GetDouble(ValueKind::String)),
+ Value::String(ref x) => match Self::parse_special_float(x) {
+ Some(f) => Ok(Value::Double(f64::from(f))),
+ None => Err(Error::GetDouble(self)),
},
- other => Err(Error::GetDouble(other.into())),
+ other => Err(Error::GetDouble(other)),
}
}
/// IEEE 754 NaN and infinities are not valid JSON numbers.
/// So they are represented in JSON as strings.
- fn parse_special_float(s: &str) -> Option<f32> {
- match s.trim().to_ascii_lowercase().as_str() {
- "nan" | "+nan" | "-nan" => Some(f32::NAN),
- "inf" | "+inf" | "infinity" | "+infinity" => Some(f32::INFINITY),
- "-inf" | "-infinity" => Some(f32::NEG_INFINITY),
+ fn parse_special_float(value: &str) -> Option<f32> {
+ match value {
+ "NaN" => Some(f32::NAN),
+ "INF" | "Infinity" => Some(f32::INFINITY),
+ "-INF" | "-Infinity" => Some(f32::NEG_INFINITY),
_ => None,
}
}
@@ -3138,4 +3138,40 @@ Field with name '"b"' is not a member of the map items"#,
)
);
}
+
+ #[test]
+ fn avro_4024_resolve_double_from_unknown_string_err() -> TestResult {
+ let schema = Schema::parse_str(r#"{"type": "double"}"#)?;
+ let value = Value::String("unknown".to_owned());
+ match value.resolve(&schema) {
+ Err(err @ Error::GetDouble(_)) => {
+ assert_eq!(
+ format!("{err:?}"),
+ r#"Double expected, got String("unknown")"#
+ );
+ }
+ other => {
+ panic!("Expected Error::GetDouble, got {other:?}");
+ }
+ }
+ Ok(())
+ }
+
+ #[test]
+ fn avro_4024_resolve_float_from_unknown_string_err() -> TestResult {
+ let schema = Schema::parse_str(r#"{"type": "float"}"#)?;
+ let value = Value::String("unknown".to_owned());
+ match value.resolve(&schema) {
+ Err(err @ Error::GetFloat(_)) => {
+ assert_eq!(
+ format!("{err:?}"),
+ r#"Float expected, got String("unknown")"#
+ );
+ }
+ other => {
+ panic!("Expected Error::GetFloat, got {other:?}");
+ }
+ }
+ Ok(())
+ }
}
diff --git a/lang/rust/avro/tests/io.rs b/lang/rust/avro/tests/io.rs
index 6a59a5071..c6589a978 100644
--- a/lang/rust/avro/tests/io.rs
+++ b/lang/rust/avro/tests/io.rs
@@ -107,14 +107,22 @@ fn default_value_examples() -> &'static Vec<(&'static
str, &'static str, Value)>
(r#""long""#, "5", Value::Long(5)),
(r#""float""#, "1.1", Value::Float(1.1)),
(r#""double""#, "1.1", Value::Double(1.1)),
- (r#""float""#, r#"" +inf ""#, Value::Float(f32::INFINITY)),
+ (r#""float""#, r#""INF""#, Value::Float(f32::INFINITY)),
+ (r#""double""#, r#""INF""#, Value::Double(f64::INFINITY)),
+ (r#""float""#, r#""Infinity""#, Value::Float(f32::INFINITY)),
+ (
+ r#""float""#,
+ r#""-Infinity""#,
+ Value::Float(f32::NEG_INFINITY),
+ ),
+ (r#""double""#, r#""Infinity""#, Value::Double(f64::INFINITY)),
(
r#""double""#,
r#""-Infinity""#,
Value::Double(f64::NEG_INFINITY),
),
- (r#""float""#, r#""-NAN""#, Value::Float(f32::NAN)),
- (r#""double""#, r#""-NAN""#, Value::Double(f64::NAN)),
+ (r#""float""#, r#""NaN""#, Value::Float(f32::NAN)),
+ (r#""double""#, r#""NaN""#, Value::Double(f64::NAN)),
(
r#"{"type": "fixed", "name": "F", "size": 2}"#,
r#""a""#,