diff --git a/aws/rust-runtime/aws-config/external-types.toml b/aws/rust-runtime/aws-config/external-types.toml index 9c85536c88..ade75eb332 100644 --- a/aws/rust-runtime/aws-config/external-types.toml +++ b/aws/rust-runtime/aws-config/external-types.toml @@ -16,8 +16,9 @@ allowed_external_types = [ "aws_smithy_types::retry", "aws_smithy_types::retry::*", "aws_smithy_types::timeout", - "aws_smithy_types::timeout::config::TimeoutConfig", - "aws_smithy_types::timeout::error::ConfigError", + "aws_smithy_types::timeout::OperationTimeoutConfig", + "aws_smithy_types::timeout::TimeoutConfig", + "aws_smithy_types::timeout::TimeoutConfigBuilder", "aws_types::*", "http::response::Response", "http::uri::Uri", diff --git a/aws/rust-runtime/aws-config/src/default_provider.rs b/aws/rust-runtime/aws-config/src/default_provider.rs index 917171b926..e1798f97b5 100644 --- a/aws/rust-runtime/aws-config/src/default_provider.rs +++ b/aws/rust-runtime/aws-config/src/default_provider.rs @@ -3,8 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Default Provider chains for [`region`](default_provider::region), [`credentials`](default_provider::credentials), -//! [retries](default_provider::retry_config), [timeouts](default_provider::timeout_config) and [app name](default_provider::app_name). +//! Providers that implement the default AWS provider chain +//! +//! Default Provider chains for [`region`](crate::default_provider::region), [`credentials`](crate::default_provider::credentials), +//! [retries](crate::default_provider::retry_config), [timeouts](crate::default_provider::timeout_config) and +//! [app name](crate::default_provider::app_name). //! //! Typically, this module is used via [`load_from_env`](crate::load_from_env) or [`from_env`](crate::from_env). It should only be used directly //! if you need to set custom configuration options to override the default resolution chain. diff --git a/aws/rust-runtime/aws-config/src/default_provider/retry_config.rs b/aws/rust-runtime/aws-config/src/default_provider/retry_config.rs index 3da892ebce..1d5b330101 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/retry_config.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/retry_config.rs @@ -3,11 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_types::retry::RetryConfig; - use crate::environment::retry_config::EnvironmentVariableRetryConfigProvider; use crate::profile; use crate::provider_config::ProviderConfig; +use aws_smithy_types::error::display::DisplayErrorContext; +use aws_smithy_types::retry::RetryConfig; /// Default RetryConfig Provider chain /// @@ -90,11 +90,11 @@ impl Builder { // We match this instead of unwrapping so we can print the error with the `Display` impl instead of the `Debug` impl that unwrap uses let builder_from_env = match self.env_provider.retry_config_builder() { Ok(retry_config_builder) => retry_config_builder, - Err(err) => panic!("{}", err), + Err(err) => panic!("{}", DisplayErrorContext(&err)), }; let builder_from_profile = match self.profile_file.build().retry_config_builder().await { Ok(retry_config_builder) => retry_config_builder, - Err(err) => panic!("{}", err), + Err(err) => panic!("{}", DisplayErrorContext(&err)), }; builder_from_env diff --git a/aws/rust-runtime/aws-config/src/environment/mod.rs b/aws/rust-runtime/aws-config/src/environment/mod.rs index 26cbda6b09..dd6d4cb597 100644 --- a/aws/rust-runtime/aws-config/src/environment/mod.rs +++ b/aws/rust-runtime/aws-config/src/environment/mod.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Providers that load configuration from environment variables + /// Load app name from the environment pub mod app_name; pub use app_name::EnvironmentVariableAppNameProvider; diff --git a/aws/rust-runtime/aws-config/src/environment/retry_config.rs b/aws/rust-runtime/aws-config/src/environment/retry_config.rs index f0ed984864..5afc64a19c 100644 --- a/aws/rust-runtime/aws-config/src/environment/retry_config.rs +++ b/aws/rust-runtime/aws-config/src/environment/retry_config.rs @@ -3,10 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::str::FromStr; - -use aws_smithy_types::retry::{RetryConfigBuilder, RetryConfigErr, RetryMode}; +use crate::retry::{ + error::RetryConfigError, error::RetryConfigErrorKind, RetryConfigBuilder, RetryMode, +}; use aws_types::os_shim_internal::Env; +use std::str::FromStr; const ENV_VAR_MAX_ATTEMPTS: &str = "AWS_MAX_ATTEMPTS"; const ENV_VAR_RETRY_MODE: &str = "AWS_RETRY_MODE"; @@ -35,20 +36,22 @@ impl EnvironmentVariableRetryConfigProvider { } /// Attempt to create a new `RetryConfig` from environment variables - pub fn retry_config_builder(&self) -> Result { + pub fn retry_config_builder(&self) -> Result { let max_attempts = match self.env.get(ENV_VAR_MAX_ATTEMPTS).ok() { Some(max_attempts) => match max_attempts.parse::() { Ok(max_attempts) if max_attempts == 0 => { - return Err(RetryConfigErr::MaxAttemptsMustNotBeZero { + return Err(RetryConfigErrorKind::MaxAttemptsMustNotBeZero { set_by: "environment variable".into(), - }); + } + .into()); } Ok(max_attempts) => Some(max_attempts), Err(source) => { - return Err(RetryConfigErr::FailedToParseMaxAttempts { + return Err(RetryConfigErrorKind::FailedToParseMaxAttempts { set_by: "environment variable".into(), source, - }); + } + .into()); } }, None => None, @@ -58,10 +61,11 @@ impl EnvironmentVariableRetryConfigProvider { Ok(retry_mode) => match RetryMode::from_str(&retry_mode) { Ok(retry_mode) => Some(retry_mode), Err(retry_mode_err) => { - return Err(RetryConfigErr::InvalidRetryMode { + return Err(RetryConfigErrorKind::InvalidRetryMode { set_by: "environment variable".into(), source: retry_mode_err, - }); + } + .into()); } }, Err(_) => None, @@ -78,10 +82,11 @@ impl EnvironmentVariableRetryConfigProvider { #[cfg(test)] mod test { - use aws_smithy_types::retry::{RetryConfig, RetryConfigErr, RetryMode}; - use aws_types::os_shim_internal::Env; - use super::{EnvironmentVariableRetryConfigProvider, ENV_VAR_MAX_ATTEMPTS, ENV_VAR_RETRY_MODE}; + use crate::retry::{ + error::RetryConfigError, error::RetryConfigErrorKind, RetryConfig, RetryMode, + }; + use aws_types::os_shim_internal::Env; fn test_provider(vars: &[(&str, &str)]) -> EnvironmentVariableRetryConfigProvider { EnvironmentVariableRetryConfigProvider::new_with_env(Env::from_slice(vars)) @@ -112,7 +117,9 @@ mod test { test_provider(&[(ENV_VAR_MAX_ATTEMPTS, "not an integer")]) .retry_config_builder() .unwrap_err(), - RetryConfigErr::FailedToParseMaxAttempts { .. } + RetryConfigError { + kind: RetryConfigErrorKind::FailedToParseMaxAttempts { .. } + } )); } @@ -148,7 +155,9 @@ mod test { .unwrap_err(); assert!(matches!( err, - RetryConfigErr::MaxAttemptsMustNotBeZero { .. } + RetryConfigError { + kind: RetryConfigErrorKind::MaxAttemptsMustNotBeZero { .. } + } )); } } diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 73a492bb26..e767203c06 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -11,7 +11,7 @@ unreachable_pub )] -//! `aws-config` provides implementations of region, credential resolution. +//! `aws-config` provides implementations of region and credential resolution. //! //! These implementations can be used either via the default chain implementation //! [`from_env`]/[`ConfigLoader`] or ad-hoc individual credential and region providers. @@ -92,9 +92,6 @@ //! ``` pub use aws_smithy_http::endpoint; -// Re-export types from smithy-types -pub use aws_smithy_types::retry; -pub use aws_smithy_types::timeout; // Re-export types from aws-types pub use aws_types::{ app_name::{AppName, InvalidAppName}, @@ -106,43 +103,28 @@ pub use loader::ConfigLoader; #[allow(dead_code)] const PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); -/// Providers that implement the default AWS provider chain -pub mod default_provider; - -/// Providers that load configuration from environment variables -pub mod environment; - -/// Meta-providers that augment existing providers with new behavior -pub mod meta; - -pub mod profile; - -pub mod sts; - #[cfg(test)] mod test_case; -pub mod web_identity_token; - -pub mod ecs; - -pub mod provider_config; - mod cache; - -pub mod imds; - -mod json_credentials; - mod fs_util; - mod http_credential_provider; - -pub mod sso; +mod json_credentials; pub mod connector; - pub mod credential_process; +pub mod default_provider; +pub mod ecs; +pub mod environment; +pub mod imds; +pub mod meta; +pub mod profile; +pub mod provider_config; +pub mod retry; +pub mod sso; +pub mod sts; +pub mod timeout; +pub mod web_identity_token; /// Create an environment loader for AWS Configuration /// diff --git a/aws/rust-runtime/aws-config/src/meta/mod.rs b/aws/rust-runtime/aws-config/src/meta/mod.rs index fbc08e45c4..f091a26c0f 100644 --- a/aws/rust-runtime/aws-config/src/meta/mod.rs +++ b/aws/rust-runtime/aws-config/src/meta/mod.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -pub mod region; +//! Meta-providers that augment existing providers with new behavior pub mod credentials; +pub mod region; diff --git a/aws/rust-runtime/aws-config/src/profile/parser/parse.rs b/aws/rust-runtime/aws-config/src/profile/parser/parse.rs index 1d3c3acb05..4d5999ceec 100644 --- a/aws/rust-runtime/aws-config/src/profile/parser/parse.rs +++ b/aws/rust-runtime/aws-config/src/profile/parser/parse.rs @@ -36,7 +36,7 @@ struct Location { } /// An error encountered while parsing a profile -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ProfileParseError { /// Location where this error occurred location: Location, diff --git a/aws/rust-runtime/aws-config/src/profile/retry_config.rs b/aws/rust-runtime/aws-config/src/profile/retry_config.rs index 0b097f78f8..8ec089afa8 100644 --- a/aws/rust-runtime/aws-config/src/profile/retry_config.rs +++ b/aws/rust-runtime/aws-config/src/profile/retry_config.rs @@ -7,8 +7,10 @@ use crate::profile::profile_file::ProfileFiles; use crate::provider_config::ProviderConfig; +use crate::retry::{ + error::RetryConfigError, error::RetryConfigErrorKind, RetryConfigBuilder, RetryMode, +}; use aws_smithy_types::error::display::DisplayErrorContext; -use aws_smithy_types::retry::{RetryConfigBuilder, RetryConfigErr, RetryMode}; use aws_types::os_shim_internal::{Env, Fs}; use std::str::FromStr; @@ -101,7 +103,7 @@ impl ProfileFileRetryConfigProvider { } /// Attempt to create a new RetryConfigBuilder from a profile file. - pub async fn retry_config_builder(&self) -> Result { + pub async fn retry_config_builder(&self) -> Result { let profile = match super::parser::load(&self.fs, &self.env, &self.profile_files).await { Ok(profile) => profile, Err(err) => { @@ -130,16 +132,18 @@ impl ProfileFileRetryConfigProvider { let max_attempts = match selected_profile.get("max_attempts") { Some(max_attempts) => match max_attempts.parse::() { Ok(max_attempts) if max_attempts == 0 => { - return Err(RetryConfigErr::MaxAttemptsMustNotBeZero { + return Err(RetryConfigErrorKind::MaxAttemptsMustNotBeZero { set_by: "aws profile".into(), - }); + } + .into()); } Ok(max_attempts) => Some(max_attempts), Err(source) => { - return Err(RetryConfigErr::FailedToParseMaxAttempts { + return Err(RetryConfigErrorKind::FailedToParseMaxAttempts { set_by: "aws profile".into(), source, - }); + } + .into()); } }, None => None, @@ -149,10 +153,11 @@ impl ProfileFileRetryConfigProvider { Some(retry_mode) => match RetryMode::from_str(retry_mode) { Ok(retry_mode) => Some(retry_mode), Err(retry_mode_err) => { - return Err(RetryConfigErr::InvalidRetryMode { + return Err(RetryConfigErrorKind::InvalidRetryMode { set_by: "aws profile".into(), source: retry_mode_err, - }); + } + .into()); } }, None => None, diff --git a/aws/rust-runtime/aws-config/src/retry.rs b/aws/rust-runtime/aws-config/src/retry.rs new file mode 100644 index 0000000000..6095b1e1ab --- /dev/null +++ b/aws/rust-runtime/aws-config/src/retry.rs @@ -0,0 +1,87 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Retry configuration + +// Re-export from aws-smithy-types +pub use aws_smithy_types::retry::ErrorKind; +pub use aws_smithy_types::retry::ProvideErrorKind; +pub use aws_smithy_types::retry::RetryConfig; +pub use aws_smithy_types::retry::RetryConfigBuilder; +pub use aws_smithy_types::retry::RetryKind; +pub use aws_smithy_types::retry::RetryMode; + +/// Errors for retry configuration +pub mod error { + use std::borrow::Cow; + use std::fmt; + use std::num::ParseIntError; + + // Re-export from aws-smithy-types + pub use aws_smithy_types::retry::RetryModeParseError; + + #[derive(Debug)] + pub(crate) enum RetryConfigErrorKind { + /// The configured retry mode wasn't recognized. + InvalidRetryMode { + /// Cause of the error. + source: RetryModeParseError, + /// Where the invalid retry mode value originated from. + set_by: Cow<'static, str>, + }, + /// Max attempts must be greater than zero. + MaxAttemptsMustNotBeZero { + /// Where the invalid max attempts value originated from. + set_by: Cow<'static, str>, + }, + /// The max attempts value couldn't be parsed to an integer. + FailedToParseMaxAttempts { + /// Cause of the error. + source: ParseIntError, + /// Where the invalid max attempts value originated from. + set_by: Cow<'static, str>, + }, + } + + /// Failure to parse retry config from profile file or environment variable. + #[derive(Debug)] + pub struct RetryConfigError { + pub(crate) kind: RetryConfigErrorKind, + } + + impl fmt::Display for RetryConfigError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use RetryConfigErrorKind::*; + match &self.kind { + InvalidRetryMode { set_by, .. } => { + write!(f, "invalid configuration set by {set_by}") + } + MaxAttemptsMustNotBeZero { set_by } => { + write!(f, "invalid configuration set by {set_by}: It is invalid to set max attempts to 0. Unset it or set it to an integer greater than or equal to one.") + } + FailedToParseMaxAttempts { set_by, .. } => { + write!(f, "failed to parse max attempts set by {set_by}",) + } + } + } + } + + impl std::error::Error for RetryConfigError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use RetryConfigErrorKind::*; + match &self.kind { + InvalidRetryMode { source, .. } => Some(source), + FailedToParseMaxAttempts { source, .. } => Some(source), + MaxAttemptsMustNotBeZero { .. } => None, + } + } + } + + impl From for RetryConfigError { + fn from(kind: RetryConfigErrorKind) -> Self { + Self { kind } + } + } +} diff --git a/aws/rust-runtime/aws-config/src/timeout.rs b/aws/rust-runtime/aws-config/src/timeout.rs new file mode 100644 index 0000000000..1409e60332 --- /dev/null +++ b/aws/rust-runtime/aws-config/src/timeout.rs @@ -0,0 +1,11 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Timeout configuration + +// Re-export from aws-smithy-types +pub use aws_smithy_types::timeout::OperationTimeoutConfig; +pub use aws_smithy_types::timeout::TimeoutConfig; +pub use aws_smithy_types::timeout::TimeoutConfigBuilder; diff --git a/aws/rust-runtime/aws-types/external-types.toml b/aws/rust-runtime/aws-types/external-types.toml index 273584842e..051e0986ec 100644 --- a/aws/rust-runtime/aws-types/external-types.toml +++ b/aws/rust-runtime/aws-types/external-types.toml @@ -5,6 +5,6 @@ allowed_external_types = [ "aws_smithy_http::endpoint::Endpoint", "aws_smithy_http::endpoint::EndpointPrefix", "aws_smithy_types::retry::RetryConfig", - "aws_smithy_types::timeout::config::TimeoutConfig", + "aws_smithy_types::timeout::TimeoutConfig", "http::uri::Uri", ] diff --git a/rust-runtime/aws-smithy-async/src/future/timeout.rs b/rust-runtime/aws-smithy-async/src/future/timeout.rs index f996321dd7..59e4409015 100644 --- a/rust-runtime/aws-smithy-async/src/future/timeout.rs +++ b/rust-runtime/aws-smithy-async/src/future/timeout.rs @@ -43,7 +43,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; /// Error returned when [`Timeout`] times out -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug)] pub struct TimedOutError; impl Error for TimedOutError {} @@ -106,28 +106,31 @@ mod tests { #[tokio::test] async fn success() { - assert_eq!( - Ok(Ok(5)), - Timeout::new(async { Ok::(5) }, Never).await - ); + assert!(matches!( + Timeout::new(async { Ok::(5) }, Never).await, + Ok(Ok(5)) + )); } #[tokio::test] async fn failure() { - assert_eq!( - Ok(Err(0)), - Timeout::new(async { Err::(0) }, Never).await - ); + assert!(matches!( + Timeout::new(async { Err::(0) }, Never).await, + Ok(Err(0)) + )); } #[tokio::test] async fn timeout() { - assert_eq!(Err(TimedOutError), Timeout::new(Never, async {}).await); + assert!(matches!( + Timeout::new(Never, async {}).await, + Err(TimedOutError) + )); } // If the value is available at the same time as the timeout, then return the value #[tokio::test] async fn prefer_value_to_timeout() { - assert_eq!(Ok(5), Timeout::new(async { 5 }, async {}).await); + assert!(matches!(Timeout::new(async { 5 }, async {}).await, Ok(5))); } } diff --git a/rust-runtime/aws-smithy-json/src/deserialize/error.rs b/rust-runtime/aws-smithy-json/src/deserialize/error.rs index 9ae8e5b546..8ecacae82b 100644 --- a/rust-runtime/aws-smithy-json/src/deserialize/error.rs +++ b/rust-runtime/aws-smithy-json/src/deserialize/error.rs @@ -129,8 +129,8 @@ impl From for DeserializeError { } } -impl From for DeserializeError { - fn from(_: aws_smithy_types::TryFromNumberError) -> Self { +impl From for DeserializeError { + fn from(_: aws_smithy_types::error::TryFromNumberError) -> Self { Self { kind: DeserializeErrorKind::InvalidNumber, offset: None, diff --git a/rust-runtime/aws-smithy-types/src/base64.rs b/rust-runtime/aws-smithy-types/src/base64.rs index b869be9409..460a07e33d 100644 --- a/rust-runtime/aws-smithy-types/src/base64.rs +++ b/rust-runtime/aws-smithy-types/src/base64.rs @@ -5,19 +5,22 @@ //! A thin wrapper over `base64-simd` -use std::error::Error; - use base64_simd::Base64; +use std::error::Error; /// Failure to decode a base64 value. #[derive(Debug)] pub struct DecodeError(base64_simd::Error); -impl Error for DecodeError {} +impl Error for DecodeError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.0) + } +} impl std::fmt::Display for DecodeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) + write!(f, "failed to decode base64") } } @@ -38,9 +41,7 @@ pub fn encode(input: impl AsRef<[u8]>) -> String { .into_string() } -/// Given the length of some data in bytes, return how many bytes it would take to base64 encode -/// -/// that data. +/// Returns the base64 representation's length for the given `length` of data pub fn encoded_length(length: usize) -> usize { Base64::STANDARD.encoded_length(length) } diff --git a/rust-runtime/aws-smithy-types/src/date_time/format.rs b/rust-runtime/aws-smithy-types/src/date_time/format.rs index 560a16409e..6108c7e28d 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/format.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/format.rs @@ -9,45 +9,56 @@ use std::fmt; const NANOS_PER_SECOND: u32 = 1_000_000_000; -/// Error returned when date-time parsing fails. -#[non_exhaustive] #[derive(Debug)] -pub enum DateTimeParseError { +pub(super) enum DateTimeParseErrorKind { /// The given date-time string was invalid. - #[non_exhaustive] Invalid(Cow<'static, str>), /// Failed to parse an integer inside the given date-time string. - #[non_exhaustive] IntParseError, } +/// Error returned when date-time parsing fails. +#[derive(Debug)] +pub struct DateTimeParseError { + kind: DateTimeParseErrorKind, +} + impl Error for DateTimeParseError {} impl fmt::Display for DateTimeParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use DateTimeParseError::*; - match self { + use DateTimeParseErrorKind::*; + match &self.kind { Invalid(msg) => write!(f, "invalid date-time: {}", msg), IntParseError => write!(f, "failed to parse int"), } } } -/// Error returned when date-time formatting fails. -#[non_exhaustive] +impl From for DateTimeParseError { + fn from(kind: DateTimeParseErrorKind) -> Self { + Self { kind } + } +} + #[derive(Debug)] -pub enum DateTimeFormatError { +enum DateTimeFormatErrorKind { /// The given date-time cannot be represented in the requested date format. - #[non_exhaustive] OutOfRange(Cow<'static, str>), } +/// Error returned when date-time formatting fails. +#[derive(Debug)] +pub struct DateTimeFormatError { + kind: DateTimeFormatErrorKind, +} + impl Error for DateTimeFormatError {} impl fmt::Display for DateTimeFormatError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::OutOfRange(msg) => write!( + match &self.kind { + DateTimeFormatErrorKind::OutOfRange(msg) => write!( f, "date-time cannot be formatted since it is out of range: {}", msg @@ -56,6 +67,12 @@ impl fmt::Display for DateTimeFormatError { } } +impl From for DateTimeFormatError { + fn from(kind: DateTimeFormatErrorKind) -> Self { + DateTimeFormatError { kind } + } +} + fn remove_trailing_zeros(string: &mut String) { while let Some(b'0') = string.as_bytes().last() { string.pop(); @@ -64,7 +81,7 @@ fn remove_trailing_zeros(string: &mut String) { pub(crate) mod epoch_seconds { use super::remove_trailing_zeros; - use super::DateTimeParseError; + use super::{DateTimeParseError, DateTimeParseErrorKind}; use crate::DateTime; use std::str::FromStr; @@ -84,22 +101,25 @@ pub(crate) mod epoch_seconds { let mut parts = value.splitn(2, '.'); let (mut whole, mut decimal) = (0i64, 0u32); if let Some(whole_str) = parts.next() { - whole = ::from_str(whole_str).map_err(|_| DateTimeParseError::IntParseError)?; + whole = + ::from_str(whole_str).map_err(|_| DateTimeParseErrorKind::IntParseError)?; } if let Some(decimal_str) = parts.next() { if decimal_str.starts_with('+') || decimal_str.starts_with('-') { - return Err(DateTimeParseError::Invalid( + return Err(DateTimeParseErrorKind::Invalid( "invalid epoch-seconds timestamp".into(), - )); + ) + .into()); } if decimal_str.len() > 9 { - return Err(DateTimeParseError::Invalid( + return Err(DateTimeParseErrorKind::Invalid( "decimal is longer than 9 digits".into(), - )); + ) + .into()); } let missing_places = 9 - decimal_str.len() as isize; decimal = - ::from_str(decimal_str).map_err(|_| DateTimeParseError::IntParseError)?; + ::from_str(decimal_str).map_err(|_| DateTimeParseErrorKind::IntParseError)?; for _ in 0..missing_places { decimal *= 10; } @@ -110,7 +130,10 @@ pub(crate) mod epoch_seconds { pub(crate) mod http_date { use super::remove_trailing_zeros; - use crate::date_time::format::{DateTimeFormatError, DateTimeParseError, NANOS_PER_SECOND}; + use crate::date_time::format::{ + DateTimeFormatError, DateTimeFormatErrorKind, DateTimeParseError, DateTimeParseErrorKind, + NANOS_PER_SECOND, + }; use crate::DateTime; use std::str::FromStr; use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday}; @@ -132,7 +155,7 @@ pub(crate) mod http_date { /// - If subsecond nanos are nonzero, 3 digits of fractional seconds are added pub(crate) fn format(date_time: &DateTime) -> Result { fn out_of_range(cause: E) -> DateTimeFormatError { - DateTimeFormatError::OutOfRange( + DateTimeFormatErrorKind::OutOfRange( format!( "HTTP dates support dates between Mon, 01 Jan 0001 00:00:00 GMT \ and Fri, 31 Dec 9999 23:59:59.999 GMT. {}", @@ -140,6 +163,7 @@ pub(crate) mod http_date { ) .into(), ) + .into() } let structured = OffsetDateTime::from_unix_timestamp_nanos(date_time.as_nanos()) .map_err(out_of_range)?; @@ -244,9 +268,7 @@ pub(crate) mod http_date { /// Not Ok: "Mon, 16 Dec 2019 23:48:18.1234 GMT" pub(crate) fn parse(s: &str) -> Result { if !s.is_ascii() { - return Err(DateTimeParseError::Invalid( - "date-time must be ASCII".into(), - )); + return Err(DateTimeParseErrorKind::Invalid("date-time must be ASCII".into()).into()); } let x = s.trim().as_bytes(); parse_imf_fixdate(x) @@ -254,15 +276,15 @@ pub(crate) mod http_date { pub(crate) fn read(s: &str) -> Result<(DateTime, &str), DateTimeParseError> { if !s.is_ascii() { - return Err(DateTimeParseError::Invalid( - "date-time must be ASCII".into(), - )); + return Err(DateTimeParseErrorKind::Invalid("date-time must be ASCII".into()).into()); } let (first_date, rest) = match find_subsequence(s.as_bytes(), b" GMT") { // split_at is correct because we asserted that this date is only valid ASCII so the byte index is // the same as the char index Some(idx) => s.split_at(idx), - None => return Err(DateTimeParseError::Invalid("date-time is not GMT".into())), + None => { + return Err(DateTimeParseErrorKind::Invalid("date-time is not GMT".into()).into()) + } }; Ok((parse(first_date)?, rest)) } @@ -283,9 +305,7 @@ pub(crate) mod http_date { || s[19] != b':' || s[22] != b':' { - return Err(DateTimeParseError::Invalid( - "incorrectly shaped string".into(), - )); + return Err(DateTimeParseErrorKind::Invalid("incorrectly shaped string".into()).into()); } let nanos: u32 = match &s[25] { b'.' => { @@ -294,9 +314,10 @@ pub(crate) mod http_date { let fraction_slice = &s[26..s.len() - 4]; if fraction_slice.len() > 3 { // Only thousandths are supported - return Err(DateTimeParseError::Invalid( + return Err(DateTimeParseErrorKind::Invalid( "Smithy http-date only supports millisecond precision".into(), - )); + ) + .into()); } let fraction: u32 = parse_slice(fraction_slice)?; // We need to convert the fractional second to nanoseconds, so we need to scale @@ -306,9 +327,9 @@ pub(crate) mod http_date { } b' ' => 0, _ => { - return Err(DateTimeParseError::Invalid( - "incorrectly shaped string".into(), - )) + return Err( + DateTimeParseErrorKind::Invalid("incorrectly shaped string".into()).into(), + ) } }; @@ -316,7 +337,9 @@ pub(crate) mod http_date { let minutes = parse_slice(&s[20..22])?; let seconds = parse_slice(&s[23..25])?; let time = Time::from_hms_nano(hours, minutes, seconds, nanos).map_err(|err| { - DateTimeParseError::Invalid(format!("time components are out of range: {}", err).into()) + DateTimeParseErrorKind::Invalid( + format!("time components are out of range: {}", err).into(), + ) })?; let month = match &s[7..12] { @@ -333,19 +356,22 @@ pub(crate) mod http_date { b" Nov " => Month::November, b" Dec " => Month::December, month => { - return Err(DateTimeParseError::Invalid( + return Err(DateTimeParseErrorKind::Invalid( format!( "invalid month: {}", std::str::from_utf8(month).unwrap_or_default() ) .into(), - )) + ) + .into()) } }; let year = parse_slice(&s[12..16])?; let day = parse_slice(&s[5..7])?; let date = Date::from_calendar_date(year, month, day).map_err(|err| { - DateTimeParseError::Invalid(format!("date components are out of range: {}", err).into()) + DateTimeParseErrorKind::Invalid( + format!("date components are out of range: {}", err).into(), + ) })?; let date_time = PrimitiveDateTime::new(date, time).assume_offset(UtcOffset::UTC); @@ -359,14 +385,16 @@ pub(crate) mod http_date { { let as_str = std::str::from_utf8(ascii_slice).expect("should only be called on ascii strings"); - as_str + Ok(as_str .parse::() - .map_err(|_| DateTimeParseError::IntParseError) + .map_err(|_| DateTimeParseErrorKind::IntParseError)?) } } pub(crate) mod rfc3339 { - use crate::date_time::format::{DateTimeFormatError, DateTimeParseError}; + use crate::date_time::format::{ + DateTimeFormatError, DateTimeFormatErrorKind, DateTimeParseError, DateTimeParseErrorKind, + }; use crate::DateTime; use time::format_description::well_known::Rfc3339; use time::OffsetDateTime; @@ -378,12 +406,13 @@ pub(crate) mod rfc3339 { // Not OK: 1985-04-12T23:20:50-02:00 pub(crate) fn parse(s: &str) -> Result { if !matches!(s.chars().last(), Some('Z')) { - return Err(DateTimeParseError::Invalid( + return Err(DateTimeParseErrorKind::Invalid( "Smithy does not support timezone offsets in RFC-3339 date times".into(), - )); + ) + .into()); } let date_time = OffsetDateTime::parse(s, &Rfc3339).map_err(|err| { - DateTimeParseError::Invalid(format!("invalid RFC-3339 date-time: {}", err).into()) + DateTimeParseErrorKind::Invalid(format!("invalid RFC-3339 date-time: {}", err).into()) })?; Ok(DateTime::from_nanos(date_time.unix_timestamp_nanos()) .expect("this date format cannot produce out of range date-times")) @@ -400,7 +429,7 @@ pub(crate) mod rfc3339 { pub(crate) fn format(date_time: &DateTime) -> Result { use std::fmt::Write; fn out_of_range(cause: E) -> DateTimeFormatError { - DateTimeFormatError::OutOfRange( + DateTimeFormatErrorKind::OutOfRange( format!( "RFC-3339 timestamps support dates between 0001-01-01T00:00:00.000Z \ and 9999-12-31T23:59:59.999Z. {}", @@ -408,6 +437,7 @@ pub(crate) mod rfc3339 { ) .into(), ) + .into() } let (year, month, day, hour, minute, second, micros) = { let s = OffsetDateTime::from_unix_timestamp_nanos(date_time.as_nanos()) @@ -585,11 +615,15 @@ mod tests { assert!(matches!( rfc3339::format(&DateTime::from_secs(-62_135_596_800 - 1)), - Err(DateTimeFormatError::OutOfRange(_)) + Err(DateTimeFormatError { + kind: DateTimeFormatErrorKind::OutOfRange(_) + }) )); assert!(matches!( rfc3339::format(&DateTime::from_secs(253402300799 + 1)), - Err(DateTimeFormatError::OutOfRange(_)) + Err(DateTimeFormatError { + kind: DateTimeFormatErrorKind::OutOfRange(_) + }) )); } @@ -634,7 +668,12 @@ mod tests { #[test] fn parse_rfc3339_timezone_forbidden() { let dt = rfc3339::parse("1985-04-12T23:20:50-02:00"); - assert!(matches!(dt.unwrap_err(), DateTimeParseError::Invalid(_))); + assert!(matches!( + dt.unwrap_err(), + DateTimeParseError { + kind: DateTimeParseErrorKind::Invalid(_) + } + )); } #[test] @@ -650,11 +689,15 @@ mod tests { assert!(matches!( http_date::format(&DateTime::from_secs(-62_135_596_800 - 1)), - Err(DateTimeFormatError::OutOfRange(_)) + Err(DateTimeFormatError { + kind: DateTimeFormatErrorKind::OutOfRange(_) + }) )); assert!(matches!( http_date::format(&DateTime::from_secs(253402300799 + 1)), - Err(DateTimeFormatError::OutOfRange(_)) + Err(DateTimeFormatError { + kind: DateTimeFormatErrorKind::OutOfRange(_) + }) )); } @@ -663,7 +706,9 @@ mod tests { let fractional = "Mon, 16 Dec 2019 23:48:18.1212 GMT"; assert!(matches!( http_date::parse(fractional), - Err(DateTimeParseError::Invalid(_)) + Err(DateTimeParseError { + kind: DateTimeParseErrorKind::Invalid(_) + }) )); } @@ -672,7 +717,9 @@ mod tests { let fractional = "Mon, 16 Dec 2019 23:48:18. GMT"; assert!(matches!( http_date::parse(fractional), - Err(DateTimeParseError::IntParseError) + Err(DateTimeParseError { + kind: DateTimeParseErrorKind::IntParseError + }) )); } diff --git a/rust-runtime/aws-smithy-types/src/date_time/mod.rs b/rust-runtime/aws-smithy-types/src/date_time/mod.rs index bf8b27b119..6c2600dc9f 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/mod.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs @@ -5,6 +5,7 @@ //! DateTime type for representing Smithy timestamps. +use crate::date_time::format::DateTimeParseErrorKind; use num_integer::div_mod_floor; use num_integer::Integer; use std::convert::TryFrom; @@ -219,9 +220,7 @@ impl DateTime { } else if next.starts_with(delim) { Ok((inst, &next[1..])) } else { - Err(DateTimeParseError::Invalid( - "didn't find expected delimiter".into(), - )) + Err(DateTimeParseErrorKind::Invalid("didn't find expected delimiter".into()).into()) } } diff --git a/rust-runtime/aws-smithy-types/src/error.rs b/rust-runtime/aws-smithy-types/src/error.rs index 0d35db10c7..dc41a67d83 100644 --- a/rust-runtime/aws-smithy-types/src/error.rs +++ b/rust-runtime/aws-smithy-types/src/error.rs @@ -8,7 +8,6 @@ use crate::retry::{ErrorKind, ProvideErrorKind}; use std::collections::HashMap; use std::fmt; -use std::fmt::{Display, Formatter}; pub mod display; @@ -125,8 +124,8 @@ impl ProvideErrorKind for Error { } } -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut fmt = f.debug_struct("Error"); if let Some(code) = &self.code { fmt.field("code", code); @@ -145,3 +144,86 @@ impl Display for Error { } impl std::error::Error for Error {} + +#[derive(Debug)] +pub(super) enum TryFromNumberErrorKind { + /// Used when the conversion from an integer type into a smaller integer type would be lossy. + OutsideIntegerRange(std::num::TryFromIntError), + /// Used when the conversion from an `u64` into a floating point type would be lossy. + U64ToFloatLossyConversion(u64), + /// Used when the conversion from an `i64` into a floating point type would be lossy. + I64ToFloatLossyConversion(i64), + /// Used when attempting to convert an `f64` into an `f32`. + F64ToF32LossyConversion(f64), + /// Used when attempting to convert a decimal, infinite, or `NaN` floating point type into an + /// integer type. + FloatToIntegerLossyConversion(f64), + /// Used when attempting to convert a negative [`Number`](crate::Number) into an unsigned integer type. + NegativeToUnsignedLossyConversion(i64), +} + +/// The error type returned when conversion into an integer type or floating point type is lossy. +#[derive(Debug)] +pub struct TryFromNumberError { + pub(super) kind: TryFromNumberErrorKind, +} + +impl fmt::Display for TryFromNumberError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use TryFromNumberErrorKind::*; + match self.kind { + OutsideIntegerRange(_) => write!(f, "integer too large"), + FloatToIntegerLossyConversion(v) => write!( + f, + "cannot convert floating point number {v} into an integer" + ), + NegativeToUnsignedLossyConversion(v) => write!( + f, + "cannot convert negative integer {v} into an unsigned integer type" + ), + U64ToFloatLossyConversion(v) => { + write!( + f, + "cannot convert {v}u64 into a floating point type without precision loss" + ) + } + I64ToFloatLossyConversion(v) => { + write!( + f, + "cannot convert {v}i64 into a floating point type without precision loss" + ) + } + F64ToF32LossyConversion(v) => { + write!(f, "will not attempt to convert {v}f64 into a f32") + } + } + } +} + +impl std::error::Error for TryFromNumberError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + use TryFromNumberErrorKind::*; + match &self.kind { + OutsideIntegerRange(err) => Some(err as _), + FloatToIntegerLossyConversion(_) + | NegativeToUnsignedLossyConversion(_) + | U64ToFloatLossyConversion(_) + | I64ToFloatLossyConversion(_) + | F64ToF32LossyConversion(_) => None, + } + } +} + +impl From for TryFromNumberError { + fn from(value: std::num::TryFromIntError) -> Self { + Self { + kind: TryFromNumberErrorKind::OutsideIntegerRange(value), + } + } +} + +impl From for TryFromNumberError { + fn from(kind: TryFromNumberErrorKind) -> Self { + Self { kind } + } +} diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index b02f07f515..06b4908640 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -13,6 +13,7 @@ unreachable_pub )] +use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; use std::collections::HashMap; pub mod base64; @@ -138,67 +139,6 @@ impl Number { } } -/// The error type returned when conversion into an integer type or floating point type is lossy. -#[derive(Debug)] -pub enum TryFromNumberError { - /// Used when the conversion from an integer type into a smaller integer type would be lossy. - OutsideIntegerRange(std::num::TryFromIntError), - /// Used when the conversion from an `u64` into a floating point type would be lossy. - U64ToFloatLossyConversion(u64), - /// Used when the conversion from an `i64` into a floating point type would be lossy. - I64ToFloatLossyConversion(i64), - /// Used when attempting to convert an `f64` into an `f32`. - F64ToF32LossyConversion(f64), - /// Used when attempting to convert a decimal, infinite, or `NaN` floating point type into an - /// integer type. - FloatToIntegerLossyConversion(f64), - /// Used when attempting to convert a negative [`Number`] into an unsigned integer type. - NegativeToUnsignedLossyConversion(i64), -} - -impl std::fmt::Display for TryFromNumberError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - TryFromNumberError::OutsideIntegerRange(err) => write!(f, "integer too large: {}", err), - TryFromNumberError::FloatToIntegerLossyConversion(v) => write!( - f, - "cannot convert floating point number {} into an integer", - v - ), - TryFromNumberError::NegativeToUnsignedLossyConversion(v) => write!( - f, - "cannot convert negative integer {} into an unsigned integer type", - v - ), - TryFromNumberError::U64ToFloatLossyConversion(v) => { - write!( - f, - "cannot convert {}u64 into a floating point type without precision loss", - v - ) - } - TryFromNumberError::I64ToFloatLossyConversion(v) => { - write!( - f, - "cannot convert {}i64 into a floating point type without precision loss", - v - ) - } - TryFromNumberError::F64ToF32LossyConversion(v) => { - write!(f, "will not attempt to convert {}f64 into a f32", v) - } - } - } -} - -impl std::error::Error for TryFromNumberError {} - -impl From for TryFromNumberError { - fn from(value: std::num::TryFromIntError) -> Self { - Self::OutsideIntegerRange(value) - } -} - macro_rules! to_unsigned_integer_converter { ($typ:ident, $styp:expr) => { #[doc = "Converts to a `"] @@ -210,8 +150,12 @@ macro_rules! to_unsigned_integer_converter { fn try_from(value: Number) -> Result { match value { Number::PosInt(v) => Ok(Self::try_from(v)?), - Number::NegInt(v) => Err(Self::Error::NegativeToUnsignedLossyConversion(v)), - Number::Float(v) => Err(Self::Error::FloatToIntegerLossyConversion(v)), + Number::NegInt(v) => { + Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) + } + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } } } } @@ -234,7 +178,9 @@ 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(Self::Error::FloatToIntegerLossyConversion(v)), + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } } } } @@ -252,8 +198,12 @@ impl TryFrom for u64 { fn try_from(value: Number) -> Result { match value { Number::PosInt(v) => Ok(v), - Number::NegInt(v) => Err(Self::Error::NegativeToUnsignedLossyConversion(v)), - Number::Float(v) => Err(Self::Error::FloatToIntegerLossyConversion(v)), + Number::NegInt(v) => { + Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) + } + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } } } } @@ -268,7 +218,9 @@ impl TryFrom for i64 { match value { Number::PosInt(v) => Ok(Self::try_from(v)?), Number::NegInt(v) => Ok(v), - Number::Float(v) => Err(Self::Error::FloatToIntegerLossyConversion(v)), + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } } } } @@ -289,14 +241,14 @@ impl TryFrom for f64 { if v <= (1 << 53) { Ok(v as Self) } else { - Err(Self::Error::U64ToFloatLossyConversion(v)) + Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) } } Number::NegInt(v) => { if (-(1 << 53)..=(1 << 53)).contains(&v) { Ok(v as Self) } else { - Err(Self::Error::I64ToFloatLossyConversion(v)) + Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) } } Number::Float(v) => Ok(v), @@ -314,17 +266,17 @@ impl TryFrom for f32 { if v <= (1 << 24) { Ok(v as Self) } else { - Err(Self::Error::U64ToFloatLossyConversion(v)) + Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) } } Number::NegInt(v) => { if (-(1 << 24)..=(1 << 24)).contains(&v) { Ok(v as Self) } else { - Err(Self::Error::I64ToFloatLossyConversion(v)) + Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) } } - Number::Float(v) => Err(Self::Error::F64ToF32LossyConversion(v)), + Number::Float(v) => Err(TryFromNumberErrorKind::F64ToF32LossyConversion(v).into()), } } } @@ -332,6 +284,7 @@ impl TryFrom for f32 { #[cfg(test)] mod number { use super::*; + use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; macro_rules! to_unsigned_converter_tests { ($typ:ident) => { @@ -339,18 +292,24 @@ mod number { assert!(matches!( $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), - TryFromNumberError::OutsideIntegerRange(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::OutsideIntegerRange(..) + } )); assert!(matches!( $typ::try_from(Number::NegInt(-1i64)).unwrap_err(), - TryFromNumberError::NegativeToUnsignedLossyConversion(..) + 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::FloatToIntegerLossyConversion(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } )); } }; @@ -362,13 +321,17 @@ mod number { assert!(matches!( u64::try_from(Number::NegInt(-1i64)).unwrap_err(), - TryFromNumberError::NegativeToUnsignedLossyConversion(..) + 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::FloatToIntegerLossyConversion(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } )); } } @@ -395,18 +358,24 @@ mod number { assert!(matches!( $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), - TryFromNumberError::OutsideIntegerRange(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::OutsideIntegerRange(..) + } )); assert!(matches!( $typ::try_from(Number::NegInt(($typ::MIN as i64) - 1i64)).unwrap_err(), - TryFromNumberError::OutsideIntegerRange(..) + 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::FloatToIntegerLossyConversion(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } )); } }; @@ -420,7 +389,9 @@ mod number { for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { assert!(matches!( u64::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError::FloatToIntegerLossyConversion(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } )); } } @@ -474,16 +445,22 @@ mod number { assert!(matches!( f64::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), - TryFromNumberError::U64ToFloatLossyConversion(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) + } )); assert!(matches!( f64::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), - TryFromNumberError::I64ToFloatLossyConversion(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } )); assert!(matches!( f64::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), - TryFromNumberError::I64ToFloatLossyConversion(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } )); } @@ -511,22 +488,30 @@ mod number { assert!(matches!( f32::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), - TryFromNumberError::U64ToFloatLossyConversion(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) + } )); assert!(matches!( f32::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), - TryFromNumberError::I64ToFloatLossyConversion(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } )); assert!(matches!( f32::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), - TryFromNumberError::I64ToFloatLossyConversion(..) + 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::F64ToF32LossyConversion(..) + TryFromNumberError { + kind: TryFromNumberErrorKind::F64ToF32LossyConversion(..) + } )); } } diff --git a/rust-runtime/aws-smithy-types/src/primitive.rs b/rust-runtime/aws-smithy-types/src/primitive.rs index ebc6b8d899..79294cd066 100644 --- a/rust-runtime/aws-smithy-types/src/primitive.rs +++ b/rust-runtime/aws-smithy-types/src/primitive.rs @@ -32,15 +32,16 @@ //! ``` use crate::primitive::private::Sealed; use std::error::Error; -use std::fmt::{self, Debug, Display, Formatter}; +use std::fmt; use std::str::FromStr; /// An error during primitive parsing #[non_exhaustive] -#[derive(Debug, Eq, PartialEq, Clone)] +#[derive(Debug)] pub struct PrimitiveParseError(&'static str); -impl Display for PrimitiveParseError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + +impl fmt::Display for PrimitiveParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "failed to parse input as {}", self.0) } } @@ -113,8 +114,8 @@ enum Inner { F64(f64, ryu::Buffer), } -impl Debug for Inner { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { +impl fmt::Debug for Inner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Bool(v) => write!(f, "Bool({})", v), Self::I8(v, _) => write!(f, "I8({})", v), @@ -266,8 +267,8 @@ mod test { assert_eq!(Encoder::from(false).encode(), "false"); let err = bool::parse_smithy_primitive("not a boolean").expect_err("should fail"); assert_eq!(err.0, "bool"); - assert_eq!(bool::parse_smithy_primitive("true"), Ok(true)); - assert_eq!(bool::parse_smithy_primitive("false"), Ok(false)); + assert!(bool::parse_smithy_primitive("true").unwrap()); + assert!(!bool::parse_smithy_primitive("false").unwrap()); } #[test] @@ -283,7 +284,7 @@ mod test { #[test] fn float_parse() { - assert_eq!(f64::parse_smithy_primitive("1234.5"), Ok(1234.5)); + assert_eq!(f64::parse_smithy_primitive("1234.5").unwrap(), 1234.5); assert!(f64::parse_smithy_primitive("NaN").unwrap().is_nan()); assert_eq!( f64::parse_smithy_primitive("Infinity").unwrap(), @@ -293,7 +294,7 @@ mod test { f64::parse_smithy_primitive("-Infinity").unwrap(), f64::NEG_INFINITY ); - assert_eq!(f32::parse_smithy_primitive("1234.5"), Ok(1234.5)); + assert_eq!(f32::parse_smithy_primitive("1234.5").unwrap(), 1234.5); assert!(f32::parse_smithy_primitive("NaN").unwrap().is_nan()); assert_eq!( f32::parse_smithy_primitive("Infinity").unwrap(), diff --git a/rust-runtime/aws-smithy-types/src/retry.rs b/rust-runtime/aws-smithy-types/src/retry.rs index 56556154bb..43be79cae4 100644 --- a/rust-runtime/aws-smithy-types/src/retry.rs +++ b/rust-runtime/aws-smithy-types/src/retry.rs @@ -5,12 +5,12 @@ //! This module defines types that describe when to retry given a response. -use std::borrow::Cow; -use std::fmt::{Display, Formatter}; -use std::num::ParseIntError; +use std::fmt; use std::str::FromStr; use std::time::Duration; +const VALID_RETRY_MODES: &[RetryMode] = &[RetryMode::Standard]; + /// Type of error that occurred when making a request. #[derive(Clone, Copy, Eq, PartialEq, Debug)] #[non_exhaustive] @@ -93,26 +93,8 @@ pub enum RetryMode { Adaptive, } -const VALID_RETRY_MODES: &[RetryMode] = &[RetryMode::Standard]; - -/// Failure to parse a `RetryMode` from string. -#[derive(Debug)] -pub struct RetryModeParseErr(String); - -impl Display for RetryModeParseErr { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!( - f, - "error parsing string '{}' as RetryMode, valid options are: {:#?}", - self.0, VALID_RETRY_MODES - ) - } -} - -impl std::error::Error for RetryModeParseErr {} - impl FromStr for RetryMode { - type Err = RetryModeParseErr; + type Err = RetryModeParseError; fn from_str(string: &str) -> Result { let string = string.trim(); @@ -123,11 +105,37 @@ impl FromStr for RetryMode { // } else if string.eq_ignore_ascii_case("adaptive") { // Ok(RetryMode::Adaptive) } else { - Err(RetryModeParseErr(string.to_owned())) + Err(RetryModeParseError::new(string)) + } + } +} + +/// Failure to parse a `RetryMode` from string. +#[derive(Debug)] +pub struct RetryModeParseError { + message: String, +} + +impl RetryModeParseError { + pub(super) fn new(message: impl Into) -> Self { + Self { + message: message.into(), } } } +impl fmt::Display for RetryModeParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "error parsing string '{}' as RetryMode, valid options are: {:#?}", + self.message, VALID_RETRY_MODES + ) + } +} + +impl std::error::Error for RetryModeParseError {} + /// Builder for [`RetryConfig`]. #[non_exhaustive] #[derive(Debug, Default, Clone, PartialEq)] @@ -295,71 +303,6 @@ impl RetryConfig { } } -/// Failure to parse retry config from profile file or environment variable. -#[non_exhaustive] -#[derive(Debug)] -pub enum RetryConfigErr { - /// The configured retry mode wasn't recognized. - InvalidRetryMode { - /// Cause of the error. - source: RetryModeParseErr, - /// Where the invalid retry mode value originated from. - set_by: Cow<'static, str>, - }, - /// Max attempts must be greater than zero. - MaxAttemptsMustNotBeZero { - /// Where the invalid max attempts value originated from. - set_by: Cow<'static, str>, - }, - /// The max attempts value couldn't be parsed to an integer. - FailedToParseMaxAttempts { - /// Cause of the error. - source: ParseIntError, - /// Where the invalid max attempts value originated from. - set_by: Cow<'static, str>, - }, - /// The adaptive retry mode hasn't been implemented yet. - AdaptiveModeIsNotSupported { - /// Where the invalid retry mode value originated from. - set_by: Cow<'static, str>, - }, -} - -impl Display for RetryConfigErr { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - use RetryConfigErr::*; - match self { - InvalidRetryMode { set_by, source } => { - write!(f, "invalid configuration set by {}: {}", set_by, source) - } - MaxAttemptsMustNotBeZero { set_by } => { - write!(f, "invalid configuration set by {}: It is invalid to set max attempts to 0. Unset it or set it to an integer greater than or equal to one.", set_by) - } - FailedToParseMaxAttempts { set_by, source } => { - write!( - f, - "failed to parse max attempts set by {}: {}", - set_by, source - ) - } - AdaptiveModeIsNotSupported { set_by } => { - write!(f, "invalid configuration set by {}: Setting retry mode to 'adaptive' is not yet supported. Unset it or set it to 'standard' mode.", set_by) - } - } - } -} - -impl std::error::Error for RetryConfigErr { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - use RetryConfigErr::*; - match self { - InvalidRetryMode { source, .. } => Some(source), - FailedToParseMaxAttempts { source, .. } => Some(source), - _ => None, - } - } -} - #[cfg(test)] mod tests { use crate::retry::{RetryConfigBuilder, RetryMode}; diff --git a/rust-runtime/aws-smithy-types/src/timeout/config.rs b/rust-runtime/aws-smithy-types/src/timeout.rs similarity index 98% rename from rust-runtime/aws-smithy-types/src/timeout/config.rs rename to rust-runtime/aws-smithy-types/src/timeout.rs index 68d1296695..04e92650a9 100644 --- a/rust-runtime/aws-smithy-types/src/timeout/config.rs +++ b/rust-runtime/aws-smithy-types/src/timeout.rs @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! This module defines types that describe timeouts that can be applied to various stages of the +//! Smithy networking stack. + use std::time::Duration; /// Builder for [`TimeoutConfig`]. diff --git a/rust-runtime/aws-smithy-types/src/timeout/error.rs b/rust-runtime/aws-smithy-types/src/timeout/error.rs deleted file mode 100644 index 204b780d4b..0000000000 --- a/rust-runtime/aws-smithy-types/src/timeout/error.rs +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use std::borrow::Cow; -use std::fmt::{Debug, Display, Formatter}; - -#[non_exhaustive] -#[derive(Debug)] -/// An error that occurs during construction of a `timeout::Config` -pub enum ConfigError { - /// A timeout value was set to an invalid value: - /// - Any number less than 0 - /// - Infinity or negative infinity - /// - `NaN` - InvalidTimeout { - /// The name of the invalid value - name: Cow<'static, str>, - /// The reason that why the timeout was considered invalid - reason: Cow<'static, str>, - /// Where the invalid value originated from - set_by: Cow<'static, str>, - }, - /// The timeout value couln't be parsed as an `f32` - ParseError { - /// The name of the invalid value - name: Cow<'static, str>, - /// Where the invalid value originated from - set_by: Cow<'static, str>, - /// The source of this error - source: Box, - }, -} - -impl Display for ConfigError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - use ConfigError::*; - match self { - InvalidTimeout { - name, - set_by, - reason, - } => { - write!( - f, - "invalid timeout '{}' set by {} is invalid: {}", - name, set_by, reason - ) - } - ParseError { - name, - set_by, - source, - } => { - write!( - f, - "timeout '{}' set by {} could not be parsed as an f32: {}", - name, set_by, source - ) - } - } - } -} diff --git a/rust-runtime/aws-smithy-types/src/timeout/mod.rs b/rust-runtime/aws-smithy-types/src/timeout/mod.rs deleted file mode 100644 index b32eaac637..0000000000 --- a/rust-runtime/aws-smithy-types/src/timeout/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! This module defines types that describe timeouts that can be applied to various stages of the -//! Smithy networking stack. - -mod config; -mod error; - -pub use config::{OperationTimeoutConfig, TimeoutConfig, TimeoutConfigBuilder}; -pub use error::ConfigError;