From b7161331f5ded4c3b3a6a0c0fc1f9063190ddfec Mon Sep 17 00:00:00 2001 From: Thomas Cameron Date: Wed, 26 Apr 2023 12:01:49 +0000 Subject: [PATCH 1/7] refactor --- rust-runtime/aws-smithy-types/src/blob.rs | 32 ++ rust-runtime/aws-smithy-types/src/document.rs | 73 +++ rust-runtime/aws-smithy-types/src/lib.rs | 544 +----------------- rust-runtime/aws-smithy-types/src/number.rs | 445 ++++++++++++++ 4 files changed, 557 insertions(+), 537 deletions(-) create mode 100644 rust-runtime/aws-smithy-types/src/blob.rs create mode 100644 rust-runtime/aws-smithy-types/src/document.rs create mode 100644 rust-runtime/aws-smithy-types/src/number.rs diff --git a/rust-runtime/aws-smithy-types/src/blob.rs b/rust-runtime/aws-smithy-types/src/blob.rs new file mode 100644 index 0000000000..bdd335c492 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/blob.rs @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Binary Blob Type +/// +/// Blobs represent protocol-agnostic binary content. +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct Blob { + inner: Vec, +} + +impl Blob { + /// Creates a new blob from the given `input`. + pub fn new>>(input: T) -> Self { + Blob { + inner: input.into(), + } + } + + /// Consumes the `Blob` and returns a `Vec` with its contents. + pub fn into_inner(self) -> Vec { + self.inner + } +} + +impl AsRef<[u8]> for Blob { + fn as_ref(&self) -> &[u8] { + &self.inner + } +} diff --git a/rust-runtime/aws-smithy-types/src/document.rs b/rust-runtime/aws-smithy-types/src/document.rs new file mode 100644 index 0000000000..4bfb641fe3 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/document.rs @@ -0,0 +1,73 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::Number; +use std::collections::HashMap; + +/* ANCHOR: document */ + +/// Document Type +/// +/// Document types represents protocol-agnostic open content that is accessed like JSON data. +/// Open content is useful for modeling unstructured data that has no schema, data that can't be +/// modeled using rigid types, or data that has a schema that evolves outside of the purview of a model. +/// The serialization format of a document is an implementation detail of a protocol. +#[derive(Debug, Clone, PartialEq)] +pub enum Document { + /// JSON object + Object(HashMap), + /// JSON array + Array(Vec), + /// JSON number + Number(Number), + /// JSON string + String(String), + /// JSON boolean + Bool(bool), + /// JSON null + Null, +} + +impl From for Document { + fn from(value: bool) -> Self { + Document::Bool(value) + } +} + +impl From for Document { + fn from(value: String) -> Self { + Document::String(value) + } +} + +impl From> for Document { + fn from(values: Vec) -> Self { + Document::Array(values) + } +} + +impl From> for Document { + fn from(values: HashMap) -> Self { + Document::Object(values) + } +} + +impl From for Document { + fn from(value: u64) -> Self { + Document::Number(Number::PosInt(value)) + } +} + +impl From for Document { + fn from(value: i64) -> Self { + Document::Number(Number::NegInt(value)) + } +} + +impl From for Document { + fn from(value: i32) -> Self { + Document::Number(Number::NegInt(value as i64)) + } +} diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index 2634ff7f39..ffa1c86ea5 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -13,10 +13,6 @@ rust_2018_idioms, unreachable_pub )] - -use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; -use std::collections::HashMap; - pub mod base64; pub mod date_time; pub mod endpoint; @@ -25,543 +21,17 @@ pub mod primitive; pub mod retry; pub mod timeout; -pub use crate::date_time::DateTime; +mod blob; +mod document; +mod number; +pub use blob::Blob; +pub use date_time::DateTime; +pub use document::Document; // TODO(deprecated): Remove deprecated re-export /// Use [error::ErrorMetadata] instead. #[deprecated( note = "`aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`" )] pub use error::ErrorMetadata as Error; - -/// Binary Blob Type -/// -/// Blobs represent protocol-agnostic binary content. -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub struct Blob { - inner: Vec, -} - -impl Blob { - /// Creates a new blob from the given `input`. - pub fn new>>(input: T) -> Self { - Blob { - inner: input.into(), - } - } - - /// Consumes the `Blob` and returns a `Vec` with its contents. - pub fn into_inner(self) -> Vec { - self.inner - } -} - -impl AsRef<[u8]> for Blob { - fn as_ref(&self) -> &[u8] { - &self.inner - } -} - -/* ANCHOR: document */ - -/// Document Type -/// -/// Document types represents protocol-agnostic open content that is accessed like JSON data. -/// Open content is useful for modeling unstructured data that has no schema, data that can't be -/// modeled using rigid types, or data that has a schema that evolves outside of the purview of a model. -/// The serialization format of a document is an implementation detail of a protocol. -#[derive(Debug, Clone, PartialEq)] -pub enum Document { - /// JSON object - Object(HashMap), - /// JSON array - Array(Vec), - /// JSON number - Number(Number), - /// JSON string - String(String), - /// JSON boolean - Bool(bool), - /// JSON null - Null, -} - -impl From for Document { - fn from(value: bool) -> Self { - Document::Bool(value) - } -} - -impl From for Document { - fn from(value: String) -> Self { - Document::String(value) - } -} - -impl From> for Document { - fn from(values: Vec) -> Self { - Document::Array(values) - } -} - -impl From> for Document { - fn from(values: HashMap) -> Self { - Document::Object(values) - } -} - -impl From for Document { - fn from(value: u64) -> Self { - Document::Number(Number::PosInt(value)) - } -} - -impl From for Document { - fn from(value: i64) -> Self { - Document::Number(Number::NegInt(value)) - } -} - -impl From for Document { - fn from(value: i32) -> Self { - Document::Number(Number::NegInt(value as i64)) - } -} - -/// A number type that implements Javascript / JSON semantics, modeled on serde_json: -/// -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Number { - /// Unsigned 64-bit integer value. - PosInt(u64), - /// Signed 64-bit integer value. The wrapped value is _always_ negative. - NegInt(i64), - /// 64-bit floating-point value. - Float(f64), -} - -/* ANCHOR_END: document */ - -impl Number { - /// Converts to an `f64` lossily. - /// Use `Number::try_from` to make the conversion only if it is not lossy. - pub fn to_f64_lossy(self) -> f64 { - match self { - Number::PosInt(v) => v as f64, - Number::NegInt(v) => v as f64, - Number::Float(v) => v, - } - } - - /// Converts to an `f32` lossily. - /// Use `Number::try_from` to make the conversion only if it is not lossy. - pub fn to_f32_lossy(self) -> f32 { - match self { - Number::PosInt(v) => v as f32, - Number::NegInt(v) => v as f32, - Number::Float(v) => v as f32, - } - } -} - -macro_rules! to_unsigned_integer_converter { - ($typ:ident, $styp:expr) => { - #[doc = "Converts to a `"] - #[doc = $styp] - #[doc = "`. This conversion fails if it is lossy."] - impl TryFrom for $typ { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => Ok(Self::try_from(v)?), - Number::NegInt(v) => { - Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) - } - Number::Float(v) => { - Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) - } - } - } - } - }; - - ($typ:ident) => { - to_unsigned_integer_converter!($typ, stringify!($typ)); - }; -} - -macro_rules! to_signed_integer_converter { - ($typ:ident, $styp:expr) => { - #[doc = "Converts to a `"] - #[doc = $styp] - #[doc = "`. This conversion fails if it is lossy."] - impl TryFrom for $typ { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - 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()) - } - } - } - } - }; - - ($typ:ident) => { - to_signed_integer_converter!($typ, stringify!($typ)); - }; -} - -/// Converts to a `u64`. The conversion fails if it is lossy. -impl TryFrom for u64 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => Ok(v), - Number::NegInt(v) => { - Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) - } - Number::Float(v) => { - Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) - } - } - } -} -to_unsigned_integer_converter!(u32); -to_unsigned_integer_converter!(u16); -to_unsigned_integer_converter!(u8); - -impl TryFrom for i64 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => Ok(Self::try_from(v)?), - Number::NegInt(v) => Ok(v), - Number::Float(v) => { - Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) - } - } - } -} -to_signed_integer_converter!(i32); -to_signed_integer_converter!(i16); -to_signed_integer_converter!(i8); - -/// Converts to an `f64`. The conversion fails if it is lossy. -impl TryFrom for f64 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - // Integers can only be represented with full precision in a float if they fit in the - // significand, which is 24 bits in `f32` and 53 bits in `f64`. - // https://github.com/rust-lang/rust/blob/58f11791af4f97572e7afd83f11cffe04bbbd12f/library/core/src/convert/num.rs#L151-L153 - Number::PosInt(v) => { - if v <= (1 << 53) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) - } - } - Number::NegInt(v) => { - if (-(1 << 53)..=(1 << 53)).contains(&v) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) - } - } - Number::Float(v) => Ok(v), - } - } -} - -/// Converts to an `f64`. The conversion fails if it is lossy. -impl TryFrom for f32 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => { - if v <= (1 << 24) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) - } - } - Number::NegInt(v) => { - if (-(1 << 24)..=(1 << 24)).contains(&v) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) - } - } - Number::Float(v) => Err(TryFromNumberErrorKind::F64ToF32LossyConversion(v).into()), - } - } -} - -#[cfg(test)] -mod number { - use super::*; - use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; - - macro_rules! to_unsigned_converter_tests { - ($typ:ident) => { - assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); - - assert!(matches!( - $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::OutsideIntegerRange(..) - } - )); - - assert!(matches!( - $typ::try_from(Number::NegInt(-1i64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) - } - )); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - $typ::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - }; - } - - #[test] - fn to_u64() { - assert_eq!(u64::try_from(Number::PosInt(69u64)).unwrap(), 69u64); - - assert!(matches!( - u64::try_from(Number::NegInt(-1i64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) - } - )); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - u64::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - } - - #[test] - fn to_u32() { - to_unsigned_converter_tests!(u32); - } - - #[test] - fn to_u16() { - to_unsigned_converter_tests!(u16); - } - - #[test] - fn to_u8() { - to_unsigned_converter_tests!(u8); - } - - macro_rules! to_signed_converter_tests { - ($typ:ident) => { - assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); - assert_eq!($typ::try_from(Number::NegInt(-69i64)).unwrap(), -69); - - assert!(matches!( - $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::OutsideIntegerRange(..) - } - )); - - assert!(matches!( - $typ::try_from(Number::NegInt(($typ::MIN as i64) - 1i64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::OutsideIntegerRange(..) - } - )); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - u64::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - }; - } - - #[test] - fn to_i64() { - assert_eq!(i64::try_from(Number::PosInt(69u64)).unwrap(), 69); - assert_eq!(i64::try_from(Number::NegInt(-69i64)).unwrap(), -69); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - u64::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - } - - #[test] - fn to_i32() { - to_signed_converter_tests!(i32); - } - - #[test] - fn to_i16() { - to_signed_converter_tests!(i16); - } - - #[test] - fn to_i8() { - to_signed_converter_tests!(i8); - } - - #[test] - fn to_f64() { - assert_eq!(f64::try_from(Number::PosInt(69u64)).unwrap(), 69f64); - assert_eq!(f64::try_from(Number::NegInt(-69i64)).unwrap(), -69f64); - assert_eq!(f64::try_from(Number::Float(-69f64)).unwrap(), -69f64); - assert!(f64::try_from(Number::Float(f64::NAN)).unwrap().is_nan()); - assert_eq!( - f64::try_from(Number::Float(f64::INFINITY)).unwrap(), - f64::INFINITY - ); - assert_eq!( - f64::try_from(Number::Float(f64::NEG_INFINITY)).unwrap(), - f64::NEG_INFINITY - ); - - let significand_max_u64: u64 = 1 << 53; - let significand_max_i64: i64 = 1 << 53; - - assert_eq!( - f64::try_from(Number::PosInt(significand_max_u64)).unwrap(), - 9007199254740992f64 - ); - - assert_eq!( - f64::try_from(Number::NegInt(significand_max_i64)).unwrap(), - 9007199254740992f64 - ); - assert_eq!( - f64::try_from(Number::NegInt(-significand_max_i64)).unwrap(), - -9007199254740992f64 - ); - - assert!(matches!( - f64::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) - } - )); - - assert!(matches!( - f64::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - assert!(matches!( - f64::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - } - - #[test] - fn to_f32() { - assert_eq!(f32::try_from(Number::PosInt(69u64)).unwrap(), 69f32); - assert_eq!(f32::try_from(Number::NegInt(-69i64)).unwrap(), -69f32); - - let significand_max_u64: u64 = 1 << 24; - let significand_max_i64: i64 = 1 << 24; - - assert_eq!( - f32::try_from(Number::PosInt(significand_max_u64)).unwrap(), - 16777216f32 - ); - - assert_eq!( - f32::try_from(Number::NegInt(significand_max_i64)).unwrap(), - 16777216f32 - ); - assert_eq!( - f32::try_from(Number::NegInt(-significand_max_i64)).unwrap(), - -16777216f32 - ); - - assert!(matches!( - f32::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) - } - )); - - assert!(matches!( - f32::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - assert!(matches!( - f32::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - - for val in [69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - f32::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::F64ToF32LossyConversion(..) - } - )); - } - } - - #[test] - fn to_f64_lossy() { - assert_eq!(Number::PosInt(69u64).to_f64_lossy(), 69f64); - assert_eq!( - Number::PosInt((1 << 53) + 1).to_f64_lossy(), - 9007199254740992f64 - ); - assert_eq!( - Number::NegInt(-(1 << 53) - 1).to_f64_lossy(), - -9007199254740992f64 - ); - } - - #[test] - fn to_f32_lossy() { - assert_eq!(Number::PosInt(69u64).to_f32_lossy(), 69f32); - assert_eq!(Number::PosInt((1 << 24) + 1).to_f32_lossy(), 16777216f32); - assert_eq!(Number::NegInt(-(1 << 24) - 1).to_f32_lossy(), -16777216f32); - assert_eq!( - Number::Float(1452089033.7674935).to_f32_lossy(), - 1452089100f32 - ); - } -} +pub use number::Number; diff --git a/rust-runtime/aws-smithy-types/src/number.rs b/rust-runtime/aws-smithy-types/src/number.rs new file mode 100644 index 0000000000..5a3cdd2b48 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/number.rs @@ -0,0 +1,445 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + + +use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; + +/// A number type that implements Javascript / JSON semantics, modeled on serde_json: +/// +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Number { + /// Unsigned 64-bit integer value. + PosInt(u64), + /// Signed 64-bit integer value. The wrapped value is _always_ negative. + NegInt(i64), + /// 64-bit floating-point value. + Float(f64), +} + +/* ANCHOR_END: document */ + +impl Number { + /// Converts to an `f64` lossily. + /// Use `Number::try_from` to make the conversion only if it is not lossy. + pub fn to_f64_lossy(self) -> f64 { + match self { + Number::PosInt(v) => v as f64, + Number::NegInt(v) => v as f64, + Number::Float(v) => v, + } + } + + /// Converts to an `f32` lossily. + /// Use `Number::try_from` to make the conversion only if it is not lossy. + pub fn to_f32_lossy(self) -> f32 { + match self { + Number::PosInt(v) => v as f32, + Number::NegInt(v) => v as f32, + Number::Float(v) => v as f32, + } + } +} + +macro_rules! to_unsigned_integer_converter { + ($typ:ident, $styp:expr) => { + #[doc = "Converts to a `"] + #[doc = $styp] + #[doc = "`. This conversion fails if it is lossy."] + impl TryFrom for $typ { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => Ok(Self::try_from(v)?), + Number::NegInt(v) => { + Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) + } + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } + } + } + } + }; + + ($typ:ident) => { + to_unsigned_integer_converter!($typ, stringify!($typ)); + }; +} + +macro_rules! to_signed_integer_converter { + ($typ:ident, $styp:expr) => { + #[doc = "Converts to a `"] + #[doc = $styp] + #[doc = "`. This conversion fails if it is lossy."] + impl TryFrom for $typ { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + 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()) + } + } + } + } + }; + + ($typ:ident) => { + to_signed_integer_converter!($typ, stringify!($typ)); + }; +} + +/// Converts to a `u64`. The conversion fails if it is lossy. +impl TryFrom for u64 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => Ok(v), + Number::NegInt(v) => { + Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) + } + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } + } + } +} +to_unsigned_integer_converter!(u32); +to_unsigned_integer_converter!(u16); +to_unsigned_integer_converter!(u8); + +impl TryFrom for i64 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => Ok(Self::try_from(v)?), + Number::NegInt(v) => Ok(v), + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } + } + } +} +to_signed_integer_converter!(i32); +to_signed_integer_converter!(i16); +to_signed_integer_converter!(i8); + +/// Converts to an `f64`. The conversion fails if it is lossy. +impl TryFrom for f64 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + // Integers can only be represented with full precision in a float if they fit in the + // significand, which is 24 bits in `f32` and 53 bits in `f64`. + // https://github.com/rust-lang/rust/blob/58f11791af4f97572e7afd83f11cffe04bbbd12f/library/core/src/convert/num.rs#L151-L153 + Number::PosInt(v) => { + if v <= (1 << 53) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) + } + } + Number::NegInt(v) => { + if (-(1 << 53)..=(1 << 53)).contains(&v) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) + } + } + Number::Float(v) => Ok(v), + } + } +} + +/// Converts to an `f64`. The conversion fails if it is lossy. +impl TryFrom for f32 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => { + if v <= (1 << 24) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) + } + } + Number::NegInt(v) => { + if (-(1 << 24)..=(1 << 24)).contains(&v) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) + } + } + Number::Float(v) => Err(TryFromNumberErrorKind::F64ToF32LossyConversion(v).into()), + } + } +} + +#[cfg(test)] +mod number { + use super::*; + use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; + + macro_rules! to_unsigned_converter_tests { + ($typ:ident) => { + assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); + + assert!(matches!( + $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::OutsideIntegerRange(..) + } + )); + + assert!(matches!( + $typ::try_from(Number::NegInt(-1i64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) + } + )); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + $typ::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + }; + } + + #[test] + fn to_u64() { + assert_eq!(u64::try_from(Number::PosInt(69u64)).unwrap(), 69u64); + + assert!(matches!( + u64::try_from(Number::NegInt(-1i64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) + } + )); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + u64::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + } + + #[test] + fn to_u32() { + to_unsigned_converter_tests!(u32); + } + + #[test] + fn to_u16() { + to_unsigned_converter_tests!(u16); + } + + #[test] + fn to_u8() { + to_unsigned_converter_tests!(u8); + } + + macro_rules! to_signed_converter_tests { + ($typ:ident) => { + assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); + assert_eq!($typ::try_from(Number::NegInt(-69i64)).unwrap(), -69); + + assert!(matches!( + $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::OutsideIntegerRange(..) + } + )); + + assert!(matches!( + $typ::try_from(Number::NegInt(($typ::MIN as i64) - 1i64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::OutsideIntegerRange(..) + } + )); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + u64::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + }; + } + + #[test] + fn to_i64() { + assert_eq!(i64::try_from(Number::PosInt(69u64)).unwrap(), 69); + assert_eq!(i64::try_from(Number::NegInt(-69i64)).unwrap(), -69); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + u64::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + } + + #[test] + fn to_i32() { + to_signed_converter_tests!(i32); + } + + #[test] + fn to_i16() { + to_signed_converter_tests!(i16); + } + + #[test] + fn to_i8() { + to_signed_converter_tests!(i8); + } + + #[test] + fn to_f64() { + assert_eq!(f64::try_from(Number::PosInt(69u64)).unwrap(), 69f64); + assert_eq!(f64::try_from(Number::NegInt(-69i64)).unwrap(), -69f64); + assert_eq!(f64::try_from(Number::Float(-69f64)).unwrap(), -69f64); + assert!(f64::try_from(Number::Float(f64::NAN)).unwrap().is_nan()); + assert_eq!( + f64::try_from(Number::Float(f64::INFINITY)).unwrap(), + f64::INFINITY + ); + assert_eq!( + f64::try_from(Number::Float(f64::NEG_INFINITY)).unwrap(), + f64::NEG_INFINITY + ); + + let significand_max_u64: u64 = 1 << 53; + let significand_max_i64: i64 = 1 << 53; + + assert_eq!( + f64::try_from(Number::PosInt(significand_max_u64)).unwrap(), + 9007199254740992f64 + ); + + assert_eq!( + f64::try_from(Number::NegInt(significand_max_i64)).unwrap(), + 9007199254740992f64 + ); + assert_eq!( + f64::try_from(Number::NegInt(-significand_max_i64)).unwrap(), + -9007199254740992f64 + ); + + assert!(matches!( + f64::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) + } + )); + + assert!(matches!( + f64::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + assert!(matches!( + f64::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + } + + #[test] + fn to_f32() { + assert_eq!(f32::try_from(Number::PosInt(69u64)).unwrap(), 69f32); + assert_eq!(f32::try_from(Number::NegInt(-69i64)).unwrap(), -69f32); + + let significand_max_u64: u64 = 1 << 24; + let significand_max_i64: i64 = 1 << 24; + + assert_eq!( + f32::try_from(Number::PosInt(significand_max_u64)).unwrap(), + 16777216f32 + ); + + assert_eq!( + f32::try_from(Number::NegInt(significand_max_i64)).unwrap(), + 16777216f32 + ); + assert_eq!( + f32::try_from(Number::NegInt(-significand_max_i64)).unwrap(), + -16777216f32 + ); + + assert!(matches!( + f32::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) + } + )); + + assert!(matches!( + f32::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + assert!(matches!( + f32::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + + for val in [69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + f32::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::F64ToF32LossyConversion(..) + } + )); + } + } + + #[test] + fn to_f64_lossy() { + assert_eq!(Number::PosInt(69u64).to_f64_lossy(), 69f64); + assert_eq!( + Number::PosInt((1 << 53) + 1).to_f64_lossy(), + 9007199254740992f64 + ); + assert_eq!( + Number::NegInt(-(1 << 53) - 1).to_f64_lossy(), + -9007199254740992f64 + ); + } + + #[test] + fn to_f32_lossy() { + assert_eq!(Number::PosInt(69u64).to_f32_lossy(), 69f32); + assert_eq!(Number::PosInt((1 << 24) + 1).to_f32_lossy(), 16777216f32); + assert_eq!(Number::NegInt(-(1 << 24) - 1).to_f32_lossy(), -16777216f32); + assert_eq!( + Number::Float(1452089033.7674935).to_f32_lossy(), + 1452089100f32 + ); + } +} From a5662c0be8bc6e9ff5cbeac9d9b112359ed085cd Mon Sep 17 00:00:00 2001 From: Thomas Cameron Date: Wed, 26 Apr 2023 12:02:35 +0000 Subject: [PATCH 2/7] chore: update CHANGELOG.next.toml --- CHANGELOG.next.toml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 1832de5613..7f23e0217a 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -92,3 +92,15 @@ message = "Update the `std::fmt::Debug` implementation for `aws-sigv4::SigningPa references = ["smithy-rs#2562"] meta = { "breaking" = false, "tada" = true, "bug" = true } author = "Velfi" + +[[aws-sdk-rust]] +message = "Refactor aws-smithy-types crate. Blob, Datetime, Number and Document structs now goes to it's own files. No breaking changes." +references = ["smithy-rs#2616"] +meta = { "breaking" = false, "tada" = false, "bug" = true } +author = "thomas-k-cameron" + +[[smithy-rs]] +message = "Refactor aws-smithy-types crate. Blob, Datetime, Number and Document structs now goes to it's own files. No breaking changes." +references = ["smithy-rs#2616"] +meta = { "breaking" = false, "tada" = false, "bug" = true } +author = "thomas-k-cameron" From deeaaef3af7e5afb8aa0ecb553d98687b2e6d67b Mon Sep 17 00:00:00 2001 From: Thomas Cameron <68596478+thomas-k-cameron@users.noreply.github.com> Date: Wed, 26 Apr 2023 21:10:50 +0900 Subject: [PATCH 3/7] Update CHANGELOG.next.toml --- CHANGELOG.next.toml | 47 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index c7a95ea6e8..bfc4547178 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -120,6 +120,53 @@ references = ["smithy-rs#2562"] meta = { "breaking" = false, "tada" = true, "bug" = true } author = "Velfi" +[[aws-sdk-rust]] +[[aws-sdk-rust]] +message = "`aws_smithy_types::date_time::Format` has been re-exported in SDK crates." +references = ["smithy-rs#2534"] +meta = { "breaking" = false, "tada" = false, "bug" = false } +author = "ysaito1001" + +[[smithy-rs]] +message = "`aws_smithy_types::date_time::Format` has been re-exported in service client crates." +references = ["smithy-rs#2534"] +meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } +author = "ysaito1001" + +[[smithy-rs]] +message = "Fix generation of constrained shapes reaching `@sensitive` shapes" +references = ["smithy-rs#2582", "smithy-rs#2585"] +meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" } +author = "david-perez" + +[[smithy-rs]] +message = "Fix server code generation bug affecting constrained shapes bound with `@httpPayload`" +references = ["smithy-rs#2583", "smithy-rs#2584"] +meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" } +author = "david-perez" + +[[aws-sdk-rust]] +message = """Reduce several instances of credential exposure in the SDK logs: +- IMDS now suppresses the body of the response from logs +- `aws-sigv4` marks the `x-amz-session-token` header as sensitive +- STS & SSO credentials have been manually marked as sensitive which suppresses logging of response bodies for relevant operations +""" +author = "rcoh" +references = ["smithy-rs#2603"] +meta = { "breaking" = false, "tada" = false, "bug" = false } + +[[smithy-rs]] +message = "Add a sensitive method to `ParseHttpResponse`. When this returns true, logging of the HTTP response body will be suppressed." +author = "rcoh" +references = ["smithy-rs#2603"] +meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } + +[[aws-sdk-rust]] +message = "Update MSRV to Rust 1.67.1" +references = ["smithy-rs#2611"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "jdisanti" + [[aws-sdk-rust]] message = "Refactor aws-smithy-types crate. Blob, Datetime, Number and Document structs now goes to it's own files. No breaking changes." references = ["smithy-rs#2616"] From 3fd99b32ded0110e7019653b21bc9fe269293ff2 Mon Sep 17 00:00:00 2001 From: Thomas Cameron <68596478+thomas-k-cameron@users.noreply.github.com> Date: Wed, 26 Apr 2023 21:11:35 +0900 Subject: [PATCH 4/7] Update CHANGELOG.next.toml --- CHANGELOG.next.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index bfc4547178..befedb9740 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -120,7 +120,6 @@ references = ["smithy-rs#2562"] meta = { "breaking" = false, "tada" = true, "bug" = true } author = "Velfi" -[[aws-sdk-rust]] [[aws-sdk-rust]] message = "`aws_smithy_types::date_time::Format` has been re-exported in SDK crates." references = ["smithy-rs#2534"] From d4eddae0bfe289c545c0a23912f9fb6ebb19a324 Mon Sep 17 00:00:00 2001 From: Thomas Cameron <68596478+thomas-k-cameron@users.noreply.github.com> Date: Wed, 26 Apr 2023 21:12:08 +0900 Subject: [PATCH 5/7] Update CHANGELOG.next.toml --- CHANGELOG.next.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index befedb9740..0312be181c 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -166,6 +166,12 @@ references = ["smithy-rs#2611"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "jdisanti" +[[smithy-rs]] +message = "Update MSRV to Rust 1.67.1" +references = ["smithy-rs#2611"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all"} +author = "jdisanti" + [[aws-sdk-rust]] message = "Refactor aws-smithy-types crate. Blob, Datetime, Number and Document structs now goes to it's own files. No breaking changes." references = ["smithy-rs#2616"] From 9d7e8d27ddac242f971ebdb32eacebb5f9aae655 Mon Sep 17 00:00:00 2001 From: Thomas Cameron Date: Wed, 26 Apr 2023 12:57:11 +0000 Subject: [PATCH 6/7] FIX precommit --- rust-runtime/aws-smithy-types/src/number.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust-runtime/aws-smithy-types/src/number.rs b/rust-runtime/aws-smithy-types/src/number.rs index 5a3cdd2b48..76fc08a218 100644 --- a/rust-runtime/aws-smithy-types/src/number.rs +++ b/rust-runtime/aws-smithy-types/src/number.rs @@ -3,9 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ - use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; - + /// A number type that implements Javascript / JSON semantics, modeled on serde_json: /// #[derive(Debug, Clone, Copy, PartialEq)] From 468ce35ad4f4acf3c001f13a816147311dd26702 Mon Sep 17 00:00:00 2001 From: Thomas Cameron Date: Wed, 26 Apr 2023 13:01:56 +0000 Subject: [PATCH 7/7] FIX --- CHANGELOG.next.toml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 0312be181c..a03141576a 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -171,15 +171,3 @@ message = "Update MSRV to Rust 1.67.1" references = ["smithy-rs#2611"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all"} author = "jdisanti" - -[[aws-sdk-rust]] -message = "Refactor aws-smithy-types crate. Blob, Datetime, Number and Document structs now goes to it's own files. No breaking changes." -references = ["smithy-rs#2616"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "thomas-k-cameron" - -[[smithy-rs]] -message = "Refactor aws-smithy-types crate. Blob, Datetime, Number and Document structs now goes to it's own files. No breaking changes." -references = ["smithy-rs#2616"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "thomas-k-cameron"