Unverified Commit 8439f2ae authored by ysaito1001's avatar ysaito1001 Committed by GitHub
Browse files

Move `aws_smithy_http::operation::error::BuildError` into `aws-smithy-types` (#3032)

## Motivation and Context
Takes care of the first part of
https://github.com/awslabs/smithy-rs/issues/3054

 (the remaining part is
denoted as `TODO(runtimeCratesVersioningCleanup)` and until that's done
the issue will not be closed).

## Description
This PR moves from `aws-smithy-http::operation::error::{BuildError,
SerializationError}` to
`aws-smithy-types::error::operation::{BuildError, SerializationError}`.

Within the origin crate, we leave "breadcrumbs" (i.e. reexports) for
existing customers of the items above so they are not broken by the
move. The breadcrumps will be removed by the part two of the issue.

As part of the move, `aws_smithy_http::endpoint::EndpointPrefix::new`
now returns its `crate::endpoint::error::InvalidEndpointError` instead
of `crate::operation::error::BuildError` for two reasons:
- `BuildError` is now in a stable crate and for an `InvalidUri` variant
to be created, it needed to take a `http::uri::InvalidUri` from a
unstable crate, which we cannot expose in public API.
- The new error is more closely related to the endpoint business.

## Testing
Relied on the tests in CI.

## Checklist
- [x] I have updated `CHANGELOG.next.toml` if I made changes to the
smithy-rs codegen or runtime crates

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._

---------

Co-authored-by: default avatarJohn DiSanti <jdisanti@amazon.com>
parent 98dadc01
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -335,3 +335,11 @@ message = "The future return types on traits `EndpointResolver` and `IdentityRes
references = ["smithy-rs#3055"]
references = ["smithy-rs#3055"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
author = "jdisanti"
author = "jdisanti"

[[smithy-rs]]
message = """
[`EndpointPrefix::new`](https://docs.rs/aws-smithy-http/latest/aws_smithy_http/endpoint/struct.EndpointPrefix.html#method.new) no longer returns `crate::operation::error::BuildError` for an Err variant, instead returns a more specific [`InvalidEndpointError`](https://docs.rs/aws-smithy-http/latest/aws_smithy_http/endpoint/error/struct.InvalidEndpointError.html).
"""
references = ["smithy-rs#3032"]
meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" }
author = "ysaito1001"
+4 −6
Original line number Original line Diff line number Diff line
@@ -16,7 +16,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider
import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider
import software.amazon.smithy.rust.codegen.core.smithy.generators.OperationBuildError
import software.amazon.smithy.rust.codegen.core.smithy.isOptional
import software.amazon.smithy.rust.codegen.core.smithy.isOptional
import software.amazon.smithy.rust.codegen.core.util.inputShape
import software.amazon.smithy.rust.codegen.core.util.inputShape


@@ -72,18 +71,17 @@ class EndpointTraitBindings(
                        rust("let $field = &$input.$field;")
                        rust("let $field = &$input.$field;")
                    }
                    }
                    if (generateValidation) {
                    if (generateValidation) {
                        val errorString = "$field was unset or empty but must be set as part of the endpoint prefix"
                        val contents =
                        val contents =
                            """
                            """
                            if $field.is_empty() {
                            if $field.is_empty() {
                                return Err(#{invalidFieldError:W}.into())
                                return Err(#{InvalidEndpointError}::failed_to_construct_uri("$errorString").into());
                            }
                            }
                            """
                            """
                        rustTemplate(
                        rustTemplate(
                            contents,
                            contents,
                            "invalidFieldError" to OperationBuildError(runtimeConfig).invalidField(
                            "InvalidEndpointError" to RuntimeType.smithyHttp(runtimeConfig)
                                field,
                                .resolve("endpoint::error::InvalidEndpointError"),
                                "$field was unset or empty but must be set as part of the endpoint prefix",
                            ),
                        )
                        )
                    }
                    }
                    "${label.content} = $field"
                    "${label.content} = $field"
+4 −6
Original line number Original line Diff line number Diff line
@@ -16,10 +16,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
import software.amazon.smithy.rust.codegen.core.rustlang.implBlock
import software.amazon.smithy.rust.codegen.core.rustlang.implBlock
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock
import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType
import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError
import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig
import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel
@@ -70,10 +69,9 @@ internal class EndpointTraitBindingsTest {
                """,
                """,
            )
            )
            implBlock(symbolProvider.toSymbol(model.lookup("test#GetStatusInput"))) {
            implBlock(symbolProvider.toSymbol(model.lookup("test#GetStatusInput"))) {
                rustBlock(
                rustBlockTemplate(
                    "fn endpoint_prefix(&self) -> std::result::Result<#T::endpoint::EndpointPrefix, #T>",
                    "fn endpoint_prefix(&self) -> std::result::Result<#{endpoint}::EndpointPrefix, #{endpoint}::error::InvalidEndpointError>",
                    RuntimeType.smithyHttp(TestRuntimeConfig),
                    "endpoint" to RuntimeType.smithyHttp(TestRuntimeConfig).resolve("endpoint"),
                    TestRuntimeConfig.operationBuildError(),
                ) {
                ) {
                    endpointBindingGenerator.render(this, "self")
                    endpointBindingGenerator.render(this, "self")
                }
                }
+10 −11
Original line number Original line Diff line number Diff line
@@ -6,7 +6,6 @@
//! Code for resolving an endpoint (URI) that a request should be sent to
//! Code for resolving an endpoint (URI) that a request should be sent to


use crate::endpoint::error::InvalidEndpointError;
use crate::endpoint::error::InvalidEndpointError;
use crate::operation::error::BuildError;
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use aws_smithy_types::config_bag::{Storable, StoreReplace};
use http::uri::{Authority, Uri};
use http::uri::{Authority, Uri};
use std::borrow::Cow;
use std::borrow::Cow;
@@ -104,15 +103,13 @@ impl<T> ResolveEndpoint<T> for Endpoint {
pub struct EndpointPrefix(String);
pub struct EndpointPrefix(String);
impl EndpointPrefix {
impl EndpointPrefix {
    /// Create a new endpoint prefix from an `impl Into<String>`. If the prefix argument is invalid,
    /// Create a new endpoint prefix from an `impl Into<String>`. If the prefix argument is invalid,
    /// a [`BuildError`] will be returned.
    /// a [`InvalidEndpointError`] will be returned.
    pub fn new(prefix: impl Into<String>) -> StdResult<Self, BuildError> {
    pub fn new(prefix: impl Into<String>) -> StdResult<Self, InvalidEndpointError> {
        let prefix = prefix.into();
        let prefix = prefix.into();
        match Authority::from_str(&prefix) {
        match Authority::from_str(&prefix) {
            Ok(_) => Ok(EndpointPrefix(prefix)),
            Ok(_) => Ok(EndpointPrefix(prefix)),
            Err(err) => Err(BuildError::invalid_uri(
            Err(err) => Err(InvalidEndpointError::failed_to_construct_authority(
                prefix,
                prefix, err,
                "invalid prefix".into(),
                err,
            )),
            )),
        }
        }
    }
    }
@@ -142,11 +139,13 @@ pub fn apply_endpoint(
        .map(|auth| auth.as_str())
        .map(|auth| auth.as_str())
        .unwrap_or("");
        .unwrap_or("");
    let authority = if !prefix.is_empty() {
    let authority = if !prefix.is_empty() {
        Authority::from_str(&format!("{}{}", prefix, authority))
        Cow::Owned(format!("{}{}", prefix, authority))
    } else {
    } else {
        Authority::from_str(authority)
        Cow::Borrowed(authority)
    }
    };
    .map_err(InvalidEndpointError::failed_to_construct_authority)?;
    let authority = Authority::from_str(&authority).map_err(|err| {
        InvalidEndpointError::failed_to_construct_authority(authority.into_owned(), err)
    })?;
    let scheme = *endpoint
    let scheme = *endpoint
        .scheme()
        .scheme()
        .as_ref()
        .as_ref()
+17 −8
Original line number Original line Diff line number Diff line
@@ -54,6 +54,7 @@ impl Error for ResolveEndpointError {
pub(super) enum InvalidEndpointErrorKind {
pub(super) enum InvalidEndpointErrorKind {
    EndpointMustHaveScheme,
    EndpointMustHaveScheme,
    FailedToConstructAuthority {
    FailedToConstructAuthority {
        authority: String,
        source: Box<dyn Error + Send + Sync + 'static>,
        source: Box<dyn Error + Send + Sync + 'static>,
    },
    },
    FailedToConstructUri {
    FailedToConstructUri {
@@ -69,23 +70,28 @@ pub struct InvalidEndpointError {
}
}


impl InvalidEndpointError {
impl InvalidEndpointError {
    pub(super) fn endpoint_must_have_scheme() -> Self {
    /// Construct a build error for a missing scheme
    pub fn endpoint_must_have_scheme() -> Self {
        Self {
        Self {
            kind: InvalidEndpointErrorKind::EndpointMustHaveScheme,
            kind: InvalidEndpointErrorKind::EndpointMustHaveScheme,
        }
        }
    }
    }


    pub(super) fn failed_to_construct_authority(
    /// Construct a build error for an invalid authority
    pub fn failed_to_construct_authority(
        authority: impl Into<String>,
        source: impl Into<Box<dyn Error + Send + Sync + 'static>>,
        source: impl Into<Box<dyn Error + Send + Sync + 'static>>,
    ) -> Self {
    ) -> Self {
        Self {
        Self {
            kind: InvalidEndpointErrorKind::FailedToConstructAuthority {
            kind: InvalidEndpointErrorKind::FailedToConstructAuthority {
                authority: authority.into(),
                source: source.into(),
                source: source.into(),
            },
            },
        }
        }
    }
    }


    pub(super) fn failed_to_construct_uri(
    /// Construct a build error for an invalid URI
    pub fn failed_to_construct_uri(
        source: impl Into<Box<dyn Error + Send + Sync + 'static>>,
        source: impl Into<Box<dyn Error + Send + Sync + 'static>>,
    ) -> Self {
    ) -> Self {
        Self {
        Self {
@@ -105,11 +111,11 @@ impl From<InvalidEndpointErrorKind> for InvalidEndpointError {
impl fmt::Display for InvalidEndpointError {
impl fmt::Display for InvalidEndpointError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use InvalidEndpointErrorKind as ErrorKind;
        use InvalidEndpointErrorKind as ErrorKind;
        match self.kind {
        match &self.kind {
            ErrorKind::EndpointMustHaveScheme => write!(f, "endpoint must contain a valid scheme"),
            ErrorKind::EndpointMustHaveScheme => write!(f, "endpoint must contain a valid scheme"),
            ErrorKind::FailedToConstructAuthority { .. } => write!(
            ErrorKind::FailedToConstructAuthority { authority, source: _ } => write!(
                f,
                f,
                "endpoint must contain a valid authority when combined with endpoint prefix"
                "endpoint must contain a valid authority when combined with endpoint prefix: {authority}"
            ),
            ),
            ErrorKind::FailedToConstructUri { .. } => write!(f, "failed to construct URI"),
            ErrorKind::FailedToConstructUri { .. } => write!(f, "failed to construct URI"),
        }
        }
@@ -120,8 +126,11 @@ impl Error for InvalidEndpointError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        use InvalidEndpointErrorKind as ErrorKind;
        use InvalidEndpointErrorKind as ErrorKind;
        match &self.kind {
        match &self.kind {
            ErrorKind::FailedToConstructUri { source }
            ErrorKind::FailedToConstructUri { source } => Some(source.as_ref()),
            | ErrorKind::FailedToConstructAuthority { source } => Some(source.as_ref()),
            ErrorKind::FailedToConstructAuthority {
                authority: _,
                source,
            } => Some(source.as_ref()),
            ErrorKind::EndpointMustHaveScheme => None,
            ErrorKind::EndpointMustHaveScheme => None,
        }
        }
    }
    }
Loading