Unverified Commit 4d3d1938 authored by Russell Cohen's avatar Russell Cohen Committed by GitHub
Browse files

Merge branch 'main' into docs-attr

parents 0bcc1936 b09b02f3
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -91,3 +91,9 @@ message = "All generated docs now include docsrs labels when features are requir
references = ["smithy-rs#3121", "smithy-rs#3295"]
meta = { "breaking" = false, "tada" = true, "bug" = false }
author = "rcoh"

[[smithy-rs]]
message = "[`Number`](https://docs.rs/aws-smithy-types/latest/aws_smithy_types/enum.Number.html) `TryInto` implementations now succesfully convert from `f64` to numeric types when no precision is lost. This fixes some deserialization issues where numbers like `25.0` were sent when `Byte` fields were expected."
references = ["smithy-rs#3294"]
meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "all" }
author = "rcoh"
+41 −12
Original line number Diff line number Diff line
@@ -77,9 +77,7 @@ macro_rules! to_unsigned_integer_converter {
                    Number::NegInt(v) => {
                        Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into())
                    }
                    Number::Float(v) => {
                        Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into())
                    }
                    Number::Float(v) => attempt_lossless!(v, $typ),
                }
            }
        }
@@ -102,9 +100,7 @@ macro_rules! to_signed_integer_converter {
                match value {
                    Number::PosInt(v) => Ok(Self::try_from(v)?),
                    Number::NegInt(v) => Ok(Self::try_from(v)?),
                    Number::Float(v) => {
                        Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into())
                    }
                    Number::Float(v) => attempt_lossless!(v, $typ),
                }
            }
        }
@@ -115,6 +111,17 @@ macro_rules! to_signed_integer_converter {
    };
}

macro_rules! attempt_lossless {
    ($value: expr, $typ: ty) => {{
        let converted = $value as $typ;
        if (converted as f64 == $value) {
            Ok(converted)
        } else {
            Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion($value).into())
        }
    }};
}

/// Converts to a `u64`. The conversion fails if it is lossy.
impl TryFrom<Number> for u64 {
    type Error = TryFromNumberError;
@@ -125,9 +132,7 @@ impl TryFrom<Number> for u64 {
            Number::NegInt(v) => {
                Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into())
            }
            Number::Float(v) => {
                Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into())
            }
            Number::Float(v) => attempt_lossless!(v, u64),
        }
    }
}
@@ -142,9 +147,7 @@ impl TryFrom<Number> for i64 {
        match value {
            Number::PosInt(v) => Ok(Self::try_from(v)?),
            Number::NegInt(v) => Ok(v),
            Number::Float(v) => {
                Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into())
            }
            Number::Float(v) => attempt_lossless!(v, i64),
        }
    }
}
@@ -236,6 +239,7 @@ mod test {
                    }
                ));
            }
            assert_eq!($typ::try_from(Number::Float(25.0)).unwrap(), 25);
        };
    }

@@ -302,6 +306,13 @@ mod test {
                    }
                ));
            }

            let range = || ($typ::MIN..=$typ::MAX);

            for val in range().take(1024).chain(range().rev().take(1024)) {
                assert_eq!(val, $typ::try_from(Number::Float(val as f64)).unwrap());
                $typ::try_from(Number::Float((val as f64) + 0.1)).expect_err("not equivalent");
            }
        };
    }

@@ -318,6 +329,19 @@ mod test {
                }
            ));
        }
        let range = || (i64::MIN..=i64::MAX);

        for val in range().take(1024).chain(range().rev().take(1024)) {
            // if we can actually represent the value
            if ((val as f64) as i64) == val {
                assert_eq!(val, i64::try_from(Number::Float(val as f64)).unwrap());
            }
            let fval = val as f64;
            // at the limits of the range, we don't have this precision
            if (fval + 0.1).fract() != 0.0 {
                i64::try_from(Number::Float((val as f64) + 0.1)).expect_err("not equivalent");
            }
        }
    }

    #[test]
@@ -333,6 +357,11 @@ mod test {
    #[test]
    fn to_i8() {
        to_signed_converter_tests!(i8);
        i8::try_from(Number::Float(-3200000.0)).expect_err("overflow");
        i8::try_from(Number::Float(32.1)).expect_err("imprecise");
        i8::try_from(Number::Float(i8::MAX as f64 + 0.1)).expect_err("imprecise");
        i8::try_from(Number::Float(f64::NAN)).expect_err("nan");
        i8::try_from(Number::Float(f64::INFINITY)).expect_err("nan");
    }

    #[test]