From 60afad806c8053a9e547d4ee531ae4aa64ca05a2 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 19 Dec 2022 15:48:55 -0800 Subject: [PATCH 01/39] Add `RequestId` trait --- aws/rust-runtime/aws-http/src/lib.rs | 5 +- aws/rust-runtime/aws-http/src/request_id.rs | 168 ++++++++++++++++++++ 2 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 aws/rust-runtime/aws-http/src/request_id.rs diff --git a/aws/rust-runtime/aws-http/src/lib.rs b/aws/rust-runtime/aws-http/src/lib.rs index b000c3d6ae..55a54c46a1 100644 --- a/aws/rust-runtime/aws-http/src/lib.rs +++ b/aws/rust-runtime/aws-http/src/lib.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Provides user agent and credentials middleware for the AWS SDK. +//! AWS-specific middleware implementations and HTTP-related features. #![warn( missing_docs, @@ -27,3 +27,6 @@ pub mod user_agent; /// AWS-specific content-encoding tools pub mod content_encoding; + +/// AWS-specific request ID support +pub mod request_id; diff --git a/aws/rust-runtime/aws-http/src/request_id.rs b/aws/rust-runtime/aws-http/src/request_id.rs new file mode 100644 index 0000000000..c3edd35df4 --- /dev/null +++ b/aws/rust-runtime/aws-http/src/request_id.rs @@ -0,0 +1,168 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::http::HttpHeaders; +use aws_smithy_http::operation; +use aws_smithy_http::result::SdkError; +use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; +use http::{HeaderMap, HeaderValue}; + +/// Constant for the [`aws_smithy_types::error::Error`] extra field that contains the request ID +const AWS_REQUEST_ID: &str = "aws_request_id"; + +/// Implementers add a function to return an AWS request ID +pub trait RequestId { + /// Returns the request ID if it's available. + fn request_id(&self) -> Option<&str>; +} + +impl RequestId for SdkError +where + R: HttpHeaders, +{ + fn request_id(&self) -> Option<&str> { + match self { + Self::ResponseError(err) => extract_request_id(err.raw().http_headers()), + Self::ServiceError(err) => extract_request_id(err.raw().http_headers()), + _ => None, + } + } +} + +impl RequestId for GenericError { + fn request_id(&self) -> Option<&str> { + self.extra(AWS_REQUEST_ID) + } +} + +impl RequestId for operation::Response { + fn request_id(&self) -> Option<&str> { + extract_request_id(self.http().headers()) + } +} + +impl RequestId for Result +where + O: RequestId, + E: RequestId, +{ + fn request_id(&self) -> Option<&str> { + match self { + Ok(ok) => ok.request_id(), + Err(err) => err.request_id(), + } + } +} + +/// Applies a request ID to a generic error builder +#[doc(hidden)] +pub fn apply_request_id( + builder: GenericErrorBuilder, + headers: &HeaderMap, +) -> GenericErrorBuilder { + if let Some(request_id) = extract_request_id(headers) { + builder.custom(AWS_REQUEST_ID, request_id) + } else { + builder + } +} + +/// Extracts a request ID from HTTP response headers +fn extract_request_id(headers: &HeaderMap) -> Option<&str> { + headers + .get("x-amzn-requestid") + .or_else(|| headers.get("x-amz-request-id")) + .map(|value| std::str::from_utf8(value.as_bytes()).ok()) + .flatten() +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use http::Response; + + #[test] + fn test_request_id_sdk_error() { + let without_request_id = + || operation::Response::new(Response::builder().body(SdkBody::empty()).unwrap()); + let with_request_id = || { + operation::Response::new( + Response::builder() + .header( + "x-amzn-requestid", + HeaderValue::from_static("some-request-id"), + ) + .body(SdkBody::empty()) + .unwrap(), + ) + }; + assert_eq!( + None, + SdkError::<(), _>::response_error("test", without_request_id()).request_id() + ); + assert_eq!( + Some("some-request-id"), + SdkError::<(), _>::response_error("test", with_request_id()).request_id() + ); + assert_eq!( + None, + SdkError::service_error((), without_request_id()).request_id() + ); + assert_eq!( + Some("some-request-id"), + SdkError::service_error((), with_request_id()).request_id() + ); + } + + #[test] + fn test_extract_request_id() { + let mut headers = HeaderMap::new(); + assert_eq!(None, extract_request_id(&headers)); + + headers.append( + "x-amzn-requestid", + HeaderValue::from_static("some-request-id"), + ); + assert_eq!(Some("some-request-id"), extract_request_id(&headers)); + + headers.append( + "x-amz-request-id", + HeaderValue::from_static("other-request-id"), + ); + assert_eq!(Some("some-request-id"), extract_request_id(&headers)); + + headers.remove("x-amzn-requestid"); + assert_eq!(Some("other-request-id"), extract_request_id(&headers)); + } + + #[test] + fn test_apply_request_id() { + let mut headers = HeaderMap::new(); + assert_eq!( + GenericError::builder().build(), + apply_request_id(GenericError::builder(), &headers).build(), + ); + + headers.append( + "x-amzn-requestid", + HeaderValue::from_static("some-request-id"), + ); + assert_eq!( + GenericError::builder() + .custom(AWS_REQUEST_ID, "some-request-id") + .build(), + apply_request_id(GenericError::builder(), &headers).build(), + ); + } + + #[test] + fn test_generic_error_request_id_impl() { + let err = GenericError::builder() + .custom(AWS_REQUEST_ID, "some-request-id") + .build(); + assert_eq!(Some("some-request-id"), err.request_id()); + } +} From c761516833be5bf5425f7630457a392dd457a719 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 19 Dec 2022 15:52:00 -0800 Subject: [PATCH 02/39] Implement `RequestId` for generated AWS client errors --- .../aws-inlineable/src/s3_errors.rs | 35 +++----- .../smithy/rustsdk/AwsCodegenDecorator.kt | 1 + .../smithy/rustsdk/AwsRequestIdDecorator.kt | 89 +++++++++++++++++++ .../rustsdk/customize/s3/S3Decorator.kt | 45 ++++++++-- .../kms/tests/integration.rs | 1 + .../lambda/tests/request_id.rs | 27 ++++++ .../s3/tests/custom-error-deserializer.rs | 17 ++-- .../integration-tests/s3/tests/request_id.rs | 56 ++++++++++++ .../client/smithy/ClientCodegenVisitor.kt | 10 ++- .../smithy/generators/ServiceGenerator.kt | 6 +- .../protocols/HttpBoundProtocolGenerator.kt | 14 ++- .../ClientEventStreamBaseRequirements.kt | 2 +- .../rust/codegen/core/smithy/RuntimeType.kt | 3 +- .../smithy/customize/CoreCodegenDecorator.kt | 32 +++++-- .../customize/OperationCustomization.kt | 12 +++ .../generators/error/ErrorCustomization.kt | 19 ++++ .../error/OperationErrorGenerator.kt | 55 ++++++------ .../generators/error/ServiceErrorGenerator.kt | 6 +- .../codegen/core/smithy/protocols/AwsJson.kt | 6 +- .../codegen/core/smithy/protocols/AwsQuery.kt | 6 +- .../codegen/core/smithy/protocols/Ec2Query.kt | 6 +- .../codegen/core/smithy/protocols/Protocol.kt | 2 +- .../codegen/core/smithy/protocols/RestJson.kt | 6 +- .../codegen/core/smithy/protocols/RestXml.kt | 6 +- .../parse/EventStreamUnmarshallerGenerator.kt | 2 +- .../error/OperationErrorGeneratorTest.kt | 8 +- .../error/ServiceErrorGeneratorTest.kt | 3 +- rust-runtime/aws-smithy-http/src/http.rs | 37 ++++++++ rust-runtime/aws-smithy-http/src/lib.rs | 1 + rust-runtime/aws-smithy-types/src/error.rs | 26 ++---- .../inlineable/src/ec2_query_errors.rs | 38 ++++---- rust-runtime/inlineable/src/json_errors.rs | 31 +++---- .../src/rest_xml_unwrapped_errors.rs | 17 ++-- .../inlineable/src/rest_xml_wrapped_errors.rs | 34 +++---- 34 files changed, 465 insertions(+), 194 deletions(-) create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt create mode 100644 aws/sdk/integration-tests/lambda/tests/request_id.rs create mode 100644 aws/sdk/integration-tests/s3/tests/request_id.rs create mode 100644 codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt create mode 100644 rust-runtime/aws-smithy-http/src/http.rs diff --git a/aws/rust-runtime/aws-inlineable/src/s3_errors.rs b/aws/rust-runtime/aws-inlineable/src/s3_errors.rs index ca15ddc42b..58f424d5dc 100644 --- a/aws/rust-runtime/aws-inlineable/src/s3_errors.rs +++ b/aws/rust-runtime/aws-inlineable/src/s3_errors.rs @@ -21,23 +21,23 @@ impl ErrorExt for aws_smithy_types::Error { } /// Parses the S3 Extended Request ID out of S3 error response headers. -pub fn parse_extended_error( - error: aws_smithy_types::Error, +pub fn apply_extended_error( + builder: aws_smithy_types::error::Builder, headers: &HeaderMap, -) -> aws_smithy_types::Error { - let mut builder = error.into_builder(); +) -> aws_smithy_types::error::Builder { let host_id = headers .get("x-amz-id-2") .and_then(|header_value| header_value.to_str().ok()); if let Some(host_id) = host_id { - builder.custom(EXTENDED_REQUEST_ID, host_id); + builder.custom(EXTENDED_REQUEST_ID, host_id) + } else { + builder } - builder.build() } #[cfg(test)] mod test { - use crate::s3_errors::{parse_extended_error, ErrorExt}; + use crate::s3_errors::{apply_extended_error, ErrorExt}; #[test] fn add_error_fields() { @@ -49,14 +49,11 @@ mod test { .status(400) .body("") .unwrap(); - let error = aws_smithy_types::Error::builder() - .message("123") - .request_id("456") - .build(); - - let error = parse_extended_error(error, resp.headers()); + let mut builder = aws_smithy_types::Error::builder().message("123"); + builder = apply_extended_error(builder, resp.headers()); assert_eq!( - error + builder + .build() .extended_request_id() .expect("extended request id should be set"), "eftixk72aD6Ap51TnqcoF8eFidJG9Z/2mkiDFu8yU9AS1ed4OpIszj7UDNEHGran" @@ -66,12 +63,8 @@ mod test { #[test] fn handle_missing_header() { let resp = http::Response::builder().status(400).body("").unwrap(); - let error = aws_smithy_types::Error::builder() - .message("123") - .request_id("456") - .build(); - - let error = parse_extended_error(error, resp.headers()); - assert_eq!(error.extended_request_id(), None); + let mut builder = aws_smithy_types::Error::builder().message("123"); + builder = apply_extended_error(builder, resp.headers()); + assert_eq!(builder.build().extended_request_id(), None); } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt index e67b1f58ae..6d8e990b10 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt @@ -37,6 +37,7 @@ val DECORATORS: List = listOf( AwsReadmeDecorator(), HttpConnectorDecorator(), AwsEndpointsStdLib(), + AwsRequestIdDecorator(), // Service specific decorators ApiGatewayDecorator(), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt new file mode 100644 index 0000000000..c75fbdd13a --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorSection + +/** + * Customizes response parsing logic to add AWS request IDs to error metadata and outputs + */ +class AwsRequestIdDecorator : ClientCodegenDecorator { + override val name: String = "AwsRequestIdDecorator" + override val order: Byte = 0 + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = baseCustomizations + listOf(AwsRequestIdOperationCustomization(codegenContext)) + + override fun errorCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + listOf(AwsRequestIdErrorCustomization(codegenContext)) + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + rustCrate.withModule(RustModule.Types) { + // Re-export RequestId in generated crate + rust("pub use #T;", codegenContext.requestIdTrait()) + } + } +} + +private class AwsRequestIdOperationCustomization( + private val codegenContext: ClientCodegenContext, +) : OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + when (section) { + is OperationSection.PopulateGenericErrorExtras -> { + rustTemplate( + "${section.builderName} = #{apply_request_id}(${section.builderName}, ${section.responseName}.headers());", + "apply_request_id" to codegenContext.requestIdModule().resolve("apply_request_id"), + ) + } + else -> {} + } + } +} + +private class AwsRequestIdErrorCustomization(private val codegenContext: ClientCodegenContext) : ErrorCustomization() { + override fun section(section: ErrorSection): Writable = writable { + when (section) { + is ErrorSection.OperationErrorAdditionalTraitImpls -> { + rustTemplate( + """ + impl #{RequestId} for #{error} { + fn request_id(&self) -> Option<&str> { + self.meta.request_id() + } + } + """, + "RequestId" to codegenContext.requestIdTrait(), + "error" to section.errorType, + ) + } + else -> {} + } + } +} + +private fun ClientCodegenContext.requestIdModule(): RuntimeType = + AwsRuntimeType.awsHttp(runtimeConfig).resolve("request_id") + +private fun ClientCodegenContext.requestIdTrait(): RuntimeType = requestIdModule().resolve("RequestId") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index 9c56a24cfe..24e29e7767 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -25,6 +25,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap @@ -56,7 +58,7 @@ class S3Decorator : ClientCodegenDecorator { currentProtocols.letIf(applies(serviceId)) { it + mapOf( RestXmlTrait.ID to ClientRestXmlFactory { protocolConfig -> - S3(protocolConfig) + S3ProtocolOverride(protocolConfig) }, ) } @@ -72,6 +74,13 @@ class S3Decorator : ClientCodegenDecorator { } } + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(applies(codegenContext.serviceShape.id)) { it + listOf(S3ParseExtendedRequestId()) } + override fun libRsCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -84,35 +93,36 @@ class S3Decorator : ClientCodegenDecorator { } } -class S3(codegenContext: CodegenContext) : RestXml(codegenContext) { +class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContext) { private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), "base_errors" to restXmlErrors, - "s3_errors" to AwsRuntimeType.S3Errors, ) override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType { return RuntimeType.forInlineFun("parse_http_generic_error", RustModule.private("xml_deser")) { rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", *errorScope, ) { rustTemplate( """ + // S3 HEAD responses have no response body to for an error code. Therefore, + // check the HTTP response status and populate an error code for 404s. if response.body().is_empty() { - let mut err = #{Error}::builder(); + let mut builder = #{Error}::builder(); if response.status().as_u16() == 404 { - err.code("NotFound"); + builder = builder.code("NotFound"); } - Ok(err.build()) + Ok(builder) } else { - let base_err = #{base_errors}::parse_generic_error(response.body().as_ref())?; - Ok(#{s3_errors}::parse_extended_error(base_err, response.headers())) + #{base_errors}::parse_generic_error(response.body().as_ref()) } """, *errorScope, @@ -122,6 +132,23 @@ class S3(codegenContext: CodegenContext) : RestXml(codegenContext) { } } +/** + * Customizes error parsing logic to add S3's extended request ID to the `aws_smithy_types::error::Error`'s extras. + */ +class S3ParseExtendedRequestId() : OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + when (section) { + is OperationSection.PopulateGenericErrorExtras -> { + rust( + "${section.builderName} = #T::apply_extended_error(${section.builderName}, ${section.responseName}.headers());", + AwsRuntimeType.S3Errors, + ) + } + else -> {} + } + } +} + class S3PubUse : LibRsCustomization() { override fun section(section: LibRsSection): Writable = when (section) { is LibRsSection.Body -> writable { diff --git a/aws/sdk/integration-tests/kms/tests/integration.rs b/aws/sdk/integration-tests/kms/tests/integration.rs index d50a231a50..41f2018f6d 100644 --- a/aws/sdk/integration-tests/kms/tests/integration.rs +++ b/aws/sdk/integration-tests/kms/tests/integration.rs @@ -6,6 +6,7 @@ use aws_http::user_agent::AwsUserAgent; use aws_sdk_kms as kms; use aws_sdk_kms::middleware::DefaultMiddleware; +use aws_sdk_kms::types::RequestId; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_client::{Client as CoreClient, SdkError}; use aws_smithy_http::body::SdkBody; diff --git a/aws/sdk/integration-tests/lambda/tests/request_id.rs b/aws/sdk/integration-tests/lambda/tests/request_id.rs new file mode 100644 index 0000000000..c991b4183a --- /dev/null +++ b/aws/sdk/integration-tests/lambda/tests/request_id.rs @@ -0,0 +1,27 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_sdk_lambda::error::ListFunctionsErrorKind; +use aws_sdk_lambda::operation::ListFunctions; +use aws_sdk_lambda::types::RequestId; +use aws_smithy_http::response::ParseHttpResponse; +use bytes::Bytes; + +#[test] +fn get_request_id_from_unmodeled_error() { + let resp = http::Response::builder() + .header("x-amzn-RequestId", "correct-request-id") + .header("X-Amzn-Errortype", "ListFunctions") + .status(500) + .body("{}") + .unwrap(); + let err = ListFunctions::new() + .parse_loaded(&resp.map(Bytes::from)) + .expect_err("status was 500, this is an error"); + dbg!(&err); + assert!(matches!(err.kind, ListFunctionsErrorKind::Unhandled(_))); + assert_eq!(Some("correct-request-id"), err.request_id()); + assert_eq!(Some("correct-request-id"), err.meta().request_id()); +} diff --git a/aws/sdk/integration-tests/s3/tests/custom-error-deserializer.rs b/aws/sdk/integration-tests/s3/tests/custom-error-deserializer.rs index 46b1fc50f7..7623fb3099 100644 --- a/aws/sdk/integration-tests/s3/tests/custom-error-deserializer.rs +++ b/aws/sdk/integration-tests/s3/tests/custom-error-deserializer.rs @@ -4,6 +4,7 @@ */ use aws_sdk_s3::operation::GetObject; +use aws_sdk_s3::types::RequestId; use aws_sdk_s3::ErrorExt; use aws_smithy_http::response::ParseHttpResponse; use bytes::Bytes; @@ -15,16 +16,16 @@ fn deserialize_extended_errors() { "x-amz-id-2", "gyB+3jRPnrkN98ZajxHXr3u7EFM67bNgSAxexeEHndCX/7GRnfTXxReKUQF28IfP", ) - .header("x-amz-request-id", "3B3C7C725673C630") + .header("x-amz-request-id", "correct-request-id") .status(404) .body( r#" - - NoSuchKey - The resource you requested does not exist - /mybucket/myfoto.jpg - 4442587FB7D0A2F9 -"#, + + NoSuchKey + The resource you requested does not exist + /mybucket/myfoto.jpg + incorrect-request-id + "#, ) .unwrap(); let err = GetObject::new() @@ -34,5 +35,5 @@ fn deserialize_extended_errors() { err.meta().extended_request_id(), Some("gyB+3jRPnrkN98ZajxHXr3u7EFM67bNgSAxexeEHndCX/7GRnfTXxReKUQF28IfP") ); - assert_eq!(err.meta().request_id(), Some("4442587FB7D0A2F9")); + assert_eq!(err.meta().request_id(), Some("correct-request-id")); } diff --git a/aws/sdk/integration-tests/s3/tests/request_id.rs b/aws/sdk/integration-tests/s3/tests/request_id.rs new file mode 100644 index 0000000000..4ee7b1bcb4 --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/request_id.rs @@ -0,0 +1,56 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_sdk_s3::error::GetObjectErrorKind; +use aws_sdk_s3::operation::GetObject; +use aws_sdk_s3::types::RequestId; +use aws_smithy_http::response::ParseHttpResponse; +use bytes::Bytes; + +#[test] +fn get_request_id_from_modeled_error() { + let resp = http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .status(404) + .body( + r#" + + NoSuchKey + The resource you requested does not exist + /mybucket/myfoto.jpg + incorrect-request-id + "#, + ) + .unwrap(); + let err = GetObject::new() + .parse_loaded(&resp.map(Bytes::from)) + .expect_err("status was 404, this is an error"); + assert!(matches!(err.kind, GetObjectErrorKind::NoSuchKey(_))); + assert_eq!(Some("correct-request-id"), err.request_id()); + assert_eq!(Some("correct-request-id"), err.meta().request_id()); +} + +#[test] +fn get_request_id_from_unmodeled_error() { + let resp = http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .status(500) + .body( + r#" + + SomeUnmodeledError + Something bad happened + /mybucket/myfoto.jpg + incorrect-request-id + "#, + ) + .unwrap(); + let err = GetObject::new() + .parse_loaded(&resp.map(Bytes::from)) + .expect_err("status 500"); + assert!(matches!(err.kind, GetObjectErrorKind::Unhandled(_))); + assert_eq!(Some("correct-request-id"), err.request_id()); + assert_eq!(Some("correct-request-id"), err.meta().request_id()); +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index d99b7ccf69..d1d01ee24f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -230,8 +230,13 @@ class ClientCodegenVisitor( val errors = shape.eventStreamErrors() .map { model.expectShape(it.asMemberShape().get().target, StructureShape::class.java) } val errorSymbol = shape.eventStreamErrorSymbol(symbolProvider) - OperationErrorGenerator(model, symbolProvider, symbol, errors) - .renderErrors(this, errorSymbol, symbol) + OperationErrorGenerator( + model, + symbolProvider, + symbol, + errors, + codegenDecorator.errorCustomizations(codegenContext, emptyList()), + ).renderErrors(this, errorSymbol, symbol) } } } @@ -247,6 +252,7 @@ class ClientCodegenVisitor( symbolProvider, operationSymbol, shape.operationErrors(model).map { it.asStructureShape().get() }, + codegenDecorator.errorCustomizations(codegenContext, emptyList()), ).render(this) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index 6710ac9c3d..46e98cd755 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -56,7 +56,11 @@ class ServiceGenerator( } } - ServiceErrorGenerator(clientCodegenContext, operations).render(rustCrate) + ServiceErrorGenerator( + clientCodegenContext, + operations, + decorator.errorCustomizations(clientCodegenContext, emptyList()), + ).render(rustCrate) rustCrate.withModule(RustModule.Config) { ServiceConfigGenerator.withBaseBehavior( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index b78ddfe059..bdf47864fb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -126,7 +126,7 @@ class HttpBoundProtocolTraitImplGenerator( *codegenScope, "O" to outputSymbol, "E" to operationShape.errorSymbol(symbolProvider), - "parse_error" to parseError(operationShape), + "parse_error" to parseError(operationShape, customizations), "parse_response" to parseResponse(operationShape, customizations), ) } @@ -158,12 +158,12 @@ class HttpBoundProtocolTraitImplGenerator( "O" to outputSymbol, "E" to operationShape.errorSymbol(symbolProvider), "parse_streaming_response" to parseStreamingResponse(operationShape, customizations), - "parse_error" to parseError(operationShape), + "parse_error" to parseError(operationShape, customizations), *codegenScope, ) } - private fun parseError(operationShape: OperationShape): RuntimeType { + private fun parseError(operationShape: OperationShape, customizations: List): RuntimeType { val fnName = "parse_${operationShape.id.name.toSnakeCase()}_error" val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) @@ -176,11 +176,17 @@ class HttpBoundProtocolTraitImplGenerator( "O" to outputSymbol, "E" to errorSymbol, ) { + Attribute.AllowUnusedMut.render(this) rust( - "let generic = #T(response).map_err(#T::unhandled)?;", + "let mut generic_builder = #T(response).map_err(#T::unhandled)?;", protocol.parseHttpGenericError(operationShape), errorSymbol, ) + writeCustomizations( + customizations, + OperationSection.PopulateGenericErrorExtras(customizations, "generic_builder", "response"), + ) + rust("let generic = generic_builder.build();") if (operationShape.operationErrors(model).isNotEmpty()) { rustTemplate( """ diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt index 1717dab2d6..4936415ffd 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt @@ -67,6 +67,6 @@ abstract class ClientEventStreamBaseRequirements : EventStreamTestRequirements, ) { - OperationErrorGenerator(model, symbolProvider, operationSymbol, errors).render(writer) + OperationErrorGenerator(model, symbolProvider, operationSymbol, errors, emptyList()).render(writer) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 7d29b3c067..7e8d1e6dec 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -260,7 +260,8 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun document(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("Document") fun errorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ErrorKind") fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::Receiver") - fun genericError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("Error") + fun genericError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Error") + fun genericErrorBuilder(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Builder") fun jsonErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.jsonErrors(runtimeConfig)) fun labelFormat(runtimeConfig: RuntimeConfig, func: String) = smithyHttp(runtimeConfig).resolve("label::$func") fun operation(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("operation::Operation") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt index 1df251c99f..1942621c49 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization import software.amazon.smithy.rust.codegen.core.util.deepMergeWith import java.util.ServiceLoader import java.util.logging.Logger @@ -61,6 +62,14 @@ interface CoreCodegenDecorator { codegenContext: CodegenContext, baseCustomizations: List, ): List = baseCustomizations + + /** + * Hook to customize generated errors. + */ + fun errorCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = baseCustomizations } /** @@ -71,14 +80,6 @@ abstract class CombinedCoreCodegenDecorator { private val orderedDecorators = decorators.sortedBy { it.order } - final override fun libRsCustomizations( - codegenContext: CodegenContext, - baseCustomizations: List, - ): List = - combineCustomizations(baseCustomizations) { decorator, customizations -> - decorator.libRsCustomizations(codegenContext, customizations) - } - final override fun crateManifestCustomizations(codegenContext: CodegenContext): ManifestCustomizations = combineCustomizations(emptyMap()) { decorator, customizations -> customizations.deepMergeWith(decorator.crateManifestCustomizations(codegenContext)) @@ -93,6 +94,21 @@ abstract class CombinedCoreCodegenDecorator, + ): List = + combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.libRsCustomizations(codegenContext, customizations) + } + + override fun errorCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.errorCustomizations(codegenContext, customizations) + } + /** * Combines customizations from multiple ordered codegen decorators. * diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt index a7dd176a9e..f8f064a10b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt @@ -53,6 +53,18 @@ sealed class OperationSection(name: String) : Section(name) { override val customizations: List, val operationShape: OperationShape, ) : OperationSection("MutateOutput") + + /** + * Allows for adding additional properties to the `extras` field on the + * `aws_smithy_types::error::Error` generic error type. + */ + data class PopulateGenericErrorExtras( + override val customizations: List, + /** Name of the generic error builder (for referring to it in Rust code) */ + val builderName: String, + /** Name of the response (for referring to it in Rust code) */ + val responseName: String, + ) : OperationSection("PopulateGenericErrorExtras") } abstract class OperationCustomization : NamedSectionGenerator() { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt new file mode 100644 index 0000000000..6b9c3d849a --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt @@ -0,0 +1,19 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.smithy.generators.error + +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedSectionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section + +/** Error customization sections */ +sealed class ErrorSection(name: String) : Section(name) { + /** Use this section to add additional trait implementations to the generated operation errors */ + data class OperationErrorAdditionalTraitImpls(val errorType: RuntimeType) : ErrorSection("OperationErrorAdditionalTraitImpls") +} + +/** Customizations for generated errors */ +abstract class ErrorCustomization : NamedSectionGenerator() diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt index 7e5bf08b7f..de305056f5 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt @@ -28,6 +28,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable 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.customize.Section +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.toSnakeCase @@ -60,6 +61,7 @@ class OperationErrorGenerator( private val symbolProvider: RustSymbolProvider, private val operationSymbol: Symbol, private val errors: List, + private val customizations: List, ) { private val runtimeConfig = symbolProvider.config().runtimeConfig private val genericError = RuntimeType.genericError(symbolProvider.config().runtimeConfig) @@ -73,7 +75,7 @@ class OperationErrorGenerator( fun renderErrors( writer: RustWriter, - errorSymbol: RuntimeType, + errorType: RuntimeType, operationSymbol: Symbol, ) { val meta = RustMetadata( @@ -84,21 +86,21 @@ class OperationErrorGenerator( writer.rust("/// Error type for the `${operationSymbol.name}` operation.") meta.render(writer) - writer.rustBlock("struct ${errorSymbol.name}") { + writer.rustBlock("struct ${errorType.name}") { rust( """ /// Kind of error that occurred. - pub kind: ${errorSymbol.name}Kind, + pub kind: ${errorType.name}Kind, /// Additional metadata about the error, including error code, message, and request ID. pub (crate) meta: #T """, RuntimeType.genericError(runtimeConfig), ) } - writer.rustBlock("impl #T for ${errorSymbol.name}", createUnhandledError) { + writer.rustBlock("impl #T for ${errorType.name}", createUnhandledError) { rustBlock("fn create_unhandled_error(source: Box) -> Self") { rustBlock("Self") { - rust("kind: ${errorSymbol.name}Kind::Unhandled(#T::new(source)),", unhandledError()) + rust("kind: ${errorType.name}Kind::Unhandled(#T::new(source)),", unhandledError()) rust("meta: Default::default()") } } @@ -106,7 +108,7 @@ class OperationErrorGenerator( writer.rust("/// Types of errors that can occur for the `${operationSymbol.name}` operation.") meta.render(writer) - writer.rustBlock("enum ${errorSymbol.name}Kind") { + writer.rustBlock("enum ${errorType.name}Kind") { errors.forEach { errorVariant -> documentShape(errorVariant, model) deprecatedShape(errorVariant) @@ -121,21 +123,23 @@ class OperationErrorGenerator( unhandledError(), ) } - writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.Display) { + writer.rustBlock("impl #T for ${errorType.name}", RuntimeType.Display) { rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { - delegateToVariants(errors, errorSymbol) { + delegateToVariants(errors, errorType) { writable { rust("_inner.fmt(f)") } } } } + writer.writeCustomizations(customizations, ErrorSection.OperationErrorAdditionalTraitImpls(errorType)) + val errorKindT = RuntimeType.errorKind(symbolProvider.config().runtimeConfig) writer.rustBlock( - "impl #T for ${errorSymbol.name}", + "impl #T for ${errorType.name}", RuntimeType.provideErrorKind(symbolProvider.config().runtimeConfig), ) { rustBlock("fn code(&self) -> Option<&str>") { - rust("${errorSymbol.name}::code(self)") + rust("${errorType.name}::code(self)") } rustBlock("fn retryable_error_kind(&self) -> Option<#T>", errorKindT) { @@ -146,7 +150,7 @@ class OperationErrorGenerator( rustBlock("match &self.kind") { retryableVariants.forEach { val errorVariantSymbol = symbolProvider.toSymbol(it) - rust("${errorSymbol.name}Kind::${errorVariantSymbol.name}(inner) => Some(inner.retryable_error_kind()),") + rust("${errorType.name}Kind::${errorVariantSymbol.name}(inner) => Some(inner.retryable_error_kind()),") } rust("_ => None") } @@ -154,27 +158,27 @@ class OperationErrorGenerator( } } - writer.rustBlock("impl ${errorSymbol.name}") { + writer.rustBlock("impl ${errorType.name}") { writer.rustTemplate( """ - /// Creates a new `${errorSymbol.name}`. - pub fn new(kind: ${errorSymbol.name}Kind, meta: #{generic_error}) -> Self { + /// Creates a new `${errorType.name}`. + pub fn new(kind: ${errorType.name}Kind, meta: #{generic_error}) -> Self { Self { kind, meta } } - /// Creates the `${errorSymbol.name}::Unhandled` variant from any error type. + /// Creates the `${errorType.name}::Unhandled` variant from any error type. pub fn unhandled(err: impl Into>) -> Self { Self { - kind: ${errorSymbol.name}Kind::Unhandled(#{Unhandled}::new(err.into())), + kind: ${errorType.name}Kind::Unhandled(#{Unhandled}::new(err.into())), meta: Default::default() } } - /// Creates the `${errorSymbol.name}::Unhandled` variant from a `#{generic_error}`. + /// Creates the `${errorType.name}::Unhandled` variant from a `#{generic_error}`. pub fn generic(err: #{generic_error}) -> Self { Self { meta: err.clone(), - kind: ${errorSymbol.name}Kind::Unhandled(#{Unhandled}::new(err.into())), + kind: ${errorType.name}Kind::Unhandled(#{Unhandled}::new(err.into())), } } @@ -189,11 +193,6 @@ class OperationErrorGenerator( &self.meta } - /// Returns the request ID if it's available. - pub fn request_id(&self) -> Option<&str> { - self.meta.request_id() - } - /// Returns the error code if it's available. pub fn code(&self) -> Option<&str> { self.meta.code() @@ -206,16 +205,16 @@ class OperationErrorGenerator( errors.forEach { error -> val errorVariantSymbol = symbolProvider.toSymbol(error) val fnName = errorVariantSymbol.name.toSnakeCase() - writer.rust("/// Returns `true` if the error kind is `${errorSymbol.name}Kind::${errorVariantSymbol.name}`.") + writer.rust("/// Returns `true` if the error kind is `${errorType.name}Kind::${errorVariantSymbol.name}`.") writer.rustBlock("pub fn is_$fnName(&self) -> bool") { - rust("matches!(&self.kind, ${errorSymbol.name}Kind::${errorVariantSymbol.name}(_))") + rust("matches!(&self.kind, ${errorType.name}Kind::${errorVariantSymbol.name}(_))") } } } - writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.StdError) { + writer.rustBlock("impl #T for ${errorType.name}", RuntimeType.StdError) { rustBlock("fn source(&self) -> Option<&(dyn #T + 'static)>", RuntimeType.StdError) { - delegateToVariants(errors, errorSymbol) { + delegateToVariants(errors, errorType) { writable { rust("Some(_inner)") } @@ -224,7 +223,7 @@ class OperationErrorGenerator( } } - sealed class VariantMatch(name: String) : Section(name) { + private sealed class VariantMatch(name: String) : Section(name) { object Unhandled : VariantMatch("Unhandled") data class Modeled(val symbol: Symbol, val shape: Shape) : VariantMatch("Modeled") } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt index c03a30b6cc..be63f1245c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt @@ -42,7 +42,11 @@ import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamE * } * ``` */ -class ServiceErrorGenerator(private val codegenContext: CodegenContext, private val operations: List) { +class ServiceErrorGenerator( + private val codegenContext: CodegenContext, + private val operations: List, + private val customizations: List, +) { private val symbolProvider = codegenContext.symbolProvider private val model = codegenContext.model diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt index 8c0ad26d88..07ddc7cd7e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt @@ -111,7 +111,7 @@ open class AwsJson( private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), "HeaderMap" to RuntimeType.Http.resolve("HeaderMap"), "JsonError" to CargoDependency.smithyJson(runtimeConfig).toType() .resolve("deserialize::error::DeserializeError"), @@ -146,7 +146,7 @@ open class AwsJson( RuntimeType.forInlineFun("parse_http_generic_error", jsonDeserModule) { rustTemplate( """ - pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{JsonError}> { + pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{JsonError}> { #{json_errors}::parse_generic_error(response.body(), response.headers()) } """, @@ -158,7 +158,7 @@ open class AwsJson( RuntimeType.forInlineFun("parse_event_stream_generic_error", jsonDeserModule) { rustTemplate( """ - pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{Error}, #{JsonError}> { + pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{ErrorBuilder}, #{JsonError}> { // Note: HeaderMap::new() doesn't allocate #{json_errors}::parse_generic_error(payload, &#{HeaderMap}::new()) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt index 6bb7a62a58..d3334ec91a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt @@ -45,7 +45,7 @@ class AwsQueryProtocol(private val codegenContext: CodegenContext) : Protocol { private val awsQueryErrors: RuntimeType = RuntimeType.wrappedXmlErrors(runtimeConfig) private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), @@ -68,7 +68,7 @@ class AwsQueryProtocol(private val codegenContext: CodegenContext) : Protocol { override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = RuntimeType.forInlineFun("parse_http_generic_error", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", *errorScope, ) { rust("#T::parse_generic_error(response.body().as_ref())", awsQueryErrors) @@ -78,7 +78,7 @@ class AwsQueryProtocol(private val codegenContext: CodegenContext) : Protocol { override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = RuntimeType.forInlineFun("parse_event_stream_generic_error", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", *errorScope, ) { rust("#T::parse_generic_error(payload.as_ref())", awsQueryErrors) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt index c388b8e85b..05e14bdac9 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt @@ -27,7 +27,7 @@ class Ec2QueryProtocol(private val codegenContext: CodegenContext) : Protocol { private val ec2QueryErrors: RuntimeType = RuntimeType.ec2QueryErrors(runtimeConfig) private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), @@ -59,7 +59,7 @@ class Ec2QueryProtocol(private val codegenContext: CodegenContext) : Protocol { override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = RuntimeType.forInlineFun("parse_http_generic_error", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", *errorScope, ) { rust("#T::parse_generic_error(response.body().as_ref())", ec2QueryErrors) @@ -69,7 +69,7 @@ class Ec2QueryProtocol(private val codegenContext: CodegenContext) : Protocol { override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = RuntimeType.forInlineFun("parse_event_stream_generic_error", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", *errorScope, ) { rust("#T::parse_generic_error(payload.as_ref())", ec2QueryErrors) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt index c5d93ae3b0..046cd76a7d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt @@ -46,7 +46,7 @@ interface Protocol { /** * Generates a function signature like the following: * ```rust - * fn parse_http_generic_error(response: &Response) -> aws_smithy_types::error::Error + * fn parse_http_generic_error(response: &Response) -> aws_smithy_types::error::Builder * ``` */ fun parseHttpGenericError(operationShape: OperationShape): RuntimeType diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt index cbcd2d511b..9eeee79038 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt @@ -66,7 +66,7 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), "HeaderMap" to RuntimeType.Http.resolve("HeaderMap"), "JsonError" to CargoDependency.smithyJson(runtimeConfig).toType() .resolve("deserialize::error::DeserializeError"), @@ -106,7 +106,7 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { RuntimeType.forInlineFun("parse_http_generic_error", jsonDeserModule) { rustTemplate( """ - pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{JsonError}> { + pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{JsonError}> { #{json_errors}::parse_generic_error(response.body(), response.headers()) } """, @@ -118,7 +118,7 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { RuntimeType.forInlineFun("parse_event_stream_generic_error", jsonDeserModule) { rustTemplate( """ - pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{Error}, #{JsonError}> { + pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{ErrorBuilder}, #{JsonError}> { // Note: HeaderMap::new() doesn't allocate #{json_errors}::parse_generic_error(payload, &#{HeaderMap}::new()) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt index 44a9631ef7..c42fefd3b4 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt @@ -27,7 +27,7 @@ open class RestXml(val codegenContext: CodegenContext) : Protocol { private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), + "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), @@ -58,7 +58,7 @@ open class RestXml(val codegenContext: CodegenContext) : Protocol { override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = RuntimeType.forInlineFun("parse_http_generic_error", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", *errorScope, ) { rust("#T::parse_generic_error(response.body().as_ref())", restXmlErrors) @@ -68,7 +68,7 @@ open class RestXml(val codegenContext: CodegenContext) : Protocol { override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = RuntimeType.forInlineFun("parse_event_stream_generic_error", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{Error}, #{XmlDecodeError}>", + "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", *errorScope, ) { rust("#T::parse_generic_error(payload.as_ref())", restXmlErrors) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index e6bf4812f3..9565a59cfb 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -307,7 +307,7 @@ class EventStreamUnmarshallerGenerator( rustTemplate( """ let generic = match #{parse_generic_error}(message.payload()) { - Ok(generic) => generic, + Ok(builder) => builder.build(), Err(err) => return Ok(#{UnmarshalledMessage}::Error(#{OpError}::unhandled(err))), }; """, diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGeneratorTest.kt index 38b27ecf4f..788f29bf7a 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGeneratorTest.kt @@ -55,7 +55,13 @@ class OperationErrorGeneratorTest { model.lookup("error#$it").renderWithModelBuilder(model, symbolProvider, this) } val errors = listOf("FooException", "ComplexError", "InvalidGreeting").map { model.lookup("error#$it") } - val generator = OperationErrorGenerator(model, symbolProvider, symbolProvider.toSymbol(model.lookup("error#Greeting")), errors) + val generator = OperationErrorGenerator( + model, + symbolProvider, + symbolProvider.toSymbol(model.lookup("error#Greeting")), + errors, + emptyList(), + ) generator.render(this) unitTest( diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt index 3555274327..dd8db9cd34 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt @@ -90,6 +90,7 @@ internal class ServiceErrorGeneratorTest { symbolProvider, symbolProvider.toSymbol(operation), operation.operationErrors(model).map { it as StructureShape }, + emptyList(), ).render(this) } } @@ -99,7 +100,7 @@ internal class ServiceErrorGeneratorTest { } } } - ServiceErrorGenerator(codegenContext, model.operationShapes.toList()).render(rustCrate) + ServiceErrorGenerator(codegenContext, model.operationShapes.toList(), emptyList()).render(rustCrate) testDir.resolve("tests").createDirectory() testDir.resolve("tests/validate_errors.rs").writeText( diff --git a/rust-runtime/aws-smithy-http/src/http.rs b/rust-runtime/aws-smithy-http/src/http.rs new file mode 100644 index 0000000000..4c7bcbe93b --- /dev/null +++ b/rust-runtime/aws-smithy-http/src/http.rs @@ -0,0 +1,37 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use http::{HeaderMap, HeaderValue}; + +/// Trait for accessing HTTP headers. +/// +/// Useful for generic impls so that they can access headers via trait bounds. +pub trait HttpHeaders { + /// Returns a reference to the associated header map. + fn http_headers(&self) -> &HeaderMap; + + /// Returns a mutable reference to the associated header map. + fn http_headers_mut(&mut self) -> &mut HeaderMap; +} + +impl HttpHeaders for http::Response { + fn http_headers(&self) -> &HeaderMap { + self.headers() + } + + fn http_headers_mut(&mut self) -> &mut HeaderMap { + self.headers_mut() + } +} + +impl HttpHeaders for crate::operation::Response { + fn http_headers(&self) -> &HeaderMap { + self.http().http_headers() + } + + fn http_headers_mut(&mut self) -> &mut HeaderMap { + self.http_mut().http_headers_mut() + } +} diff --git a/rust-runtime/aws-smithy-http/src/lib.rs b/rust-runtime/aws-smithy-http/src/lib.rs index 54449bf38e..4271dff8fa 100644 --- a/rust-runtime/aws-smithy-http/src/lib.rs +++ b/rust-runtime/aws-smithy-http/src/lib.rs @@ -20,6 +20,7 @@ pub mod body; pub mod endpoint; pub mod header; +pub mod http; pub mod http_versions; pub mod label; pub mod middleware; diff --git a/rust-runtime/aws-smithy-types/src/error.rs b/rust-runtime/aws-smithy-types/src/error.rs index dc41a67d83..1945b7334a 100644 --- a/rust-runtime/aws-smithy-types/src/error.rs +++ b/rust-runtime/aws-smithy-types/src/error.rs @@ -20,7 +20,6 @@ pub mod display; pub struct Error { code: Option, message: Option, - request_id: Option, extras: HashMap<&'static str, String>, } @@ -32,23 +31,17 @@ pub struct Builder { impl Builder { /// Sets the error message. - pub fn message(&mut self, message: impl Into) -> &mut Self { + pub fn message(mut self, message: impl Into) -> Self { self.inner.message = Some(message.into()); self } /// Sets the error code. - pub fn code(&mut self, code: impl Into) -> &mut Self { + pub fn code(mut self, code: impl Into) -> Self { self.inner.code = Some(code.into()); self } - /// Sets the request ID the error happened for. - pub fn request_id(&mut self, request_id: impl Into) -> &mut Self { - self.inner.request_id = Some(request_id.into()); - self - } - /// Set a custom field on the error metadata /// /// Typically, these will be accessed with an extension trait: @@ -70,18 +63,18 @@ impl Builder { /// use S3ErrorExt; /// let sdk_response: Result<(), Error> = Err(Error::builder().custom(HOST_ID, "x-1234").build()); /// if let Err(err) = sdk_response { - /// println!("request id: {:?}, extended request id: {:?}", err.request_id(), err.extended_request_id()); + /// println!("extended request id: {:?}", err.extended_request_id()); /// } /// } /// ``` - pub fn custom(&mut self, key: &'static str, value: impl Into) -> &mut Self { + pub fn custom(mut self, key: &'static str, value: impl Into) -> Self { self.inner.extras.insert(key, value.into()); self } /// Creates the error. - pub fn build(&mut self) -> Error { - std::mem::take(&mut self.inner) + pub fn build(self) -> Error { + self.inner } } @@ -94,10 +87,6 @@ impl Error { pub fn message(&self) -> Option<&str> { self.message.as_deref() } - /// Returns the request ID the error occurred for, if it's available. - pub fn request_id(&self) -> Option<&str> { - self.request_id.as_deref() - } /// Returns additional information about the error if it's present. pub fn extra(&self, key: &'static str) -> Option<&str> { self.extras.get(key).map(|k| k.as_str()) @@ -133,9 +122,6 @@ impl fmt::Display for Error { if let Some(message) = &self.message { fmt.field("message", message); } - if let Some(req_id) = &self.request_id { - fmt.field("request_id", req_id); - } for (k, v) in &self.extras { fmt.field(k, &v); } diff --git a/rust-runtime/inlineable/src/ec2_query_errors.rs b/rust-runtime/inlineable/src/ec2_query_errors.rs index a7fc1b1163..55aa5554a8 100644 --- a/rust-runtime/inlineable/src/ec2_query_errors.rs +++ b/rust-runtime/inlineable/src/ec2_query_errors.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; use aws_smithy_xml::decode::{try_data, Document, ScopedDecoder, XmlDecodeError}; use std::convert::TryFrom; @@ -13,36 +14,30 @@ pub fn body_is_error(body: &[u8]) -> Result { Ok(scoped.start_el().matches("Response")) } -pub fn parse_generic_error(body: &[u8]) -> Result { +pub fn parse_generic_error(body: &[u8]) -> Result { let mut doc = Document::try_from(body)?; let mut root = doc.root_element()?; - let mut err_builder = aws_smithy_types::Error::builder(); + let mut err_builder = GenericError::builder(); while let Some(mut tag) = root.next_tag() { - match tag.start_el().local() { - "Errors" => { - while let Some(mut error_tag) = tag.next_tag() { - if let "Error" = error_tag.start_el().local() { - while let Some(mut error_field) = error_tag.next_tag() { - match error_field.start_el().local() { - "Code" => { - err_builder.code(try_data(&mut error_field)?); - } - "Message" => { - err_builder.message(try_data(&mut error_field)?); - } - _ => {} + if tag.start_el().local() == "Errors" { + while let Some(mut error_tag) = tag.next_tag() { + if let "Error" = error_tag.start_el().local() { + while let Some(mut error_field) = error_tag.next_tag() { + match error_field.start_el().local() { + "Code" => { + err_builder = err_builder.code(try_data(&mut error_field)?); } + "Message" => { + err_builder = err_builder.message(try_data(&mut error_field)?); + } + _ => {} } } } } - "RequestId" => { - err_builder.request_id(try_data(&mut tag)?); - } - _ => {} } } - Ok(err_builder.build()) + Ok(err_builder) } #[allow(unused)] @@ -92,8 +87,7 @@ mod test { "#; assert!(body_is_error(xml).unwrap()); - let parsed = parse_generic_error(xml).expect("valid xml"); - assert_eq!(parsed.request_id(), Some("foo-id")); + let parsed = parse_generic_error(xml).expect("valid xml").build(); assert_eq!(parsed.message(), Some("Hi")); assert_eq!(parsed.code(), Some("InvalidGreeting")); } diff --git a/rust-runtime/inlineable/src/json_errors.rs b/rust-runtime/inlineable/src/json_errors.rs index ea13da3ba8..065e5cae1a 100644 --- a/rust-runtime/inlineable/src/json_errors.rs +++ b/rust-runtime/inlineable/src/json_errors.rs @@ -5,7 +5,7 @@ use aws_smithy_json::deserialize::token::skip_value; use aws_smithy_json::deserialize::{error::DeserializeError, json_token_iter, Token}; -use aws_smithy_types::Error as SmithyError; +use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; use bytes::Bytes; use http::header::ToStrError; use http::{HeaderMap, HeaderValue}; @@ -82,33 +82,24 @@ fn error_type_from_header(headers: &HeaderMap) -> Result) -> Option<&str> { - headers - .get("X-Amzn-Requestid") - .and_then(|v| v.to_str().ok()) -} - pub fn parse_generic_error( payload: &Bytes, headers: &HeaderMap, -) -> Result { +) -> Result { let ErrorBody { code, message } = parse_error_body(payload.as_ref())?; - let mut err_builder = SmithyError::builder(); + let mut err_builder = GenericError::builder(); if let Some(code) = error_type_from_header(headers) .map_err(|_| DeserializeError::custom("X-Amzn-Errortype header was not valid UTF-8"))? .or(code.as_deref()) .map(sanitize_error_code) { - err_builder.code(code); + err_builder = err_builder.code(code); } if let Some(message) = message { - err_builder.message(message); + err_builder = err_builder.message(message); } - if let Some(request_id) = request_id(headers) { - err_builder.request_id(request_id); - } - Ok(err_builder.build()) + Ok(err_builder) } #[cfg(test)] @@ -121,17 +112,17 @@ mod test { #[test] fn generic_error() { let response = http::Response::builder() - .header("X-Amzn-Requestid", "1234") .body(Bytes::from_static( br#"{ "__type": "FooError", "message": "Go to foo" }"#, )) .unwrap(); assert_eq!( - parse_generic_error(response.body(), response.headers()).unwrap(), + parse_generic_error(response.body(), response.headers()) + .unwrap() + .build(), Error::builder() .code("FooError") .message("Go to foo") - .request_id("1234") .build() ) } @@ -209,7 +200,9 @@ mod test { )) .unwrap(); assert_eq!( - parse_generic_error(response.body(), response.headers()).unwrap(), + parse_generic_error(response.body(), response.headers()) + .unwrap() + .build(), Error::builder() .code("ResourceNotFoundException") .message("Functions from 'us-west-2' are not reachable from us-east-1") diff --git a/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs b/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs index df0f22ef4e..d93ba1b262 100644 --- a/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs +++ b/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs @@ -6,6 +6,7 @@ //! Error abstractions for `noErrorWrapping`. Code generators should either inline this file //! or its companion `rest_xml_wrapped_errors.rs` for code generation +use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; use aws_smithy_xml::decode::{try_data, Document, ScopedDecoder, XmlDecodeError}; use std::convert::TryFrom; @@ -26,25 +27,22 @@ pub fn error_scope<'a, 'b>( Ok(scoped) } -pub fn parse_generic_error(body: &[u8]) -> Result { +pub fn parse_generic_error(body: &[u8]) -> Result { let mut doc = Document::try_from(body)?; let mut root = doc.root_element()?; - let mut err = aws_smithy_types::Error::builder(); + let mut builder = GenericError::builder(); while let Some(mut tag) = root.next_tag() { match tag.start_el().local() { "Code" => { - err.code(try_data(&mut tag)?); + builder = builder.code(try_data(&mut tag)?); } "Message" => { - err.message(try_data(&mut tag)?); - } - "RequestId" => { - err.request_id(try_data(&mut tag)?); + builder = builder.message(try_data(&mut tag)?); } _ => {} } } - Ok(err.build()) + Ok(builder) } #[cfg(test)] @@ -61,8 +59,7 @@ mod test { foo-id "#; assert!(body_is_error(xml).unwrap()); - let parsed = parse_generic_error(xml).expect("valid xml"); - assert_eq!(parsed.request_id(), Some("foo-id")); + let parsed = parse_generic_error(xml).expect("valid xml").build(); assert_eq!(parsed.message(), Some("Hi")); assert_eq!(parsed.code(), Some("InvalidGreeting")); } diff --git a/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs b/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs index c90301bf39..f580b310d2 100644 --- a/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs +++ b/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; use aws_smithy_xml::decode::{try_data, Document, ScopedDecoder, XmlDecodeError}; use std::convert::TryFrom; @@ -13,32 +14,26 @@ pub fn body_is_error(body: &[u8]) -> Result { Ok(scoped.start_el().matches("ErrorResponse")) } -pub fn parse_generic_error(body: &[u8]) -> Result { +pub fn parse_generic_error(body: &[u8]) -> Result { let mut doc = Document::try_from(body)?; let mut root = doc.root_element()?; - let mut err_builder = aws_smithy_types::Error::builder(); + let mut err_builder = GenericError::builder(); while let Some(mut tag) = root.next_tag() { - match tag.start_el().local() { - "Error" => { - while let Some(mut error_field) = tag.next_tag() { - match error_field.start_el().local() { - "Code" => { - err_builder.code(try_data(&mut error_field)?); - } - "Message" => { - err_builder.message(try_data(&mut error_field)?); - } - _ => {} + if tag.start_el().local() == "Error" { + while let Some(mut error_field) = tag.next_tag() { + match error_field.start_el().local() { + "Code" => { + err_builder = err_builder.code(try_data(&mut error_field)?); } + "Message" => { + err_builder = err_builder.message(try_data(&mut error_field)?); + } + _ => {} } } - "RequestId" => { - err_builder.request_id(try_data(&mut tag)?); - } - _ => {} } } - Ok(err_builder.build()) + Ok(err_builder) } #[allow(unused)] @@ -83,8 +78,7 @@ mod test { foo-id "#; assert!(body_is_error(xml).unwrap()); - let parsed = parse_generic_error(xml).expect("valid xml"); - assert_eq!(parsed.request_id(), Some("foo-id")); + let parsed = parse_generic_error(xml).expect("valid xml").build(); assert_eq!(parsed.message(), Some("Hi")); assert_eq!(parsed.code(), Some("InvalidGreeting")); } From 1250e659c93b4ca737f3035b0ea91c3374383a97 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 19 Dec 2022 17:19:33 -0800 Subject: [PATCH 03/39] Move `RustWriter.implBlock` out of `StructureGenerator` --- .../rust/codegen/client/smithy/ClientCodegenVisitor.kt | 4 ++-- .../generators/protocol/ClientProtocolGenerator.kt | 6 +++--- .../smithy/generators/EndpointTraitBindingsTest.kt | 4 ++-- .../eventstream/ClientEventStreamBaseRequirements.kt | 4 ++-- .../smithy/rust/codegen/core/rustlang/RustWriter.kt | 7 +++++++ .../codegen/core/smithy/generators/StructureGenerator.kt | 9 --------- .../smithy/rust/codegen/core/testutil/TestHelpers.kt | 4 ++-- .../core/smithy/generators/BuilderGeneratorTest.kt | 7 ++++--- .../rust/codegen/server/smithy/ServerCodegenVisitor.kt | 6 +++--- .../codegen/server/smithy/testutil/ServerTestHelpers.kt | 4 ++-- .../smithy/generators/ServerBuilderGeneratorTest.kt | 4 ++-- .../eventstream/ServerEventStreamBaseRequirements.kt | 6 +++--- .../ServerEventStreamUnmarshallerGeneratorTest.kt | 4 ++-- 13 files changed, 34 insertions(+), 35 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index d1d01ee24f..a8510c7d2d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -25,6 +25,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientProtoco import software.amazon.smithy.rust.codegen.client.smithy.transformers.AddErrorMessage import software.amazon.smithy.rust.codegen.client.smithy.transformers.RemoveEventStreamOperations 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.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig @@ -34,7 +35,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGener import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.OperationErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer @@ -193,7 +193,7 @@ class ClientCodegenVisitor( if (!shape.hasTrait()) { val builderGenerator = BuilderGenerator(codegenContext.model, codegenContext.symbolProvider, shape) builderGenerator.render(this) - this.implBlock(shape, symbolProvider) { + implBlock(symbolProvider.toSymbol(shape)) { builderGenerator.renderConvenienceMethod(this) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt index 9b66e01187..a28b9e8f0f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.client.Fluen import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.docLink +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.rustBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext @@ -18,7 +19,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustom import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolTraitImplGenerator @@ -51,7 +51,7 @@ open class ClientProtocolGenerator( // impl OperationInputShape { ... } val operationName = symbolProvider.toSymbol(operationShape).name - inputWriter.implBlock(inputShape, symbolProvider) { + inputWriter.implBlock(symbolProvider.toSymbol(inputShape)) { writeCustomizations( customizations, OperationSection.InputImpl(customizations, operationShape, inputShape, protocol), @@ -78,7 +78,7 @@ open class ClientProtocolGenerator( operationWriter.rustBlock("pub struct $operationName") { write("_private: ()") } - operationWriter.implBlock(operationShape, symbolProvider) { + operationWriter.implBlock(symbolProvider.toSymbol(operationShape)) { builderGenerator.renderConvenienceMethod(this) rust("/// Creates a new `$operationName` operation.") diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt index f682446d05..ee57996449 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt @@ -12,10 +12,10 @@ import software.amazon.smithy.model.traits.EndpointTrait import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider 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.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock 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.TestWorkspace @@ -67,7 +67,7 @@ internal class EndpointTraitBindingsTest { } """, ) - implBlock(model.lookup("test#GetStatusInput"), sym) { + implBlock(sym.toSymbol(model.lookup("test#GetStatusInput"))) { rustBlock( "fn endpoint_prefix(&self) -> std::result::Result<#T::endpoint::EndpointPrefix, #T>", RuntimeType.smithyHttp(TestRuntimeConfig), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt index 4936415ffd..8926d7b30f 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt @@ -18,11 +18,11 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClien import software.amazon.smithy.rust.codegen.client.testutil.clientTestRustSettings import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.OperationErrorGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestModels import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestRequirements import java.util.stream.Stream @@ -54,7 +54,7 @@ abstract class ClientEventStreamBaseRequirements : EventStreamTestRequirements elem.appendChildren(childNodesCopy()) }) } +/** Write an `impl` block for the given symbol */ +fun RustWriter.implBlock(symbol: Symbol, block: Writable) { + rustBlock("impl ${symbol.name}") { + block() + } +} + /** * Write _exactly_ the text as written into the code writer without newlines or formatting */ diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index 5f4e9ffb91..a44a8cac48 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -6,16 +6,13 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape -import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.SensitiveTrait import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asDeref import software.amazon.smithy.rust.codegen.core.rustlang.asRef import software.amazon.smithy.rust.codegen.core.rustlang.deprecatedShape @@ -36,12 +33,6 @@ import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.redactIfNecessary -fun RustWriter.implBlock(structureShape: Shape, symbolProvider: SymbolProvider, block: Writable) { - rustBlock("impl ${symbolProvider.toSymbol(structureShape).name}") { - block() - } -} - open class StructureGenerator( val model: Model, private val symbolProvider: RustSymbolProvider, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt index 40f0014327..9587494b19 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordSymbolProvider import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.smithy.BaseSymbolMetadataProvider import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget @@ -26,7 +27,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.letIf import java.io.File @@ -107,7 +107,7 @@ fun StructureShape.renderWithModelBuilder( StructureGenerator(model, symbolProvider, writer, this).render(forWhom) val modelBuilder = BuilderGenerator(model, symbolProvider, this) modelBuilder.render(writer) - writer.implBlock(this, symbolProvider) { + writer.implBlock(symbolProvider.toSymbol(this)) { modelBuilder.renderConvenienceMethod(this) } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt index efbfbe5ab4..9ba33ed107 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.traits.EnumDefinition import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +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.smithy.Default import software.amazon.smithy.rust.codegen.core.smithy.MaybeRenamed @@ -37,7 +38,7 @@ internal class BuilderGeneratorTest { generator.render() innerGenerator.render() builderGenerator.render(writer) - writer.implBlock(struct, provider) { + writer.implBlock(provider.toSymbol(struct)) { builderGenerator.renderConvenienceMethod(this) } writer.compileAndTest( @@ -84,7 +85,7 @@ internal class BuilderGeneratorTest { innerGenerator.render() val builderGenerator = BuilderGenerator(model, provider, struct) builderGenerator.render(writer) - writer.implBlock(struct, provider) { + writer.implBlock(provider.toSymbol(struct)) { builderGenerator.renderConvenienceMethod(this) } writer.compileAndTest( @@ -104,7 +105,7 @@ internal class BuilderGeneratorTest { val builderGenerator = BuilderGenerator(model, provider, credentials) credsGenerator.render() builderGenerator.render(writer) - writer.implBlock(credentials, provider) { + writer.implBlock(provider.toSymbol(credentials)) { builderGenerator.renderConvenienceMethod(this) } writer.compileAndTest( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index dc35293bca..d8aeec76c4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -30,6 +30,7 @@ import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.ConstrainedModule import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings @@ -40,7 +41,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.UnconstrainedModule import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer @@ -261,7 +261,7 @@ open class ServerCodegenVisitor( serverBuilderGenerator.render(writer) if (codegenContext.settings.codegenConfig.publicConstrainedTypes) { - writer.implBlock(shape, codegenContext.symbolProvider) { + writer.implBlock(codegenContext.symbolProvider.toSymbol(shape)) { serverBuilderGenerator.renderConvenienceMethod(this) } } @@ -281,7 +281,7 @@ open class ServerCodegenVisitor( ServerBuilderGeneratorWithoutPublicConstrainedTypes(codegenContext, shape) serverBuilderGeneratorWithoutPublicConstrainedTypes.render(writer) - writer.implBlock(shape, codegenContext.symbolProvider) { + writer.implBlock(codegenContext.symbolProvider.toSymbol(shape)) { serverBuilderGeneratorWithoutPublicConstrainedTypes.renderConvenienceMethod(this) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 9d49dfb9fe..fec2c6d484 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -12,12 +12,12 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.server.smithy.RustCodegenServerPlugin import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenConfig @@ -124,7 +124,7 @@ fun StructureShape.serverRenderWithModelBuilder(model: Model, symbolProvider: Ru // regardless of the `publicConstrainedTypes` setting. val modelBuilder = ServerBuilderGenerator(serverCodegenContext, this) modelBuilder.render(writer) - writer.implBlock(this, symbolProvider) { + writer.implBlock(symbolProvider.toSymbol(this)) { modelBuilder.renderConvenienceMethod(this) } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt index 24688f3654..009ab2f0a2 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt @@ -8,9 +8,9 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.util.lookup @@ -40,7 +40,7 @@ class ServerBuilderGeneratorTest { StructureGenerator(model, codegenContext.symbolProvider, writer, shape).render(CodegenTarget.SERVER) val builderGenerator = ServerBuilderGenerator(codegenContext, shape) builderGenerator.render(writer) - writer.implBlock(shape, codegenContext.symbolProvider) { + writer.implBlock(codegenContext.symbolProvider.toSymbol(shape)) { builderGenerator.renderConvenienceMethod(this) } writer.compileAndTest( diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamBaseRequirements.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamBaseRequirements.kt index e3ff924db8..25b56033fb 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamBaseRequirements.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamBaseRequirements.kt @@ -14,9 +14,9 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.generators.implBlock import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestModels import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestRequirements import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenConfig @@ -70,14 +70,14 @@ abstract class ServerEventStreamBaseRequirements : EventStreamTestRequirements Date: Mon, 19 Dec 2022 18:04:05 -0800 Subject: [PATCH 04/39] Create structure/builder customization hooks --- .../client/smithy/ClientCodegenVisitor.kt | 16 +++++++- .../protocol/ClientProtocolGenerator.kt | 2 +- .../ClientEventStreamBaseRequirements.kt | 2 +- .../smithy/customize/CoreCodegenDecorator.kt | 33 +++++++++++++++ .../smithy/generators/BuilderGenerator.kt | 27 +++++++++++++ .../smithy/generators/StructureGenerator.kt | 17 ++++++++ .../core/testutil/EventStreamTestTools.kt | 2 +- .../rust/codegen/core/testutil/TestHelpers.kt | 4 +- .../smithy/generators/BuilderGeneratorTest.kt | 22 ++++++---- .../generators/StructureGeneratorTest.kt | 40 +++++++++---------- .../error/ServiceErrorGeneratorTest.kt | 2 +- .../RecursiveShapesIntegrationTest.kt | 2 +- .../PythonServerStructureGenerator.kt | 2 +- .../server/smithy/ServerCodegenVisitor.kt | 8 +++- .../smithy/testutil/ServerTestHelpers.kt | 2 +- .../generators/ServerBuilderGeneratorTest.kt | 8 +++- ...verEventStreamUnmarshallerGeneratorTest.kt | 2 +- 17 files changed, 149 insertions(+), 42 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index a8510c7d2d..81999aefce 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -189,9 +189,21 @@ class ClientCodegenVisitor( override fun structureShape(shape: StructureShape) { logger.fine("generating a structure...") rustCrate.useShapeWriter(shape) { - StructureGenerator(model, symbolProvider, this, shape).render() + StructureGenerator( + model, + symbolProvider, + this, + shape, + codegenDecorator.structureCustomizations(codegenContext, emptyList()), + ).render() if (!shape.hasTrait()) { - val builderGenerator = BuilderGenerator(codegenContext.model, codegenContext.symbolProvider, shape) + val builderGenerator = + BuilderGenerator( + codegenContext.model, + codegenContext.symbolProvider, + shape, + codegenDecorator.builderCustomizations(codegenContext, emptyList()), + ) builderGenerator.render(this) implBlock(symbolProvider.toSymbol(shape)) { builderGenerator.renderConvenienceMethod(this) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt index a28b9e8f0f..aa83e5429b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt @@ -46,7 +46,7 @@ open class ClientProtocolGenerator( customizations: List, ) { val inputShape = operationShape.inputShape(model) - val builderGenerator = BuilderGenerator(model, symbolProvider, operationShape.inputShape(model)) + val builderGenerator = BuilderGenerator(model, symbolProvider, operationShape.inputShape(model), emptyList()) builderGenerator.render(inputWriter) // impl OperationInputShape { ... } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt index 8926d7b30f..84b59bc28a 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt @@ -52,7 +52,7 @@ abstract class ClientEventStreamBaseRequirements : EventStreamTestRequirements { codegenContext: CodegenContext, baseCustomizations: List, ): List = baseCustomizations + + /** + * Hook to customize generated structures. + */ + fun structureCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + + // TODO(https://github.com/awslabs/smithy-rs/issues/1401): Move these customizations into `ClientCodegenDecorator` + /** + * Hook to customize generated builders. + */ + fun builderCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = baseCustomizations } /** @@ -109,6 +128,20 @@ abstract class CombinedCoreCodegenDecorator, + ): List = combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.structureCustomizations(codegenContext, customizations) + } + + override fun builderCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.builderCustomizations(codegenContext, customizations) + } + /** * Combines customizations from multiple ordered codegen decorators. * diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index b612ca19fc..016294270a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -34,6 +34,9 @@ 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.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.canUseDefault +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedSectionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.defaultValue import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.isOptional @@ -50,6 +53,25 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase // TODO(https://github.com/awslabs/smithy-rs/issues/1401) This builder generator is only used by the client. // Move this entire file, and its tests, to `codegen-client`. +/** BuilderGenerator customization sections */ +sealed class BuilderSection(name: String) : Section(name) { + /** Hook to add additional fields to the builder */ + data class AdditionalFields(val shape: StructureShape) : BuilderSection("AdditionalFields") + + /** Hook to add additional methods to the builder */ + data class AdditionalMethods(val shape: StructureShape) : BuilderSection("AdditionalMethods") + + /** Hook to add additional fields to the `build()` method */ + data class AdditionalFieldsInBuild(val shape: StructureShape) : BuilderSection("AdditionalFieldsInBuild") + + /** Hook to add additional fields to the `Debug` impl */ + data class AdditionalDebugFields(val shape: StructureShape, val formatterName: String) : + BuilderSection("AdditionalDebugFields") +} + +/** Customizations for BuilderGenerator */ +abstract class BuilderCustomization : NamedSectionGenerator() + fun builderSymbolFn(symbolProvider: RustSymbolProvider): (StructureShape) -> Symbol = { structureShape -> structureShape.builderSymbol(symbolProvider) } @@ -90,6 +112,7 @@ class BuilderGenerator( private val model: Model, private val symbolProvider: RustSymbolProvider, private val shape: StructureShape, + private val customizations: List, ) { companion object { /** @@ -211,6 +234,7 @@ class BuilderGenerator( val memberSymbol = symbolProvider.toSymbol(member).makeOptional() renderBuilderMember(this, memberName, memberSymbol) } + writeCustomizations(customizations, BuilderSection.AdditionalFields(shape)) } writer.rustBlock("impl $builderName") { @@ -230,6 +254,7 @@ class BuilderGenerator( renderBuilderMemberSetterFn(this, outerType, member, memberName) } + writeCustomizations(customizations, BuilderSection.AdditionalMethods(shape)) renderBuildFn(this) } } @@ -246,6 +271,7 @@ class BuilderGenerator( "formatter.field(${memberName.dq()}, &$fieldValue);", ) } + writeCustomizations(customizations, BuilderSection.AdditionalDebugFields(shape, "formatter")) rust("formatter.finish()") } } @@ -324,6 +350,7 @@ class BuilderGenerator( } } } + writeCustomizations(customizations, BuilderSection.AdditionalFieldsInBuild(shape)) } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index a44a8cac48..7e60cb821c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -25,6 +25,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget 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.customize.NamedSectionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom @@ -33,11 +36,22 @@ import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.redactIfNecessary +/** StructureGenerator customization sections */ +sealed class StructureSection(name: String) : Section(name) { + data class AdditionalMembers(val shape: StructureShape) : StructureSection("AdditionalMembers") + data class AdditionalTraitImpls(val shape: StructureShape, val structName: String) : + StructureSection("AdditionalTraitImpls") +} + +/** Customizations for StructureGenerator */ +abstract class StructureCustomization : NamedSectionGenerator() + open class StructureGenerator( val model: Model, private val symbolProvider: RustSymbolProvider, private val writer: RustWriter, private val shape: StructureShape, + private val customizations: List, ) { private val errorTrait = shape.getTrait() protected val members: List = shape.allMembers.values.toList() @@ -141,12 +155,15 @@ open class StructureGenerator( writer.forEachMember(members) { member, memberName, memberSymbol -> renderStructureMember(writer, member, memberName, memberSymbol) } + writeCustomizations(customizations, StructureSection.AdditionalMembers(shape)) } renderStructureImpl() if (!containerMeta.derives.derives.contains(RuntimeType.Debug)) { renderDebugImpl() } + + writer.writeCustomizations(customizations, StructureSection.AdditionalTraitImpls(shape, name)) } protected fun RustWriter.forEachMember( diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt index 05151b7ce7..bb35605d71 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt @@ -126,7 +126,7 @@ object EventStreamTestTools { requirements.renderOperationError(this, model, symbolProvider, operationSymbol, errors) requirements.renderOperationError(this, model, symbolProvider, symbolProvider.toSymbol(unionShape), errors) for (shape in errors) { - StructureGenerator(model, symbolProvider, this, shape).render(codegenTarget) + StructureGenerator(model, symbolProvider, this, shape, emptyList()).render(codegenTarget) requirements.renderBuilderForShape(this, codegenContext, shape) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt index 9587494b19..11a5525d7d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt @@ -104,8 +104,8 @@ fun StructureShape.renderWithModelBuilder( writer: RustWriter, forWhom: CodegenTarget = CodegenTarget.CLIENT, ) { - StructureGenerator(model, symbolProvider, writer, this).render(forWhom) - val modelBuilder = BuilderGenerator(model, symbolProvider, this) + StructureGenerator(model, symbolProvider, writer, this, emptyList()).render(forWhom) + val modelBuilder = BuilderGenerator(model, symbolProvider, this, emptyList()) modelBuilder.render(writer) writer.implBlock(symbolProvider.toSymbol(this)) { modelBuilder.renderConvenienceMethod(this) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt index 9ba33ed107..537f7b0370 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt @@ -32,9 +32,9 @@ internal class BuilderGeneratorTest { val provider = testSymbolProvider(model) val writer = RustWriter.forModule("model") writer.rust("##![allow(deprecated)]") - val innerGenerator = StructureGenerator(model, provider, writer, inner) - val generator = StructureGenerator(model, provider, writer, struct) - val builderGenerator = BuilderGenerator(model, provider, struct) + val innerGenerator = StructureGenerator(model, provider, writer, inner, emptyList()) + val generator = StructureGenerator(model, provider, writer, struct, emptyList()) + val builderGenerator = BuilderGenerator(model, provider, struct, emptyList()) generator.render() innerGenerator.render() builderGenerator.render(writer) @@ -74,16 +74,22 @@ internal class BuilderGeneratorTest { val writer = RustWriter.forModule("model") writer.rust("##![allow(deprecated)]") val innerGenerator = StructureGenerator( - StructureGeneratorTest.model, provider, writer, + StructureGeneratorTest.model, + provider, + writer, StructureGeneratorTest.inner, + emptyList(), ) val generator = StructureGenerator( - StructureGeneratorTest.model, provider, writer, + StructureGeneratorTest.model, + provider, + writer, StructureGeneratorTest.struct, + emptyList(), ) generator.render() innerGenerator.render() - val builderGenerator = BuilderGenerator(model, provider, struct) + val builderGenerator = BuilderGenerator(model, provider, struct, emptyList()) builderGenerator.render(writer) writer.implBlock(provider.toSymbol(struct)) { builderGenerator.renderConvenienceMethod(this) @@ -101,8 +107,8 @@ internal class BuilderGeneratorTest { fun `builder for a struct with sensitive fields should implement the debug trait as such`() { val provider = testSymbolProvider(model) val writer = RustWriter.forModule("model") - val credsGenerator = StructureGenerator(model, provider, writer, credentials) - val builderGenerator = BuilderGenerator(model, provider, credentials) + val credsGenerator = StructureGenerator(model, provider, writer, credentials, emptyList()) + val builderGenerator = BuilderGenerator(model, provider, credentials, emptyList()) credsGenerator.render() builderGenerator.render(writer) writer.implBlock(provider.toSymbol(credentials)) { diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt index fcf242dcc4..91c15ddddf 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt @@ -82,8 +82,8 @@ class StructureGeneratorTest { val provider = testSymbolProvider(model) val project = TestWorkspace.testProject(provider) project.useShapeWriter(inner) { - StructureGenerator(model, provider, this, inner).render() - StructureGenerator(model, provider, this, struct).render() + StructureGenerator(model, provider, this, inner, emptyList()).render() + StructureGenerator(model, provider, this, struct, emptyList()).render() unitTest( "struct_fields_optional", """ @@ -105,11 +105,11 @@ class StructureGeneratorTest { project.lib { rust("##![allow(deprecated)]") } project.withModule(ModelsModule) { - val innerGenerator = StructureGenerator(model, provider, this, inner) + val innerGenerator = StructureGenerator(model, provider, this, inner, emptyList()) innerGenerator.render() } project.withModule(RustModule.public("structs")) { - val generator = StructureGenerator(model, provider, this, struct) + val generator = StructureGenerator(model, provider, this, struct, emptyList()) generator.render() } // By putting the test in another module, it can't access the struct @@ -132,7 +132,7 @@ class StructureGeneratorTest { fun `generate error structures`() { val provider = testSymbolProvider(model) val writer = RustWriter.forModule("error") - val generator = StructureGenerator(model, provider, writer, error) + val generator = StructureGenerator(model, provider, writer, error, emptyList()) generator.render() writer.compileAndTest( """ @@ -146,7 +146,7 @@ class StructureGeneratorTest { fun `generate a custom debug implementation when the sensitive trait is present`() { val provider = testSymbolProvider(model) val writer = RustWriter.forModule("lib") - val generator = StructureGenerator(model, provider, writer, credentials) + val generator = StructureGenerator(model, provider, writer, credentials, emptyList()) generator.render() writer.unitTest( "sensitive_fields_redacted", @@ -185,8 +185,8 @@ class StructureGeneratorTest { Attribute.Custom("deny(missing_docs)").render(this) } project.withModule(ModelsModule) { - StructureGenerator(model, provider, this, model.lookup("com.test#Inner")).render() - StructureGenerator(model, provider, this, model.lookup("com.test#MyStruct")).render() + StructureGenerator(model, provider, this, model.lookup("com.test#Inner"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("com.test#MyStruct"), emptyList()).render() } project.compileAndTest() @@ -196,7 +196,7 @@ class StructureGeneratorTest { fun `documents are optional in structs`() { val provider = testSymbolProvider(model) val writer = RustWriter.forModule("lib") - StructureGenerator(model, provider, writer, structWithDoc).render() + StructureGenerator(model, provider, writer, structWithDoc, emptyList()).render() writer.compileAndTest( """ @@ -229,10 +229,10 @@ class StructureGeneratorTest { val project = TestWorkspace.testProject(provider) project.lib { rust("##![allow(deprecated)]") } project.withModule(ModelsModule) { - StructureGenerator(model, provider, this, model.lookup("test#Foo")).render() - StructureGenerator(model, provider, this, model.lookup("test#Bar")).render() - StructureGenerator(model, provider, this, model.lookup("test#Baz")).render() - StructureGenerator(model, provider, this, model.lookup("test#Qux")).render() + StructureGenerator(model, provider, this, model.lookup("test#Foo"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("test#Bar"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("test#Baz"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("test#Qux"), emptyList()).render() } // turn on clippy to check the semver-compliant version of `since`. @@ -262,9 +262,9 @@ class StructureGeneratorTest { val project = TestWorkspace.testProject(provider) project.lib { rust("##![allow(deprecated)]") } project.withModule(ModelsModule) { - StructureGenerator(model, provider, this, model.lookup("test#Nested")).render() - StructureGenerator(model, provider, this, model.lookup("test#Foo")).render() - StructureGenerator(model, provider, this, model.lookup("test#Bar")).render() + StructureGenerator(model, provider, this, model.lookup("test#Nested"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("test#Foo"), emptyList()).render() + StructureGenerator(model, provider, this, model.lookup("test#Bar"), emptyList()).render() } project.compileAndTest() @@ -311,8 +311,8 @@ class StructureGeneratorTest { val project = TestWorkspace.testProject(provider) project.useShapeWriter(inner) { - StructureGenerator(testModel, provider, this, testModel.lookup("test#One")).render() - StructureGenerator(testModel, provider, this, testModel.lookup("test#Two")).render() + StructureGenerator(testModel, provider, this, testModel.lookup("test#One"), emptyList()).render() + StructureGenerator(testModel, provider, this, testModel.lookup("test#Two"), emptyList()).render() rustBlock("fn compile_test_one(one: &crate::model::One)") { rust( @@ -369,7 +369,7 @@ class StructureGeneratorTest { val provider = testSymbolProvider(model) RustWriter.forModule("test").let { - StructureGenerator(model, provider, it, struct).render() + StructureGenerator(model, provider, it, struct, emptyList()).render() assertEquals(6, it.toString().split("#[doc(hidden)]").size, "there should be 5 doc-hiddens") } } @@ -385,7 +385,7 @@ class StructureGeneratorTest { val provider = testSymbolProvider(model) RustWriter.forModule("test").let { writer -> - StructureGenerator(model, provider, writer, struct).render() + StructureGenerator(model, provider, writer, struct, emptyList()).render() writer.toString().shouldNotContain("#[doc(hidden)]") } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt index dd8db9cd34..cb5beb44c7 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt @@ -96,7 +96,7 @@ internal class ServiceErrorGeneratorTest { } for (shape in model.structureShapes) { if (shape.id.namespace == "com.example") { - StructureGenerator(model, symbolProvider, this, shape).render(CodegenTarget.CLIENT) + StructureGenerator(model, symbolProvider, this, shape, emptyList()).render(CodegenTarget.CLIENT) } } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt index bea21fd083..5001b9ca9d 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt @@ -47,7 +47,7 @@ class RecursiveShapesIntegrationTest { val writer = RustWriter.forModule("model") val symbolProvider = testSymbolProvider(input) structures.forEach { - StructureGenerator(input, symbolProvider, writer, it).render() + StructureGenerator(input, symbolProvider, writer, it, emptyList()).render() } UnionGenerator(input, symbolProvider, writer, input.lookup("com.example#Atom")).render() writer diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt index 468c1c5313..115a23a8a5 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt @@ -33,7 +33,7 @@ class PythonServerStructureGenerator( private val symbolProvider: RustSymbolProvider, private val writer: RustWriter, private val shape: StructureShape, -) : StructureGenerator(model, symbolProvider, writer, shape) { +) : StructureGenerator(model, symbolProvider, writer, shape, emptyList()) { private val pyo3Symbols = listOf(PythonServerCargoDependency.PyO3.toType()) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index d8aeec76c4..91767eec71 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -246,7 +246,13 @@ open class ServerCodegenVisitor( override fun structureShape(shape: StructureShape) { logger.info("[rust-server-codegen] Generating a structure $shape") rustCrate.useShapeWriter(shape) { - StructureGenerator(model, codegenContext.symbolProvider, this, shape).render(CodegenTarget.SERVER) + StructureGenerator( + model, + codegenContext.symbolProvider, + this, + shape, + codegenDecorator.structureCustomizations(codegenContext, emptyList()), + ).render(CodegenTarget.SERVER) renderStructureShapeBuilder(shape, this) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index fec2c6d484..4452d6d017 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -118,7 +118,7 @@ fun serverTestCodegenContext( * In tests, we frequently need to generate a struct, a builder, and an impl block to access said builder. */ fun StructureShape.serverRenderWithModelBuilder(model: Model, symbolProvider: RustSymbolProvider, writer: RustWriter) { - StructureGenerator(model, symbolProvider, writer, this).render(CodegenTarget.SERVER) + StructureGenerator(model, symbolProvider, writer, this, emptyList()).render(CodegenTarget.SERVER) val serverCodegenContext = serverTestCodegenContext(model) // Note that this always uses `ServerBuilderGenerator` and _not_ `ServerBuilderGeneratorWithoutPublicConstrainedTypes`, // regardless of the `publicConstrainedTypes` setting. diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt index 009ab2f0a2..cc3ba0193e 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt @@ -37,7 +37,13 @@ class ServerBuilderGeneratorTest { val codegenContext = serverTestCodegenContext(model) val writer = RustWriter.forModule("model") val shape = model.lookup("test#Credentials") - StructureGenerator(model, codegenContext.symbolProvider, writer, shape).render(CodegenTarget.SERVER) + StructureGenerator( + model, + codegenContext.symbolProvider, + writer, + shape, + emptyList(), + ).render(CodegenTarget.SERVER) val builderGenerator = ServerBuilderGenerator(codegenContext, shape) builderGenerator.render(writer) writer.implBlock(codegenContext.symbolProvider.toSymbol(shape)) { diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamUnmarshallerGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamUnmarshallerGeneratorTest.kt index 11017835c5..480c3c078d 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamUnmarshallerGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamUnmarshallerGeneratorTest.kt @@ -58,7 +58,7 @@ class ServerEventStreamUnmarshallerGeneratorTest { codegenContext: ServerCodegenContext, shape: StructureShape, ) { - BuilderGenerator(codegenContext.model, codegenContext.symbolProvider, shape).apply { + BuilderGenerator(codegenContext.model, codegenContext.symbolProvider, shape, emptyList()).apply { render(writer) writer.implBlock(codegenContext.symbolProvider.toSymbol(shape)) { renderConvenienceMethod(writer) From 1ce44f8aacae2b3eb7538d47308cd520de238911 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 19 Dec 2022 18:26:46 -0800 Subject: [PATCH 05/39] Customize `_request_id` into AWS outputs --- .../smithy/rustsdk/AwsRequestIdDecorator.kt | 78 +++++++++++++++++++ .../kms/tests/sensitive-it.rs | 7 +- .../smithy/generators/BuilderGenerator.kt | 10 ++- .../smithy/generators/StructureGenerator.kt | 16 +++- 4 files changed, 103 insertions(+), 8 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt index c75fbdd13a..299dce4ea6 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt @@ -17,8 +17,14 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorSection +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait +import software.amazon.smithy.rust.codegen.core.util.hasTrait /** * Customizes response parsing logic to add AWS request IDs to error metadata and outputs @@ -38,6 +44,16 @@ class AwsRequestIdDecorator : ClientCodegenDecorator { baseCustomizations: List, ): List = baseCustomizations + listOf(AwsRequestIdErrorCustomization(codegenContext)) + override fun structureCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + listOf(AwsRequestIdStructureCustomization(codegenContext)) + + override fun builderCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + listOf(AwsRequestIdBuilderCustomization()) + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { rustCrate.withModule(RustModule.Types) { // Re-export RequestId in generated crate @@ -83,6 +99,68 @@ private class AwsRequestIdErrorCustomization(private val codegenContext: ClientC } } +private class AwsRequestIdStructureCustomization( + private val codegenContext: ClientCodegenContext, +) : StructureCustomization() { + override fun section(section: StructureSection): Writable = writable { + if (section.shape.hasTrait()) { + when (section) { + is StructureSection.AdditionalFields -> { + rust("_request_id: Option,") + } + is StructureSection.AdditionalTraitImpls -> { + rustTemplate( + """ + impl #{RequestId} for ${section.structName} { + fn request_id(&self) -> Option<&str> { + self._request_id.as_deref() + } + } + """, + "RequestId" to codegenContext.requestIdTrait(), + ) + } + is StructureSection.AdditionalDebugFields -> { + rust("""${section.formatterName}.field("_request_id", &self._request_id);""") + } + } + } + } +} + +private class AwsRequestIdBuilderCustomization : BuilderCustomization() { + override fun section(section: BuilderSection): Writable = writable { + if (section.shape.hasTrait()) { + when (section) { + is BuilderSection.AdditionalFields -> { + rust("_request_id: Option,") + } + is BuilderSection.AdditionalMethods -> { + rust( + """ + pub(crate) fn _request_id(mut self, request_id: impl Into) -> Self { + self._request_id = Some(request_id.into()); + self + } + + pub(crate) fn _set_request_id(&mut self, request_id: Option) -> &mut Self { + self._request_id = request_id; + self + } + """, + ) + } + is BuilderSection.AdditionalDebugFields -> { + rust("""${section.formatterName}.field("_request_id", &self._request_id);""") + } + is BuilderSection.AdditionalFieldsInBuild -> { + rust("_request_id: self._request_id,") + } + } + } + } +} + private fun ClientCodegenContext.requestIdModule(): RuntimeType = AwsRuntimeType.awsHttp(runtimeConfig).resolve("request_id") diff --git a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs index 5a97651d83..00f3c8d95e 100644 --- a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs +++ b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs @@ -19,12 +19,17 @@ use kms::types::Blob; #[test] fn validate_sensitive_trait() { + let builder = GenerateRandomOutput::builder().plaintext(Blob::new("some output")); + assert_eq!( + format!("{:?}", builder), + "Builder { plaintext: \"*** Sensitive Data Redacted ***\", _request_id: None }" + ); let output = GenerateRandomOutput::builder() .plaintext(Blob::new("some output")) .build(); assert_eq!( format!("{:?}", output), - "GenerateRandomOutput { plaintext: \"*** Sensitive Data Redacted ***\" }" + "GenerateRandomOutput { plaintext: \"*** Sensitive Data Redacted ***\", _request_id: None }" ); } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index 016294270a..58de9a5c2c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -55,17 +55,19 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase /** BuilderGenerator customization sections */ sealed class BuilderSection(name: String) : Section(name) { + abstract val shape: StructureShape + /** Hook to add additional fields to the builder */ - data class AdditionalFields(val shape: StructureShape) : BuilderSection("AdditionalFields") + data class AdditionalFields(override val shape: StructureShape) : BuilderSection("AdditionalFields") /** Hook to add additional methods to the builder */ - data class AdditionalMethods(val shape: StructureShape) : BuilderSection("AdditionalMethods") + data class AdditionalMethods(override val shape: StructureShape) : BuilderSection("AdditionalMethods") /** Hook to add additional fields to the `build()` method */ - data class AdditionalFieldsInBuild(val shape: StructureShape) : BuilderSection("AdditionalFieldsInBuild") + data class AdditionalFieldsInBuild(override val shape: StructureShape) : BuilderSection("AdditionalFieldsInBuild") /** Hook to add additional fields to the `Debug` impl */ - data class AdditionalDebugFields(val shape: StructureShape, val formatterName: String) : + data class AdditionalDebugFields(override val shape: StructureShape, val formatterName: String) : BuilderSection("AdditionalDebugFields") } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index 7e60cb821c..d0b5237742 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -38,8 +38,17 @@ import software.amazon.smithy.rust.codegen.core.util.redactIfNecessary /** StructureGenerator customization sections */ sealed class StructureSection(name: String) : Section(name) { - data class AdditionalMembers(val shape: StructureShape) : StructureSection("AdditionalMembers") - data class AdditionalTraitImpls(val shape: StructureShape, val structName: String) : + abstract val shape: StructureShape + + /** Hook to add additional fields to the structure */ + data class AdditionalFields(override val shape: StructureShape) : StructureSection("AdditionalFields") + + /** Hook to add additional fields to the `Debug` impl */ + data class AdditionalDebugFields(override val shape: StructureShape, val formatterName: String) : + StructureSection("AdditionalDebugFields") + + /** Hook to add additional trait impls to the structure */ + data class AdditionalTraitImpls(override val shape: StructureShape, val structName: String) : StructureSection("AdditionalTraitImpls") } @@ -103,6 +112,7 @@ open class StructureGenerator( "formatter.field(${memberName.dq()}, &$fieldValue);", ) } + writeCustomizations(customizations, StructureSection.AdditionalDebugFields(shape, "formatter")) rust("formatter.finish()") } } @@ -155,7 +165,7 @@ open class StructureGenerator( writer.forEachMember(members) { member, memberName, memberSymbol -> renderStructureMember(writer, member, memberName, memberSymbol) } - writeCustomizations(customizations, StructureSection.AdditionalMembers(shape)) + writeCustomizations(customizations, StructureSection.AdditionalFields(shape)) } renderStructureImpl() From b133cc94238a631e2fae7a55c2362ae351d1bce3 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 21 Dec 2022 15:50:21 -0800 Subject: [PATCH 06/39] Set request ID on outputs --- aws/rust-runtime/aws-http/src/request_id.rs | 6 +++ .../smithy/rustsdk/AwsRequestIdDecorator.kt | 3 ++ .../lambda/tests/request_id.rs | 14 ++++++- .../integration-tests/s3/tests/request_id.rs | 37 ++++++++++++++++++- 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/aws/rust-runtime/aws-http/src/request_id.rs b/aws/rust-runtime/aws-http/src/request_id.rs index c3edd35df4..adf07eab64 100644 --- a/aws/rust-runtime/aws-http/src/request_id.rs +++ b/aws/rust-runtime/aws-http/src/request_id.rs @@ -43,6 +43,12 @@ impl RequestId for operation::Response { } } +impl RequestId for http::Response { + fn request_id(&self) -> Option<&str> { + extract_request_id(self.headers()) + } +} + impl RequestId for Result where O: RequestId, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt index 299dce4ea6..c42b8e7066 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt @@ -73,6 +73,9 @@ private class AwsRequestIdOperationCustomization( "apply_request_id" to codegenContext.requestIdModule().resolve("apply_request_id"), ) } + is OperationSection.MutateOutput -> { + rust("output._set_request_id(#T::request_id(response).map(str::to_string));", codegenContext.requestIdTrait()) + } else -> {} } } diff --git a/aws/sdk/integration-tests/lambda/tests/request_id.rs b/aws/sdk/integration-tests/lambda/tests/request_id.rs index c991b4183a..34fdba0d44 100644 --- a/aws/sdk/integration-tests/lambda/tests/request_id.rs +++ b/aws/sdk/integration-tests/lambda/tests/request_id.rs @@ -20,8 +20,20 @@ fn get_request_id_from_unmodeled_error() { let err = ListFunctions::new() .parse_loaded(&resp.map(Bytes::from)) .expect_err("status was 500, this is an error"); - dbg!(&err); assert!(matches!(err.kind, ListFunctionsErrorKind::Unhandled(_))); assert_eq!(Some("correct-request-id"), err.request_id()); assert_eq!(Some("correct-request-id"), err.meta().request_id()); } + +#[test] +fn get_request_id_from_successful_response() { + let resp = http::Response::builder() + .header("x-amzn-RequestId", "correct-request-id") + .status(200) + .body(r#"{"Functions":[],"NextMarker":null}"#) + .unwrap(); + let output = ListFunctions::new() + .parse_loaded(&resp.map(Bytes::from)) + .expect("valid successful response"); + assert_eq!(Some("correct-request-id"), output.request_id()); +} diff --git a/aws/sdk/integration-tests/s3/tests/request_id.rs b/aws/sdk/integration-tests/s3/tests/request_id.rs index 4ee7b1bcb4..76d88cc506 100644 --- a/aws/sdk/integration-tests/s3/tests/request_id.rs +++ b/aws/sdk/integration-tests/s3/tests/request_id.rs @@ -4,8 +4,10 @@ */ use aws_sdk_s3::error::GetObjectErrorKind; -use aws_sdk_s3::operation::GetObject; +use aws_sdk_s3::operation::{GetObject, ListBuckets}; use aws_sdk_s3::types::RequestId; +use aws_smithy_http::body::SdkBody; +use aws_smithy_http::operation; use aws_smithy_http::response::ParseHttpResponse; use bytes::Bytes; @@ -54,3 +56,36 @@ fn get_request_id_from_unmodeled_error() { assert_eq!(Some("correct-request-id"), err.request_id()); assert_eq!(Some("correct-request-id"), err.meta().request_id()); } + +#[test] +fn get_request_id_from_successful_nonstreaming_response() { + let resp = http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .status(200) + .body( + r#" + + some-idsome-display-name + + "#, + ) + .unwrap(); + let output = ListBuckets::new() + .parse_loaded(&resp.map(Bytes::from)) + .expect("valid successful response"); + assert_eq!(Some("correct-request-id"), output.request_id()); +} + +#[test] +fn get_request_id_from_successful_streaming_response() { + let resp = http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .status(200) + .body(SdkBody::from("some streaming file data")) + .unwrap(); + let mut resp = operation::Response::new(resp); + let output = GetObject::new() + .parse_unloaded(&mut resp) + .expect("valid successful response"); + assert_eq!(Some("correct-request-id"), output.request_id()); +} From d1468c55adcce4674ef712a32a2ae6bdb21b1437 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 21 Dec 2022 17:54:02 -0800 Subject: [PATCH 07/39] Refactor SDK service decorators --- .../smithy/rustsdk/AwsCodegenDecorator.kt | 19 +-- .../{auth => }/DisabledAuthDecorator.kt | 6 +- .../customize/ServiceSpecificDecorator.kt | 121 ++++++++++++++++++ .../apigateway/ApiGatewayDecorator.kt | 12 +- .../rustsdk/customize/ec2/Ec2Decorator.kt | 18 +-- .../customize/glacier/GlacierDecorator.kt | 24 +--- .../customize/route53/Route53Decorator.kt | 17 +-- .../rustsdk/customize/s3/S3Decorator.kt | 40 ++---- .../customize/s3control/S3ControlDecorator.kt | 11 +- .../rustsdk/customize/sts/STSDecorator.kt | 23 ++-- 10 files changed, 173 insertions(+), 118 deletions(-) rename aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/{auth => }/DisabledAuthDecorator.kt (96%) create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt index 6d8e990b10..cae41a45f0 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt @@ -9,10 +9,11 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.DocsRsMe import software.amazon.smithy.rust.codegen.client.smithy.customizations.DocsRsMetadataSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator +import software.amazon.smithy.rustsdk.customize.DisabledAuthDecorator import software.amazon.smithy.rustsdk.customize.apigateway.ApiGatewayDecorator -import software.amazon.smithy.rustsdk.customize.auth.DisabledAuthDecorator import software.amazon.smithy.rustsdk.customize.ec2.Ec2Decorator import software.amazon.smithy.rustsdk.customize.glacier.GlacierDecorator +import software.amazon.smithy.rustsdk.customize.onlyApplyTo import software.amazon.smithy.rustsdk.customize.route53.Route53Decorator import software.amazon.smithy.rustsdk.customize.s3.S3Decorator import software.amazon.smithy.rustsdk.customize.s3control.S3ControlDecorator @@ -38,16 +39,16 @@ val DECORATORS: List = listOf( HttpConnectorDecorator(), AwsEndpointsStdLib(), AwsRequestIdDecorator(), + DisabledAuthDecorator(), // Service specific decorators - ApiGatewayDecorator(), - DisabledAuthDecorator(), - Ec2Decorator(), - GlacierDecorator(), - Route53Decorator(), - S3Decorator(), - S3ControlDecorator(), - STSDecorator(), + ApiGatewayDecorator().onlyApplyTo("com.amazonaws.apigateway#BackplaneControlService"), + Ec2Decorator().onlyApplyTo("com.amazonaws.ec2#AmazonEC2"), + GlacierDecorator().onlyApplyTo("com.amazonaws.glacier#Glacier"), + Route53Decorator().onlyApplyTo("com.amazonaws.route53#AWSDnsV20130401"), + S3Decorator().onlyApplyTo("com.amazonaws.s3#AmazonS3"), + S3ControlDecorator().onlyApplyTo("com.amazonaws.s3control#AWSS3ControlServiceV20180820"), + STSDecorator().onlyApplyTo("com.amazonaws.sts#AWSSecurityTokenServiceV20110615"), // Only build docs-rs for linux to reduce load on docs.rs DocsRsMetadataDecorator(DocsRsMetadataSettings(targets = listOf("x86_64-unknown-linux-gnu"), allFeatures = true)), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/auth/DisabledAuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt similarity index 96% rename from aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/auth/DisabledAuthDecorator.kt rename to aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt index 2c65f95bd3..caf42f7518 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/auth/DisabledAuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rustsdk.customize.auth +package software.amazon.smithy.rustsdk.customize import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape @@ -13,8 +13,6 @@ import software.amazon.smithy.model.traits.AuthTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -private fun String.shapeId() = ShapeId.from(this) - // / STS (and possibly other services) need to have auth manually set to [] class DisabledAuthDecorator : ClientCodegenDecorator { override val name: String = "OptionalAuth" @@ -46,3 +44,5 @@ class DisabledAuthDecorator : ClientCodegenDecorator { } } } + +private fun String.shapeId() = ShapeId.from(this) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt new file mode 100644 index 0000000000..e06e43fd2e --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -0,0 +1,121 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk.customize + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.ToShapeId +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientProtocolMap +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization + +/** Only apply this decorator to the given service ID */ +fun ClientCodegenDecorator.onlyApplyTo(serviceId: String): ServiceSpecificDecorator = + ServiceSpecificDecorator(ShapeId.from(serviceId), this) + +/** + * Delegating decorator that only applies to a configured service ID + */ +class ServiceSpecificDecorator( + /** Service ID this decorator is active for */ + private val appliesToServiceId: ShapeId, + /** Decorator to delegate to */ + private val delegateTo: ClientCodegenDecorator, + /** Decorator name */ + override val name: String = "${appliesToServiceId.namespace}.${appliesToServiceId.name}", + /** Decorator order */ + override val order: Byte = 0, +) : ClientCodegenDecorator { + private fun T.maybeApply(serviceId: ToShapeId, delegatedValue: () -> T): T = + if (appliesToServiceId == serviceId.toShapeId()) { + delegatedValue() + } else { + this + } + + // This kind of decorator gets explicitly added to the root sdk-codegen decorator + override fun classpathDiscoverable(): Boolean = false + + override fun builderCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.builderCustomizations(codegenContext, baseCustomizations) + } + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.configCustomizations(codegenContext, baseCustomizations) + } + + override fun crateManifestCustomizations(codegenContext: ClientCodegenContext): ManifestCustomizations = + emptyMap().maybeApply(codegenContext.serviceShape) { + delegateTo.crateManifestCustomizations(codegenContext) + } + + override fun endpointCustomizations(codegenContext: ClientCodegenContext): List = + emptyList().maybeApply(codegenContext.serviceShape) { + delegateTo.endpointCustomizations(codegenContext) + } + + override fun errorCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.errorCustomizations(codegenContext, baseCustomizations) + } + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + maybeApply(codegenContext.serviceShape) { + delegateTo.extras(codegenContext, rustCrate) + } + } + + override fun libRsCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.libRsCustomizations(codegenContext, baseCustomizations) + } + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.operationCustomizations(codegenContext, operation, baseCustomizations) + } + + override fun protocols(serviceId: ShapeId, currentProtocols: ClientProtocolMap): ClientProtocolMap = + currentProtocols.maybeApply(serviceId) { + delegateTo.protocols(serviceId, currentProtocols) + } + + override fun structureCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.structureCustomizations(codegenContext, baseCustomizations) + } + + override fun transformModel(service: ServiceShape, model: Model): Model = + model.maybeApply(service) { + delegateTo.transformModel(service, model) + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt index 9fc0f3e4c7..5959918ef7 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt @@ -6,34 +6,24 @@ package software.amazon.smithy.rustsdk.customize.apigateway import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection -import software.amazon.smithy.rust.codegen.core.util.letIf class ApiGatewayDecorator : ClientCodegenDecorator { override val name: String = "ApiGateway" override val order: Byte = 0 - private fun applies(codegenContext: CodegenContext) = - codegenContext.serviceShape.id == ShapeId.from("com.amazonaws.apigateway#BackplaneControlService") - override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List { - return baseCustomizations.letIf(applies(codegenContext)) { - it + ApiGatewayAddAcceptHeader() - } - } + ): List = baseCustomizations + ApiGatewayAddAcceptHeader() } class ApiGatewayAddAcceptHeader : OperationCustomization() { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt index ee005da318..e788920e1d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt @@ -7,24 +7,14 @@ package software.amazon.smithy.rustsdk.customize.ec2 import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.core.util.letIf class Ec2Decorator : ClientCodegenDecorator { override val name: String = "Ec2" override val order: Byte = 0 - private val ec2 = ShapeId.from("com.amazonaws.ec2#AmazonEC2") - private fun applies(serviceShape: ServiceShape) = - serviceShape.id == ec2 - - override fun transformModel(service: ServiceShape, model: Model): Model { - // EC2 incorrectly models primitive shapes as unboxed when they actually - // need to be boxed for the API to work properly - return model.letIf( - applies(service), - EC2MakePrimitivesOptional::processModel, - ) - } + // EC2 incorrectly models primitive shapes as unboxed when they actually + // need to be boxed for the API to work properly + override fun transformModel(service: ServiceShape, model: Model): Model = + EC2MakePrimitivesOptional.processModel(model) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt index 7bfc3c4e42..5ba71e2f20 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt @@ -6,35 +6,21 @@ package software.amazon.smithy.rustsdk.customize.glacier import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -val Glacier: ShapeId = ShapeId.from("com.amazonaws.glacier#Glacier") - class GlacierDecorator : ClientCodegenDecorator { override val name: String = "Glacier" override val order: Byte = 0 - private fun applies(codegenContext: CodegenContext) = codegenContext.serviceShape.id == Glacier - override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List { - val extras = if (applies(codegenContext)) { - val apiVersion = codegenContext.serviceShape.version - listOfNotNull( - ApiVersionHeader(apiVersion), - TreeHashHeader.forOperation(operation, codegenContext.runtimeConfig), - AccountIdAutofill.forOperation(operation, codegenContext.model), - ) - } else { - emptyList() - } - return baseCustomizations + extras - } + ): List = baseCustomizations + listOfNotNull( + ApiVersionHeader(codegenContext.serviceShape.version), + TreeHashHeader.forOperation(operation, codegenContext.runtimeConfig), + AccountIdAutofill.forOperation(operation, codegenContext.model), + ) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt index e1adcf46af..c8b8cb9813 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt @@ -26,26 +26,19 @@ import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rustsdk.InlineAwsDependency import java.util.logging.Logger -val Route53: ShapeId = ShapeId.from("com.amazonaws.route53#AWSDnsV20130401") - class Route53Decorator : ClientCodegenDecorator { override val name: String = "Route53" override val order: Byte = 0 private val logger: Logger = Logger.getLogger(javaClass.name) private val resourceShapes = setOf(ShapeId.from("com.amazonaws.route53#ResourceId"), ShapeId.from("com.amazonaws.route53#ChangeId")) - private fun applies(service: ServiceShape) = service.id == Route53 - - override fun transformModel(service: ServiceShape, model: Model): Model { - return model.letIf(applies(service)) { - ModelTransformer.create().mapShapes(model) { shape -> - shape.letIf(isResourceId(shape)) { - logger.info("Adding TrimResourceId trait to $shape") - (shape as MemberShape).toBuilder().addTrait(TrimResourceId()).build() - } + override fun transformModel(service: ServiceShape, model: Model): Model = + ModelTransformer.create().mapShapes(model) { shape -> + shape.letIf(isResourceId(shape)) { + logger.info("Adding TrimResourceId trait to $shape") + (shape as MemberShape).toBuilder().addTrait(TrimResourceId()).build() } } - } override fun operationCustomizations( codegenContext: ClientCodegenContext, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index 24e29e7767..54833071ba 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -48,45 +48,33 @@ class S3Decorator : ClientCodegenDecorator { ShapeId.from("com.amazonaws.s3#GetObjectAttributesOutput"), ) - private fun applies(serviceId: ShapeId) = - serviceId == ShapeId.from("com.amazonaws.s3#AmazonS3") - override fun protocols( serviceId: ShapeId, currentProtocols: ProtocolMap, - ): ProtocolMap = - currentProtocols.letIf(applies(serviceId)) { - it + mapOf( - RestXmlTrait.ID to ClientRestXmlFactory { protocolConfig -> - S3ProtocolOverride(protocolConfig) - }, - ) - } + ): ProtocolMap = currentProtocols + mapOf( + RestXmlTrait.ID to ClientRestXmlFactory { protocolConfig -> + S3ProtocolOverride(protocolConfig) + }, + ) - override fun transformModel(service: ServiceShape, model: Model): Model { - return model.letIf(applies(service.id)) { - ModelTransformer.create().mapShapes(model) { shape -> - shape.letIf(isInInvalidXmlRootAllowList(shape)) { - logger.info("Adding AllowInvalidXmlRoot trait to $it") - (it as StructureShape).toBuilder().addTrait(AllowInvalidXmlRoot()).build() - } - }.let(StripBucketFromHttpPath()::transform) - } - } + override fun transformModel(service: ServiceShape, model: Model): Model = + ModelTransformer.create().mapShapes(model) { shape -> + shape.letIf(isInInvalidXmlRootAllowList(shape)) { + logger.info("Adding AllowInvalidXmlRoot trait to $it") + (it as StructureShape).toBuilder().addTrait(AllowInvalidXmlRoot()).build() + } + }.let(StripBucketFromHttpPath()::transform) override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = - baseCustomizations.letIf(applies(codegenContext.serviceShape.id)) { it + listOf(S3ParseExtendedRequestId()) } + ): List = baseCustomizations + listOf(S3ParseExtendedRequestId()) override fun libRsCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, - ): List = baseCustomizations.letIf(applies(codegenContext.serviceShape.id)) { - it + S3PubUse() - } + ): List = baseCustomizations + S3PubUse() private fun isInInvalidXmlRootAllowList(shape: Shape): Boolean { return shape.isStructureShape && invalidXmlRootAllowList.contains(shape.id) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt index 1266d080a3..e98902e1d7 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rustsdk.customize.s3control import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.EndpointTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator @@ -15,18 +14,12 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen class S3ControlDecorator : ClientCodegenDecorator { override val name: String = "S3Control" override val order: Byte = 0 - private fun applies(service: ServiceShape) = - service.id == ShapeId.from("com.amazonaws.s3control#AWSS3ControlServiceV20180820") - override fun transformModel(service: ServiceShape, model: Model): Model { - if (!applies(service)) { - return model - } - return ModelTransformer.create() + override fun transformModel(service: ServiceShape, model: Model): Model = + ModelTransformer.create() .removeTraitsIf(model) { _, trait -> trait is EndpointTrait && trait.hostPrefix.labels.any { it.isLabel && it.content == "AccountId" } } - } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt index 75d0555c8f..a332dd3035 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rustsdk.customize.sts import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.RetryableTrait @@ -22,24 +21,18 @@ class STSDecorator : ClientCodegenDecorator { override val order: Byte = 0 private val logger: Logger = Logger.getLogger(javaClass.name) - private fun applies(serviceId: ShapeId) = - serviceId == ShapeId.from("com.amazonaws.sts#AWSSecurityTokenServiceV20110615") - private fun isIdpCommunicationError(shape: Shape): Boolean = shape is StructureShape && shape.hasTrait() && shape.id.namespace == "com.amazonaws.sts" && shape.id.name == "IDPCommunicationErrorException" - override fun transformModel(service: ServiceShape, model: Model): Model { - return model.letIf(applies(service.id)) { - ModelTransformer.create().mapShapes(model) { shape -> - shape.letIf(isIdpCommunicationError(shape)) { - logger.info("Adding @retryable trait to $shape and setting its error type to 'server'") - (shape as StructureShape).toBuilder() - .removeTrait(ErrorTrait.ID) - .addTrait(ErrorTrait("server")) - .addTrait(RetryableTrait.builder().build()).build() - } + override fun transformModel(service: ServiceShape, model: Model): Model = + ModelTransformer.create().mapShapes(model) { shape -> + shape.letIf(isIdpCommunicationError(shape)) { + logger.info("Adding @retryable trait to $shape and setting its error type to 'server'") + (shape as StructureShape).toBuilder() + .removeTrait(ErrorTrait.ID) + .addTrait(ErrorTrait("server")) + .addTrait(RetryableTrait.builder().build()).build() } } - } } From d3d201dae35f5b875eef65842515a160dae1d47a Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 21 Dec 2022 16:48:28 -0800 Subject: [PATCH 08/39] Refactor S3's extended request ID implementation --- aws/rust-runtime/aws-http/src/request_id.rs | 3 +- aws/rust-runtime/aws-inlineable/src/lib.rs | 4 +- .../aws-inlineable/src/s3_errors.rs | 70 ------- .../aws-inlineable/src/s3_request_id.rs | 169 +++++++++++++++++ .../smithy/rustsdk/AwsCodegenDecorator.kt | 53 +++--- .../smithy/rustsdk/AwsRequestIdDecorator.kt | 159 +--------------- .../amazon/smithy/rustsdk/AwsRuntimeType.kt | 1 - .../smithy/rustsdk/BaseRequestIdDecorator.kt | 176 ++++++++++++++++++ .../customize/ServiceSpecificDecorator.kt | 8 +- .../rustsdk/customize/s3/S3Decorator.kt | 49 ----- .../s3/S3ExtendedRequestIdDecorator.kt | 28 +++ .../s3/tests/custom-error-deserializer.rs | 39 ---- .../integration-tests/s3/tests/request_id.rs | 30 ++- 13 files changed, 451 insertions(+), 338 deletions(-) delete mode 100644 aws/rust-runtime/aws-inlineable/src/s3_errors.rs create mode 100644 aws/rust-runtime/aws-inlineable/src/s3_request_id.rs create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3ExtendedRequestIdDecorator.kt delete mode 100644 aws/sdk/integration-tests/s3/tests/custom-error-deserializer.rs diff --git a/aws/rust-runtime/aws-http/src/request_id.rs b/aws/rust-runtime/aws-http/src/request_id.rs index adf07eab64..f9e9b89c68 100644 --- a/aws/rust-runtime/aws-http/src/request_id.rs +++ b/aws/rust-runtime/aws-http/src/request_id.rs @@ -80,8 +80,7 @@ fn extract_request_id(headers: &HeaderMap) -> Option<&str> { headers .get("x-amzn-requestid") .or_else(|| headers.get("x-amz-request-id")) - .map(|value| std::str::from_utf8(value.as_bytes()).ok()) - .flatten() + .and_then(|value| value.to_str().ok()) } #[cfg(test)] diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index b4e00994d4..9361f3c304 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -24,8 +24,8 @@ pub mod no_credentials; /// Support types required for adding presigning to an operation in a generated service. pub mod presigning; -/// Special logic for handling S3's error responses. -pub mod s3_errors; +/// Special logic for extracting request IDs from S3's responses. +pub mod s3_request_id; /// Glacier-specific checksumming behavior pub mod glacier_checksums; diff --git a/aws/rust-runtime/aws-inlineable/src/s3_errors.rs b/aws/rust-runtime/aws-inlineable/src/s3_errors.rs deleted file mode 100644 index 58f424d5dc..0000000000 --- a/aws/rust-runtime/aws-inlineable/src/s3_errors.rs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use http::{HeaderMap, HeaderValue}; - -const EXTENDED_REQUEST_ID: &str = "s3_extended_request_id"; - -/// S3-specific service error additions. -pub trait ErrorExt { - /// Returns the S3 Extended Request ID necessary when contacting AWS Support. - /// Read more at . - fn extended_request_id(&self) -> Option<&str>; -} - -impl ErrorExt for aws_smithy_types::Error { - fn extended_request_id(&self) -> Option<&str> { - self.extra(EXTENDED_REQUEST_ID) - } -} - -/// Parses the S3 Extended Request ID out of S3 error response headers. -pub fn apply_extended_error( - builder: aws_smithy_types::error::Builder, - headers: &HeaderMap, -) -> aws_smithy_types::error::Builder { - let host_id = headers - .get("x-amz-id-2") - .and_then(|header_value| header_value.to_str().ok()); - if let Some(host_id) = host_id { - builder.custom(EXTENDED_REQUEST_ID, host_id) - } else { - builder - } -} - -#[cfg(test)] -mod test { - use crate::s3_errors::{apply_extended_error, ErrorExt}; - - #[test] - fn add_error_fields() { - let resp = http::Response::builder() - .header( - "x-amz-id-2", - "eftixk72aD6Ap51TnqcoF8eFidJG9Z/2mkiDFu8yU9AS1ed4OpIszj7UDNEHGran", - ) - .status(400) - .body("") - .unwrap(); - let mut builder = aws_smithy_types::Error::builder().message("123"); - builder = apply_extended_error(builder, resp.headers()); - assert_eq!( - builder - .build() - .extended_request_id() - .expect("extended request id should be set"), - "eftixk72aD6Ap51TnqcoF8eFidJG9Z/2mkiDFu8yU9AS1ed4OpIszj7UDNEHGran" - ); - } - - #[test] - fn handle_missing_header() { - let resp = http::Response::builder().status(400).body("").unwrap(); - let mut builder = aws_smithy_types::Error::builder().message("123"); - builder = apply_extended_error(builder, resp.headers()); - assert_eq!(builder.build().extended_request_id(), None); - } -} diff --git a/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs new file mode 100644 index 0000000000..d39611cdc1 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs @@ -0,0 +1,169 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_client::SdkError; +use aws_smithy_http::http::HttpHeaders; +use aws_smithy_http::operation; +use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; +use http::{HeaderMap, HeaderValue}; + +const EXTENDED_REQUEST_ID: &str = "s3_extended_request_id"; + +/// Trait to retrieve the S3-specific extended request ID +/// +/// Read more at . +pub trait RequestIdExt { + /// Returns the S3 Extended Request ID necessary when contacting AWS Support. + fn extended_request_id(&self) -> Option<&str>; +} + +impl RequestIdExt for SdkError +where + R: HttpHeaders, +{ + fn extended_request_id(&self) -> Option<&str> { + match self { + Self::ResponseError(err) => extract_extended_request_id(err.raw().http_headers()), + Self::ServiceError(err) => extract_extended_request_id(err.raw().http_headers()), + _ => None, + } + } +} + +impl RequestIdExt for GenericError { + fn extended_request_id(&self) -> Option<&str> { + self.extra(EXTENDED_REQUEST_ID) + } +} + +impl RequestIdExt for operation::Response { + fn extended_request_id(&self) -> Option<&str> { + extract_extended_request_id(self.http().headers()) + } +} + +impl RequestIdExt for http::Response { + fn extended_request_id(&self) -> Option<&str> { + extract_extended_request_id(self.headers()) + } +} + +impl RequestIdExt for Result +where + O: RequestIdExt, + E: RequestIdExt, +{ + fn extended_request_id(&self) -> Option<&str> { + match self { + Ok(ok) => ok.extended_request_id(), + Err(err) => err.extended_request_id(), + } + } +} + +/// Applies the extended request ID to a generic error builder +#[doc(hidden)] +pub fn apply_extended_request_id( + builder: GenericErrorBuilder, + headers: &HeaderMap, +) -> GenericErrorBuilder { + if let Some(extended_request_id) = extract_extended_request_id(headers) { + builder.custom(EXTENDED_REQUEST_ID, extended_request_id) + } else { + builder + } +} + +/// Extracts the S3 Extended Request ID from HTTP response headers +fn extract_extended_request_id(headers: &HeaderMap) -> Option<&str> { + headers + .get("x-amz-id-2") + .and_then(|value| value.to_str().ok()) +} + +#[cfg(test)] +mod test { + use super::*; + use aws_smithy_client::SdkError; + use aws_smithy_http::body::SdkBody; + use http::Response; + + #[test] + fn handle_missing_header() { + let resp = http::Response::builder().status(400).body("").unwrap(); + let mut builder = aws_smithy_types::Error::builder().message("123"); + builder = apply_extended_request_id(builder, resp.headers()); + assert_eq!(builder.build().extended_request_id(), None); + } + + #[test] + fn test_extended_request_id_sdk_error() { + let without_extended_request_id = + || operation::Response::new(Response::builder().body(SdkBody::empty()).unwrap()); + let with_extended_request_id = || { + operation::Response::new( + Response::builder() + .header("x-amz-id-2", HeaderValue::from_static("some-request-id")) + .body(SdkBody::empty()) + .unwrap(), + ) + }; + assert_eq!( + None, + SdkError::<(), _>::response_error("test", without_extended_request_id()) + .extended_request_id() + ); + assert_eq!( + Some("some-request-id"), + SdkError::<(), _>::response_error("test", with_extended_request_id()) + .extended_request_id() + ); + assert_eq!( + None, + SdkError::service_error((), without_extended_request_id()).extended_request_id() + ); + assert_eq!( + Some("some-request-id"), + SdkError::service_error((), with_extended_request_id()).extended_request_id() + ); + } + + #[test] + fn test_extract_extended_request_id() { + let mut headers = HeaderMap::new(); + assert_eq!(None, extract_extended_request_id(&headers)); + + headers.append("x-amz-id-2", HeaderValue::from_static("some-request-id")); + assert_eq!( + Some("some-request-id"), + extract_extended_request_id(&headers) + ); + } + + #[test] + fn test_apply_extended_request_id() { + let mut headers = HeaderMap::new(); + assert_eq!( + GenericError::builder().build(), + apply_extended_request_id(GenericError::builder(), &headers).build(), + ); + + headers.append("x-amz-id-2", HeaderValue::from_static("some-request-id")); + assert_eq!( + GenericError::builder() + .custom(EXTENDED_REQUEST_ID, "some-request-id") + .build(), + apply_extended_request_id(GenericError::builder(), &headers).build(), + ); + } + + #[test] + fn test_generic_error_extended_request_id_impl() { + let err = GenericError::builder() + .custom(EXTENDED_REQUEST_ID, "some-request-id") + .build(); + assert_eq!(Some("some-request-id"), err.extended_request_id()); + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt index cae41a45f0..2ab21dabe0 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCodegenDecorator.kt @@ -11,48 +11,57 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator import software.amazon.smithy.rustsdk.customize.DisabledAuthDecorator import software.amazon.smithy.rustsdk.customize.apigateway.ApiGatewayDecorator +import software.amazon.smithy.rustsdk.customize.applyDecorators import software.amazon.smithy.rustsdk.customize.ec2.Ec2Decorator import software.amazon.smithy.rustsdk.customize.glacier.GlacierDecorator import software.amazon.smithy.rustsdk.customize.onlyApplyTo import software.amazon.smithy.rustsdk.customize.route53.Route53Decorator import software.amazon.smithy.rustsdk.customize.s3.S3Decorator +import software.amazon.smithy.rustsdk.customize.s3.S3ExtendedRequestIdDecorator import software.amazon.smithy.rustsdk.customize.s3control.S3ControlDecorator import software.amazon.smithy.rustsdk.customize.sts.STSDecorator val DECORATORS: List = listOf( // General AWS Decorators - CredentialsProviderDecorator(), - RegionDecorator(), - AwsEndpointDecorator(), - UserAgentDecorator(), - SigV4SigningDecorator(), - HttpRequestChecksumDecorator(), - HttpResponseChecksumDecorator(), - RetryClassifierDecorator(), - IntegrationTestDecorator(), - AwsFluentClientDecorator(), - CrateLicenseDecorator(), - SdkConfigDecorator(), - ServiceConfigDecorator(), - AwsPresigningDecorator(), - AwsReadmeDecorator(), - HttpConnectorDecorator(), - AwsEndpointsStdLib(), - AwsRequestIdDecorator(), - DisabledAuthDecorator(), + listOf( + CredentialsProviderDecorator(), + RegionDecorator(), + AwsEndpointDecorator(), + UserAgentDecorator(), + SigV4SigningDecorator(), + HttpRequestChecksumDecorator(), + HttpResponseChecksumDecorator(), + RetryClassifierDecorator(), + IntegrationTestDecorator(), + AwsFluentClientDecorator(), + CrateLicenseDecorator(), + SdkConfigDecorator(), + ServiceConfigDecorator(), + AwsPresigningDecorator(), + AwsReadmeDecorator(), + HttpConnectorDecorator(), + AwsEndpointsStdLib(), + AwsRequestIdDecorator(), + DisabledAuthDecorator(), + ), // Service specific decorators ApiGatewayDecorator().onlyApplyTo("com.amazonaws.apigateway#BackplaneControlService"), Ec2Decorator().onlyApplyTo("com.amazonaws.ec2#AmazonEC2"), GlacierDecorator().onlyApplyTo("com.amazonaws.glacier#Glacier"), Route53Decorator().onlyApplyTo("com.amazonaws.route53#AWSDnsV20130401"), - S3Decorator().onlyApplyTo("com.amazonaws.s3#AmazonS3"), + "com.amazonaws.s3#AmazonS3".applyDecorators( + S3Decorator(), + S3ExtendedRequestIdDecorator(), + ), S3ControlDecorator().onlyApplyTo("com.amazonaws.s3control#AWSS3ControlServiceV20180820"), STSDecorator().onlyApplyTo("com.amazonaws.sts#AWSSecurityTokenServiceV20110615"), // Only build docs-rs for linux to reduce load on docs.rs - DocsRsMetadataDecorator(DocsRsMetadataSettings(targets = listOf("x86_64-unknown-linux-gnu"), allFeatures = true)), -) + listOf( + DocsRsMetadataDecorator(DocsRsMetadataSettings(targets = listOf("x86_64-unknown-linux-gnu"), allFeatures = true)), + ), +).flatten() class AwsCodegenDecorator : CombinedClientCodegenDecorator(DECORATORS) { override val name: String = "AwsSdkCodegenDecorator" diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt index c42b8e7066..0b496ce2c9 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRequestIdDecorator.kt @@ -5,166 +5,25 @@ package software.amazon.smithy.rustsdk -import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule -import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection -import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorSection -import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait -import software.amazon.smithy.rust.codegen.core.util.hasTrait /** * Customizes response parsing logic to add AWS request IDs to error metadata and outputs */ -class AwsRequestIdDecorator : ClientCodegenDecorator { +class AwsRequestIdDecorator : BaseRequestIdDecorator() { override val name: String = "AwsRequestIdDecorator" override val order: Byte = 0 - override fun operationCustomizations( - codegenContext: ClientCodegenContext, - operation: OperationShape, - baseCustomizations: List, - ): List = baseCustomizations + listOf(AwsRequestIdOperationCustomization(codegenContext)) + override val fieldName: String = "request_id" + override val accessorFunctionName: String = "request_id" - override fun errorCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List = baseCustomizations + listOf(AwsRequestIdErrorCustomization(codegenContext)) + private fun requestIdModule(codegenContext: ClientCodegenContext): RuntimeType = + AwsRuntimeType.awsHttp(codegenContext.runtimeConfig).resolve("request_id") - override fun structureCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List = baseCustomizations + listOf(AwsRequestIdStructureCustomization(codegenContext)) + override fun accessorTrait(codegenContext: ClientCodegenContext): RuntimeType = + requestIdModule(codegenContext).resolve("RequestId") - override fun builderCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List = baseCustomizations + listOf(AwsRequestIdBuilderCustomization()) - - override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { - rustCrate.withModule(RustModule.Types) { - // Re-export RequestId in generated crate - rust("pub use #T;", codegenContext.requestIdTrait()) - } - } -} - -private class AwsRequestIdOperationCustomization( - private val codegenContext: ClientCodegenContext, -) : OperationCustomization() { - override fun section(section: OperationSection): Writable = writable { - when (section) { - is OperationSection.PopulateGenericErrorExtras -> { - rustTemplate( - "${section.builderName} = #{apply_request_id}(${section.builderName}, ${section.responseName}.headers());", - "apply_request_id" to codegenContext.requestIdModule().resolve("apply_request_id"), - ) - } - is OperationSection.MutateOutput -> { - rust("output._set_request_id(#T::request_id(response).map(str::to_string));", codegenContext.requestIdTrait()) - } - else -> {} - } - } -} - -private class AwsRequestIdErrorCustomization(private val codegenContext: ClientCodegenContext) : ErrorCustomization() { - override fun section(section: ErrorSection): Writable = writable { - when (section) { - is ErrorSection.OperationErrorAdditionalTraitImpls -> { - rustTemplate( - """ - impl #{RequestId} for #{error} { - fn request_id(&self) -> Option<&str> { - self.meta.request_id() - } - } - """, - "RequestId" to codegenContext.requestIdTrait(), - "error" to section.errorType, - ) - } - else -> {} - } - } -} - -private class AwsRequestIdStructureCustomization( - private val codegenContext: ClientCodegenContext, -) : StructureCustomization() { - override fun section(section: StructureSection): Writable = writable { - if (section.shape.hasTrait()) { - when (section) { - is StructureSection.AdditionalFields -> { - rust("_request_id: Option,") - } - is StructureSection.AdditionalTraitImpls -> { - rustTemplate( - """ - impl #{RequestId} for ${section.structName} { - fn request_id(&self) -> Option<&str> { - self._request_id.as_deref() - } - } - """, - "RequestId" to codegenContext.requestIdTrait(), - ) - } - is StructureSection.AdditionalDebugFields -> { - rust("""${section.formatterName}.field("_request_id", &self._request_id);""") - } - } - } - } -} - -private class AwsRequestIdBuilderCustomization : BuilderCustomization() { - override fun section(section: BuilderSection): Writable = writable { - if (section.shape.hasTrait()) { - when (section) { - is BuilderSection.AdditionalFields -> { - rust("_request_id: Option,") - } - is BuilderSection.AdditionalMethods -> { - rust( - """ - pub(crate) fn _request_id(mut self, request_id: impl Into) -> Self { - self._request_id = Some(request_id.into()); - self - } - - pub(crate) fn _set_request_id(&mut self, request_id: Option) -> &mut Self { - self._request_id = request_id; - self - } - """, - ) - } - is BuilderSection.AdditionalDebugFields -> { - rust("""${section.formatterName}.field("_request_id", &self._request_id);""") - } - is BuilderSection.AdditionalFieldsInBuild -> { - rust("_request_id: self._request_id,") - } - } - } - } + override fun applyToError(codegenContext: ClientCodegenContext): RuntimeType = + requestIdModule(codegenContext).resolve("apply_request_id") } - -private fun ClientCodegenContext.requestIdModule(): RuntimeType = - AwsRuntimeType.awsHttp(runtimeConfig).resolve("request_id") - -private fun ClientCodegenContext.requestIdTrait(): RuntimeType = requestIdModule().resolve("RequestId") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt index c0a92d0970..378a9515d1 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt @@ -41,7 +41,6 @@ fun RuntimeConfig.awsRoot(): RuntimeCrateLocation { } object AwsRuntimeType { - val S3Errors by lazy { RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("s3_errors")) } val Presigning by lazy { RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning", visibility = Visibility.PUBLIC)) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt new file mode 100644 index 0000000000..49aa477010 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt @@ -0,0 +1,176 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorSection +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait +import software.amazon.smithy.rust.codegen.core.util.hasTrait + +/** + * Base customization for adding a request ID (or extended request ID) to outputs and errors. + */ +abstract class BaseRequestIdDecorator : ClientCodegenDecorator { + abstract val accessorFunctionName: String + abstract val fieldName: String + abstract fun accessorTrait(codegenContext: ClientCodegenContext): RuntimeType + abstract fun applyToError(codegenContext: ClientCodegenContext): RuntimeType + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = baseCustomizations + listOf(RequestIdOperationCustomization(codegenContext)) + + override fun errorCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations + listOf(RequestIdErrorCustomization(codegenContext)) + + override fun structureCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + listOf(RequestIdStructureCustomization(codegenContext)) + + override fun builderCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + listOf(RequestIdBuilderCustomization()) + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + rustCrate.withModule(RustModule.Types) { + // Re-export RequestId in generated crate + rust("pub use #T;", accessorTrait(codegenContext)) + } + } + + private inner class RequestIdOperationCustomization(private val codegenContext: ClientCodegenContext) : + OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + when (section) { + is OperationSection.PopulateGenericErrorExtras -> { + rustTemplate( + "${section.builderName} = #{apply_to_error}(${section.builderName}, ${section.responseName}.headers());", + "apply_to_error" to applyToError(codegenContext), + ) + } + is OperationSection.MutateOutput -> { + rust( + "output._set_$fieldName(#T::$accessorFunctionName(response).map(str::to_string));", + accessorTrait(codegenContext), + ) + } + else -> {} + } + } + } + + private inner class RequestIdErrorCustomization(private val codegenContext: ClientCodegenContext) : + ErrorCustomization() { + override fun section(section: ErrorSection): Writable = writable { + when (section) { + is ErrorSection.OperationErrorAdditionalTraitImpls -> { + rustTemplate( + """ + impl #{AccessorTrait} for #{error} { + fn $accessorFunctionName(&self) -> Option<&str> { + self.meta.$accessorFunctionName() + } + } + """, + "AccessorTrait" to accessorTrait(codegenContext), + "error" to section.errorType, + ) + } + + else -> {} + } + } + } + + private inner class RequestIdStructureCustomization(private val codegenContext: ClientCodegenContext) : + StructureCustomization() { + override fun section(section: StructureSection): Writable = writable { + if (section.shape.hasTrait()) { + when (section) { + is StructureSection.AdditionalFields -> { + rust("_$fieldName: Option,") + } + + is StructureSection.AdditionalTraitImpls -> { + rustTemplate( + """ + impl #{AccessorTrait} for ${section.structName} { + fn $accessorFunctionName(&self) -> Option<&str> { + self._$fieldName.as_deref() + } + } + """, + "AccessorTrait" to accessorTrait(codegenContext), + ) + } + + is StructureSection.AdditionalDebugFields -> { + rust("""${section.formatterName}.field("_$fieldName", &self._$fieldName);""") + } + } + } + } + } + + private inner class RequestIdBuilderCustomization : BuilderCustomization() { + override fun section(section: BuilderSection): Writable = writable { + if (section.shape.hasTrait()) { + when (section) { + is BuilderSection.AdditionalFields -> { + rust("_$fieldName: Option,") + } + + is BuilderSection.AdditionalMethods -> { + rust( + """ + pub(crate) fn _$fieldName(mut self, $fieldName: impl Into) -> Self { + self._$fieldName = Some($fieldName.into()); + self + } + + pub(crate) fn _set_$fieldName(&mut self, $fieldName: Option) -> &mut Self { + self._$fieldName = $fieldName; + self + } + """, + ) + } + + is BuilderSection.AdditionalDebugFields -> { + rust("""${section.formatterName}.field("_$fieldName", &self._$fieldName);""") + } + + is BuilderSection.AdditionalFieldsInBuild -> { + rust("_$fieldName: self._$fieldName,") + } + } + } + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index e06e43fd2e..8490265ed7 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -24,8 +24,12 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCusto import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization /** Only apply this decorator to the given service ID */ -fun ClientCodegenDecorator.onlyApplyTo(serviceId: String): ServiceSpecificDecorator = - ServiceSpecificDecorator(ShapeId.from(serviceId), this) +fun ClientCodegenDecorator.onlyApplyTo(serviceId: String): List = + listOf(ServiceSpecificDecorator(ShapeId.from(serviceId), this)) + +/** Apply the given decorators only to this service ID */ +fun String.applyDecorators(vararg decorators: ClientCodegenDecorator): List = + decorators.map { it.onlyApplyTo(this) }.flatten() /** * Delegating decorator that only applies to a configured service ID diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index 54833071ba..4834bf1401 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -18,22 +18,14 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientRestXmlFactory import software.amazon.smithy.rust.codegen.core.rustlang.RustModule -import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.rust 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.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection -import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestXml import software.amazon.smithy.rust.codegen.core.smithy.traits.AllowInvalidXmlRoot import software.amazon.smithy.rust.codegen.core.util.letIf -import software.amazon.smithy.rustsdk.AwsRuntimeType import java.util.logging.Logger /** @@ -65,17 +57,6 @@ class S3Decorator : ClientCodegenDecorator { } }.let(StripBucketFromHttpPath()::transform) - override fun operationCustomizations( - codegenContext: ClientCodegenContext, - operation: OperationShape, - baseCustomizations: List, - ): List = baseCustomizations + listOf(S3ParseExtendedRequestId()) - - override fun libRsCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List = baseCustomizations + S3PubUse() - private fun isInInvalidXmlRootAllowList(shape: Shape): Boolean { return shape.isStructureShape && invalidXmlRootAllowList.contains(shape.id) } @@ -119,33 +100,3 @@ class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContex } } } - -/** - * Customizes error parsing logic to add S3's extended request ID to the `aws_smithy_types::error::Error`'s extras. - */ -class S3ParseExtendedRequestId() : OperationCustomization() { - override fun section(section: OperationSection): Writable = writable { - when (section) { - is OperationSection.PopulateGenericErrorExtras -> { - rust( - "${section.builderName} = #T::apply_extended_error(${section.builderName}, ${section.responseName}.headers());", - AwsRuntimeType.S3Errors, - ) - } - else -> {} - } - } -} - -class S3PubUse : LibRsCustomization() { - override fun section(section: LibRsSection): Writable = when (section) { - is LibRsSection.Body -> writable { - rust( - "pub use #T::ErrorExt;", - AwsRuntimeType.S3Errors, - ) - } - - else -> emptySection - } -} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3ExtendedRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3ExtendedRequestIdDecorator.kt new file mode 100644 index 0000000000..6b117b60da --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3ExtendedRequestIdDecorator.kt @@ -0,0 +1,28 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk.customize.s3 + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rustsdk.BaseRequestIdDecorator +import software.amazon.smithy.rustsdk.InlineAwsDependency + +class S3ExtendedRequestIdDecorator : BaseRequestIdDecorator() { + override val name: String = "S3ExtendedRequestIdDecorator" + override val order: Byte = 0 + + override val fieldName: String = "extended_request_id" + override val accessorFunctionName: String = "extended_request_id" + + private val requestIdModule: RuntimeType = + RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("s3_request_id")) + + override fun accessorTrait(codegenContext: ClientCodegenContext): RuntimeType = + requestIdModule.resolve("RequestIdExt") + + override fun applyToError(codegenContext: ClientCodegenContext): RuntimeType = + requestIdModule.resolve("apply_extended_request_id") +} diff --git a/aws/sdk/integration-tests/s3/tests/custom-error-deserializer.rs b/aws/sdk/integration-tests/s3/tests/custom-error-deserializer.rs deleted file mode 100644 index 7623fb3099..0000000000 --- a/aws/sdk/integration-tests/s3/tests/custom-error-deserializer.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_sdk_s3::operation::GetObject; -use aws_sdk_s3::types::RequestId; -use aws_sdk_s3::ErrorExt; -use aws_smithy_http::response::ParseHttpResponse; -use bytes::Bytes; - -#[test] -fn deserialize_extended_errors() { - let resp = http::Response::builder() - .header( - "x-amz-id-2", - "gyB+3jRPnrkN98ZajxHXr3u7EFM67bNgSAxexeEHndCX/7GRnfTXxReKUQF28IfP", - ) - .header("x-amz-request-id", "correct-request-id") - .status(404) - .body( - r#" - - NoSuchKey - The resource you requested does not exist - /mybucket/myfoto.jpg - incorrect-request-id - "#, - ) - .unwrap(); - let err = GetObject::new() - .parse_loaded(&resp.map(Bytes::from)) - .expect_err("status was 404, this is an error"); - assert_eq!( - err.meta().extended_request_id(), - Some("gyB+3jRPnrkN98ZajxHXr3u7EFM67bNgSAxexeEHndCX/7GRnfTXxReKUQF28IfP") - ); - assert_eq!(err.meta().request_id(), Some("correct-request-id")); -} diff --git a/aws/sdk/integration-tests/s3/tests/request_id.rs b/aws/sdk/integration-tests/s3/tests/request_id.rs index 76d88cc506..6bd5f446c6 100644 --- a/aws/sdk/integration-tests/s3/tests/request_id.rs +++ b/aws/sdk/integration-tests/s3/tests/request_id.rs @@ -5,7 +5,7 @@ use aws_sdk_s3::error::GetObjectErrorKind; use aws_sdk_s3::operation::{GetObject, ListBuckets}; -use aws_sdk_s3::types::RequestId; +use aws_sdk_s3::types::{RequestId, RequestIdExt}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::operation; use aws_smithy_http::response::ParseHttpResponse; @@ -15,6 +15,7 @@ use bytes::Bytes; fn get_request_id_from_modeled_error() { let resp = http::Response::builder() .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") .status(404) .body( r#" @@ -32,12 +33,21 @@ fn get_request_id_from_modeled_error() { assert!(matches!(err.kind, GetObjectErrorKind::NoSuchKey(_))); assert_eq!(Some("correct-request-id"), err.request_id()); assert_eq!(Some("correct-request-id"), err.meta().request_id()); + assert_eq!( + Some("correct-extended-request-id"), + err.extended_request_id() + ); + assert_eq!( + Some("correct-extended-request-id"), + err.meta().extended_request_id() + ); } #[test] fn get_request_id_from_unmodeled_error() { let resp = http::Response::builder() .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") .status(500) .body( r#" @@ -55,12 +65,21 @@ fn get_request_id_from_unmodeled_error() { assert!(matches!(err.kind, GetObjectErrorKind::Unhandled(_))); assert_eq!(Some("correct-request-id"), err.request_id()); assert_eq!(Some("correct-request-id"), err.meta().request_id()); + assert_eq!( + Some("correct-extended-request-id"), + err.extended_request_id() + ); + assert_eq!( + Some("correct-extended-request-id"), + err.meta().extended_request_id() + ); } #[test] fn get_request_id_from_successful_nonstreaming_response() { let resp = http::Response::builder() .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") .status(200) .body( r#" @@ -74,12 +93,17 @@ fn get_request_id_from_successful_nonstreaming_response() { .parse_loaded(&resp.map(Bytes::from)) .expect("valid successful response"); assert_eq!(Some("correct-request-id"), output.request_id()); + assert_eq!( + Some("correct-extended-request-id"), + output.extended_request_id() + ); } #[test] fn get_request_id_from_successful_streaming_response() { let resp = http::Response::builder() .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") .status(200) .body(SdkBody::from("some streaming file data")) .unwrap(); @@ -88,4 +112,8 @@ fn get_request_id_from_successful_streaming_response() { .parse_unloaded(&mut resp) .expect("valid successful response"); assert_eq!(Some("correct-request-id"), output.request_id()); + assert_eq!( + Some("correct-extended-request-id"), + output.extended_request_id() + ); } From bd9d1667d0ed03afdaa39656c8a69c8b86432ebb Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 22 Dec 2022 15:06:38 -0800 Subject: [PATCH 09/39] Update changelog --- CHANGELOG.next.toml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 6e52a1ed29..d18c260360 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -75,3 +75,35 @@ A new AWS runtime crate called `aws-credential-types` has been introduced. Types references = ["smithy-rs#2108"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "ysaito1001" + +[[aws-sdk-rust]] +message = """Request IDs can now be easily retrieved on successful responses. For example, with S3: +```rust +// Import the trait to get the `request_id` method on outputs +use aws_sdk_s3::types::RequestId; + +let output = client.list_buckets().send().await?; +println!("Request ID: {:?}", output.request_id()); +``` +""" +references = ["smithy-rs#76", "smithy-rs#2129"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "jdisanti" + +[[aws-sdk-rust]] +message = """Retrieving a request ID from errors now requires importing the `RequestId` trait. For example, with S3: +```rust +use aws_sdk_s3::types::RequestId; + +println!("Request ID: {:?}", error.request_id()); +``` +""" +references = ["smithy-rs#76", "smithy-rs#2129"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "jdisanti" + +[[smithy-rs]] +message = "Generic clients no longer expose a `request_id()` function on errors. To get request ID functionality, use the SDK code generator." +references = ["smithy-rs#76", "smithy-rs#2129"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client"} +author = "jdisanti" From fa8266133b94a3d1ad6867056b49951bf1427c78 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 11 Jan 2023 12:57:49 -0800 Subject: [PATCH 10/39] Fix `codegen-core` tests --- .../codegen/core/smithy/generators/BuilderGeneratorTest.kt | 6 +++--- .../core/smithy/generators/StructureGeneratorTest.kt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt index e6a0ed7e0b..eeafe31091 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt @@ -131,11 +131,11 @@ internal class BuilderGeneratorTest { fun `builder for a sensitive struct should implement the debug trait as such`() { val provider = testSymbolProvider(model) val writer = RustWriter.forModule("model") - val structGenerator = StructureGenerator(model, provider, writer, secretStructure) - val builderGenerator = BuilderGenerator(model, provider, secretStructure) + val structGenerator = StructureGenerator(model, provider, writer, secretStructure, emptyList()) + val builderGenerator = BuilderGenerator(model, provider, secretStructure, emptyList()) structGenerator.render() builderGenerator.render(writer) - writer.implBlock(secretStructure, provider) { + writer.implBlock(provider.toSymbol(secretStructure)) { builderGenerator.renderConvenienceMethod(this) } writer.compileAndTest( diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt index bce83f58bb..3c9787fd9b 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt @@ -177,7 +177,7 @@ class StructureGeneratorTest { fun `generate a custom debug implementation when the sensitive trait is applied to the struct`() { val provider = testSymbolProvider(model) val writer = RustWriter.forModule("lib") - val generator = StructureGenerator(model, provider, writer, secretStructure) + val generator = StructureGenerator(model, provider, writer, secretStructure, emptyList()) generator.render() writer.unitTest( "sensitive_structure_redacted", @@ -196,8 +196,8 @@ class StructureGeneratorTest { val provider = testSymbolProvider(model) val project = TestWorkspace.testProject(provider) project.useShapeWriter(inner) { - val secretGenerator = StructureGenerator(model, provider, this, secretStructure) - val generator = StructureGenerator(model, provider, this, structWithInnerSecretStructure) + val secretGenerator = StructureGenerator(model, provider, this, secretStructure, emptyList()) + val generator = StructureGenerator(model, provider, this, structWithInnerSecretStructure, emptyList()) secretGenerator.render() generator.render() unitTest( From 8c09f45c5e70a11593b698f60c4a04472089cdc9 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 12 Jan 2023 09:56:33 -0800 Subject: [PATCH 11/39] Combine `Error` and `ErrorKind` --- .../aws-config/src/sts/assume_role.rs | 8 +- aws/rust-runtime/aws-http/src/request_id.rs | 10 +- .../aws-inlineable/src/s3_request_id.rs | 10 +- .../smithy/rustsdk/BaseRequestIdDecorator.kt | 25 ++- .../query-strings-are-correctly-encoded.rs | 15 +- .../integration-tests/s3/tests/request_id.rs | 6 +- .../client/smithy/ClientCodegenVisitor.kt | 53 ++++--- .../protocols/HttpBoundProtocolGenerator.kt | 13 +- .../rust/codegen/core/smithy/RuntimeType.kt | 4 +- .../smithy/generators/StructureGenerator.kt | 11 +- .../generators/error/ErrorCustomization.kt | 9 ++ .../smithy/generators/error/ErrorGenerator.kt | 86 ++++++++++- .../error/OperationErrorGenerator.kt | 145 +++++++++--------- .../generators/error/ServiceErrorGenerator.kt | 24 ++- .../error/UnhandledErrorGenerator.kt | 50 ------ .../core/testutil/EventStreamTestTools.kt | 2 +- .../rust/codegen/core/testutil/TestHelpers.kt | 2 +- .../error/ServiceErrorGeneratorTest.kt | 2 +- rust-runtime/aws-smithy-http/src/result.rs | 37 ++++- rust-runtime/aws-smithy-types/src/error.rs | 49 +++++- .../aws-smithy-types/src/error/unhandled.rs | 90 +++++++++++ 21 files changed, 450 insertions(+), 201 deletions(-) delete mode 100644 codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/UnhandledErrorGenerator.kt create mode 100644 rust-runtime/aws-smithy-types/src/error/unhandled.rs diff --git a/aws/rust-runtime/aws-config/src/sts/assume_role.rs b/aws/rust-runtime/aws-config/src/sts/assume_role.rs index 8b4da9466c..b0a7ae14c7 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -9,7 +9,7 @@ use aws_credential_types::lazy_caching::LazyCachingCredentialsProvider; use aws_credential_types::provider::{ self, error::CredentialsError, future, ProvideCredentials, SharedCredentialsProvider, }; -use aws_sdk_sts::error::AssumeRoleErrorKind; +use aws_sdk_sts::error::AssumeRoleError; use aws_sdk_sts::middleware::DefaultMiddleware; use aws_sdk_sts::model::PolicyDescriptorType; use aws_sdk_sts::operation::AssumeRole; @@ -255,9 +255,9 @@ impl Inner { } Err(SdkError::ServiceError(ref context)) if matches!( - context.err().kind, - AssumeRoleErrorKind::RegionDisabledException(_) - | AssumeRoleErrorKind::MalformedPolicyDocumentException(_) + context.err(), + AssumeRoleError::RegionDisabledException(_) + | AssumeRoleError::MalformedPolicyDocumentException(_) ) => { Err(CredentialsError::invalid_configuration( diff --git a/aws/rust-runtime/aws-http/src/request_id.rs b/aws/rust-runtime/aws-http/src/request_id.rs index f9e9b89c68..358c735c9e 100644 --- a/aws/rust-runtime/aws-http/src/request_id.rs +++ b/aws/rust-runtime/aws-http/src/request_id.rs @@ -6,7 +6,9 @@ use aws_smithy_http::http::HttpHeaders; use aws_smithy_http::operation; use aws_smithy_http::result::SdkError; -use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; +use aws_smithy_types::error::{ + Builder as GenericErrorBuilder, Error as GenericError, ErrorMetadata, Unhandled, +}; use http::{HeaderMap, HeaderValue}; /// Constant for the [`aws_smithy_types::error::Error`] extra field that contains the request ID @@ -37,6 +39,12 @@ impl RequestId for GenericError { } } +impl RequestId for Unhandled { + fn request_id(&self) -> Option<&str> { + self.meta().request_id() + } +} + impl RequestId for operation::Response { fn request_id(&self) -> Option<&str> { extract_request_id(self.http().headers()) diff --git a/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs index d39611cdc1..b4eae20345 100644 --- a/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs +++ b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs @@ -6,7 +6,9 @@ use aws_smithy_client::SdkError; use aws_smithy_http::http::HttpHeaders; use aws_smithy_http::operation; -use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; +use aws_smithy_types::error::{ + Builder as GenericErrorBuilder, Error as GenericError, ErrorMetadata, Unhandled, +}; use http::{HeaderMap, HeaderValue}; const EXTENDED_REQUEST_ID: &str = "s3_extended_request_id"; @@ -38,6 +40,12 @@ impl RequestIdExt for GenericError { } } +impl RequestIdExt for Unhandled { + fn extended_request_id(&self) -> Option<&str> { + self.meta().extended_request_id() + } +} + impl RequestIdExt for operation::Response { fn extended_request_id(&self) -> Option<&str> { extract_extended_request_id(self.http().headers()) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt index 49aa477010..4b9c858452 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.Writable 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.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType @@ -94,7 +95,7 @@ abstract class BaseRequestIdDecorator : ClientCodegenDecorator { """ impl #{AccessorTrait} for #{error} { fn $accessorFunctionName(&self) -> Option<&str> { - self.meta.$accessorFunctionName() + self.meta().$accessorFunctionName() } } """, @@ -103,6 +104,28 @@ abstract class BaseRequestIdDecorator : ClientCodegenDecorator { ) } + is ErrorSection.ServiceErrorAdditionalTraitImpls -> { + rustBlock("impl #T for Error", accessorTrait(codegenContext)) { + rustBlock("fn $accessorFunctionName(&self) -> Option<&str>") { + rustBlock("match self") { + section.allErrors.forEach { error -> + val sym = codegenContext.symbolProvider.toSymbol(error) + rust("Self::${sym.name}(e) => e.$accessorFunctionName(),") + } + rust("Self::Unhandled(e) => e.$accessorFunctionName(),") + } + } + } + } + + is ErrorSection.ErrorAdditionalTraitImpls -> { + rustBlock("impl #1T for #2T", accessorTrait(codegenContext), section.errorType) { + rustBlock("fn $accessorFunctionName(&self) -> Option<&str>") { + rust("use #T;", RuntimeType.errorMetadataTrait(codegenContext.runtimeConfig)) + rust("self.meta().$accessorFunctionName()") + } + } + } else -> {} } } diff --git a/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs b/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs index 858d013841..7714becfe5 100644 --- a/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs +++ b/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs @@ -71,7 +71,7 @@ async fn test_s3_signer_query_string_with_all_valid_chars() { #[tokio::test] #[ignore] async fn test_query_strings_are_correctly_encoded() { - use aws_sdk_s3::error::{ListObjectsV2Error, ListObjectsV2ErrorKind}; + use aws_sdk_s3::error::ListObjectsV2Error; use aws_smithy_http::result::SdkError; tracing_subscriber::fmt::init(); @@ -92,22 +92,19 @@ async fn test_query_strings_are_correctly_encoded() { .send() .await; if let Err(SdkError::ServiceError(context)) = res { - let ListObjectsV2Error { kind, .. } = context.err(); - match kind { - ListObjectsV2ErrorKind::Unhandled(e) + match context.err() { + ListObjectsV2Error::Unhandled(e) if e.to_string().contains("SignatureDoesNotMatch") => { chars_that_break_signing.push(byte); } - ListObjectsV2ErrorKind::Unhandled(e) if e.to_string().contains("InvalidUri") => { + ListObjectsV2Error::Unhandled(e) if e.to_string().contains("InvalidUri") => { chars_that_break_uri_parsing.push(byte); } - ListObjectsV2ErrorKind::Unhandled(e) - if e.to_string().contains("InvalidArgument") => - { + ListObjectsV2Error::Unhandled(e) if e.to_string().contains("InvalidArgument") => { chars_that_are_invalid_arguments.push(byte); } - ListObjectsV2ErrorKind::Unhandled(e) if e.to_string().contains("InvalidToken") => { + ListObjectsV2Error::Unhandled(e) if e.to_string().contains("InvalidToken") => { panic!("refresh your credentials and run this test again"); } e => todo!("unexpected error: {:?}", e), diff --git a/aws/sdk/integration-tests/s3/tests/request_id.rs b/aws/sdk/integration-tests/s3/tests/request_id.rs index 6bd5f446c6..c732db7bb8 100644 --- a/aws/sdk/integration-tests/s3/tests/request_id.rs +++ b/aws/sdk/integration-tests/s3/tests/request_id.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_s3::error::GetObjectErrorKind; +use aws_sdk_s3::error::GetObjectError; use aws_sdk_s3::operation::{GetObject, ListBuckets}; use aws_sdk_s3::types::{RequestId, RequestIdExt}; use aws_smithy_http::body::SdkBody; @@ -30,7 +30,7 @@ fn get_request_id_from_modeled_error() { let err = GetObject::new() .parse_loaded(&resp.map(Bytes::from)) .expect_err("status was 404, this is an error"); - assert!(matches!(err.kind, GetObjectErrorKind::NoSuchKey(_))); + assert!(matches!(err, GetObjectError::NoSuchKey(_))); assert_eq!(Some("correct-request-id"), err.request_id()); assert_eq!(Some("correct-request-id"), err.meta().request_id()); assert_eq!( @@ -62,7 +62,7 @@ fn get_request_id_from_unmodeled_error() { let err = GetObject::new() .parse_loaded(&resp.map(Bytes::from)) .expect_err("status 500"); - assert!(matches!(err.kind, GetObjectErrorKind::Unhandled(_))); + assert!(matches!(err, GetObjectError::Unhandled(_))); assert_eq!(Some("correct-request-id"), err.request_id()); assert_eq!(Some("correct-request-id"), err.meta().request_id()); assert_eq!( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index 81999aefce..bb1802619e 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceGenerator @@ -26,6 +27,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.transformers.AddErrorMe import software.amazon.smithy.rust.codegen.client.smithy.transformers.RemoveEventStreamOperations 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.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig @@ -33,6 +35,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerat import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.OperationErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory @@ -187,26 +190,40 @@ class ClientCodegenVisitor( * This function _does not_ generate any serializers */ override fun structureShape(shape: StructureShape) { - logger.fine("generating a structure...") rustCrate.useShapeWriter(shape) { - StructureGenerator( - model, - symbolProvider, - this, - shape, - codegenDecorator.structureCustomizations(codegenContext, emptyList()), - ).render() - if (!shape.hasTrait()) { - val builderGenerator = - BuilderGenerator( - codegenContext.model, - codegenContext.symbolProvider, + when (val errorTrait = shape.getTrait()) { + null -> { + StructureGenerator( + model, + symbolProvider, + this, + shape, + codegenDecorator.structureCustomizations(codegenContext, emptyList()), + ).render() + + if (!shape.hasTrait()) { + val builderGenerator = + BuilderGenerator( + codegenContext.model, + codegenContext.symbolProvider, + shape, + codegenDecorator.builderCustomizations(codegenContext, emptyList()), + ) + builderGenerator.render(this) + implBlock(symbolProvider.toSymbol(shape)) { + builderGenerator.renderConvenienceMethod(this) + } + } + } + else -> { + ErrorGenerator( + model, + symbolProvider, + this, shape, - codegenDecorator.builderCustomizations(codegenContext, emptyList()), - ) - builderGenerator.render(this) - implBlock(symbolProvider.toSymbol(shape)) { - builderGenerator.renderConvenienceMethod(this) + errorTrait, + codegenDecorator.errorCustomizations(codegenContext, emptyList()), + ).render(CodegenTarget.CLIENT) } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index b25642f34c..b8f8b08bdd 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -206,8 +206,8 @@ class HttpBoundProtocolTraitImplGenerator( val variantName = symbolProvider.toSymbol(model.expectShape(error.id)).name val errorCode = httpBindingResolver.errorCode(errorShape).dq() withBlock( - "$errorCode => #1T { meta: generic, kind: #1TKind::$variantName({", - "})},", + "$errorCode => #1T::$variantName({", + "}),", errorSymbol, ) { Attribute.AllowUnusedMut.render(this) @@ -218,7 +218,14 @@ class HttpBoundProtocolTraitImplGenerator( errorShape, httpBindingResolver.errorResponseBindings(errorShape), errorSymbol, - listOf(), + listOf(object : OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + if (section is OperationSection.MutateOutput) { + rust("let output = output._meta(generic);") + } + } + }, + ), ) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 1998f2a86f..e5591acb2f 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -259,10 +259,12 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun classifyRetry(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("retry::ClassifyRetry") fun dateTime(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("DateTime") fun document(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("Document") - fun errorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ErrorKind") + fun retryErrorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ErrorKind") fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::Receiver") fun genericError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Error") fun genericErrorBuilder(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Builder") + fun errorMetadataTrait(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::ErrorMetadata") + fun unhandledError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Unhandled") fun jsonErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.jsonErrors(runtimeConfig)) fun labelFormat(runtimeConfig: RuntimeConfig, func: String) = smithyHttp(runtimeConfig).resolve("label::$func") fun operation(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("operation::Operation") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index 50c0ecebc3..904a57c474 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -22,14 +22,12 @@ import software.amazon.smithy.rust.codegen.core.rustlang.isDeref import software.amazon.smithy.rust.codegen.core.rustlang.render 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.smithy.CodegenTarget 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.customize.NamedSectionGenerator import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.dq @@ -64,18 +62,15 @@ open class StructureGenerator( ) { private val errorTrait = shape.getTrait() protected val members: List = shape.allMembers.values.toList() - protected val accessorMembers: List = when (errorTrait) { + private val accessorMembers: List = when (errorTrait) { null -> members // Let the ErrorGenerator render the error message accessor if this is an error struct else -> members.filter { "message" != symbolProvider.toMemberName(it) } } - protected val name = symbolProvider.toSymbol(shape).name + protected val name: String = symbolProvider.toSymbol(shape).name - fun render(forWhom: CodegenTarget = CodegenTarget.CLIENT) { + fun render() { renderStructure() - errorTrait?.also { errorTrait -> - ErrorGenerator(model, symbolProvider, writer, shape, errorTrait).render(forWhom) - } } /** diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt index 6b9c3d849a..786a9e798e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt @@ -5,6 +5,8 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators.error +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedSectionGenerator import software.amazon.smithy.rust.codegen.core.smithy.customize.Section @@ -13,6 +15,13 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.Section sealed class ErrorSection(name: String) : Section(name) { /** Use this section to add additional trait implementations to the generated operation errors */ data class OperationErrorAdditionalTraitImpls(val errorType: RuntimeType) : ErrorSection("OperationErrorAdditionalTraitImpls") + + /** Use this section to add additional trait implementations to the generated service error */ + class ServiceErrorAdditionalTraitImpls(val allErrors: List) : + ErrorSection("ServiceErrorAdditionalTraitImpls") + + /** Use this section to add additional trait implementations to the generated error structures */ + class ErrorAdditionalTraitImpls(val errorType: Symbol) : ErrorSection("ErrorAdditionalTraitImpls") } /** Customizations for generated errors */ diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt index 071a5bd89a..36d7057237 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt @@ -13,16 +13,26 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asDeref +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.rustlang.render 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.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget 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.Companion.StdError +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.genericError import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.mapRustType import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.ValueExpression @@ -39,17 +49,17 @@ sealed class ErrorKind { object Throttling : ErrorKind() { override fun writable(runtimeConfig: RuntimeConfig) = - writable { rust("#T::ThrottlingError", RuntimeType.errorKind(runtimeConfig)) } + writable { rust("#T::ThrottlingError", RuntimeType.retryErrorKind(runtimeConfig)) } } object Client : ErrorKind() { override fun writable(runtimeConfig: RuntimeConfig) = - writable { rust("#T::ClientError", RuntimeType.errorKind(runtimeConfig)) } + writable { rust("#T::ClientError", RuntimeType.retryErrorKind(runtimeConfig)) } } object Server : ErrorKind() { override fun writable(runtimeConfig: RuntimeConfig) = - writable { rust("#T::ServerError", RuntimeType.errorKind(runtimeConfig)) } + writable { rust("#T::ServerError", RuntimeType.retryErrorKind(runtimeConfig)) } } } @@ -75,13 +85,72 @@ class ErrorGenerator( private val writer: RustWriter, private val shape: StructureShape, private val error: ErrorTrait, + private val customizations: List, ) { + private val runtimeConfig = symbolProvider.config().runtimeConfig + fun render(forWhom: CodegenTarget = CodegenTarget.CLIENT) { val symbol = symbolProvider.toSymbol(shape) + + StructureGenerator( + model, symbolProvider, writer, shape, + listOf(object : StructureCustomization() { + override fun section(section: StructureSection): Writable = writable { + when (section) { + is StructureSection.AdditionalFields -> { + rust("pub(crate) _meta: #T,", genericError(runtimeConfig)) + } + is StructureSection.AdditionalDebugFields -> { + rust("""${section.formatterName}.field("_meta", &self._meta);""") + } + } + } + }, + ), + ).render() + + BuilderGenerator( + model, symbolProvider, shape, + listOf(object : BuilderCustomization() { + override fun section(section: BuilderSection): Writable = writable { + when (section) { + is BuilderSection.AdditionalFields -> { + rust("_meta: Option<#T>,", genericError(runtimeConfig)) + } + is BuilderSection.AdditionalMethods -> { + rustTemplate( + """ + pub(crate) fn _meta(mut self, _meta: #{generic_error}) -> Self { + self._meta = Some(_meta); + self + } + + pub(crate) fn _set_meta(&mut self, _meta: Option<#{generic_error}>) -> &mut Self { + self._meta = _meta; + self + } + """, + "generic_error" to genericError(runtimeConfig), + ) + } + is BuilderSection.AdditionalFieldsInBuild -> { + rust("_meta: self._meta.unwrap_or_default(),") + } + } + } + }, + ), + ).let { builderGen -> + writer.implBlock(symbol) { + builderGen.renderConvenienceMethod(this) + } + builderGen.render(writer) + } + val messageShape = shape.errorMessageMember() - val errorKindT = RuntimeType.errorKind(symbolProvider.config().runtimeConfig) + val errorKindT = RuntimeType.retryErrorKind(runtimeConfig) writer.rustBlock("impl ${symbol.name}") { - val retryKindWriteable = shape.modeledRetryKind(error)?.writable(symbolProvider.config().runtimeConfig) + val retryKindWriteable = shape.modeledRetryKind(error)?.writable(runtimeConfig) if (retryKindWriteable != null) { rust("/// Returns `Some(${errorKindT.name})` if the error is retryable. Otherwise, returns `None`.") rustBlock("pub fn retryable_error_kind(&self) -> #T", errorKindT) { @@ -153,6 +222,13 @@ class ErrorGenerator( write("Ok(())") } } + writer.write("impl #T for ${symbol.name} {}", StdError) + + writer.rustBlock("impl #T for ${symbol.name}", RuntimeType.errorMetadataTrait(runtimeConfig)) { + rust("fn meta(&self) -> &#T { &self._meta }", genericError(runtimeConfig)) + } + + writer.writeCustomizations(customizations, ErrorSection.ErrorAdditionalTraitImpls(symbol)) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt index 0db6e51234..650f18d01d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt @@ -26,6 +26,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.genericError +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.unhandledError import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations @@ -64,7 +66,7 @@ class OperationErrorGenerator( private val customizations: List, ) { private val runtimeConfig = symbolProvider.config().runtimeConfig - private val genericError = RuntimeType.genericError(symbolProvider.config().runtimeConfig) + private val genericError = genericError(symbolProvider.config().runtimeConfig) private val createUnhandledError = RuntimeType.smithyHttp(runtimeConfig).resolve("result::CreateUnhandledError") @@ -86,71 +88,78 @@ class OperationErrorGenerator( writer.rust("/// Error type for the `${operationSymbol.name}` operation.") meta.render(writer) - writer.rustBlock("struct ${errorType.name}") { - rust( - """ - /// Kind of error that occurred. - pub kind: ${errorType.name}Kind, - /// Additional metadata about the error, including error code, message, and request ID. - pub (crate) meta: #T - """, - RuntimeType.genericError(runtimeConfig), - ) - } - writer.rustBlock("impl #T for ${errorType.name}", createUnhandledError) { - rustBlock("fn create_unhandled_error(source: Box) -> Self") { - rustBlock("Self") { - rust("kind: ${errorType.name}Kind::Unhandled(#T::new(source)),", unhandledError()) - rust("meta: Default::default()") - } - } - } - - writer.rust("/// Types of errors that can occur for the `${operationSymbol.name}` operation.") - meta.render(writer) - writer.rustBlock("enum ${errorType.name}Kind") { + writer.rustBlock("enum ${errorType.name}") { errors.forEach { errorVariant -> documentShape(errorVariant, model) deprecatedShape(errorVariant) val errorVariantSymbol = symbolProvider.toSymbol(errorVariant) write("${errorVariantSymbol.name}(#T),", errorVariantSymbol) } - docs(UNHANDLED_ERROR_DOCS) rust( """ + /// An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code). Unhandled(#T), """, - unhandledError(), + unhandledError(runtimeConfig), ) } + writer.rustBlock("impl #T for ${errorType.name}", createUnhandledError) { + rustBlock( + """ + fn create_unhandled_error( + source: Box, + meta: Option<#T> + ) -> Self + """, + genericError, + ) { + rust( + """ + Self::Unhandled({ + let mut builder = #T::builder().source(source); + builder.set_meta(meta); + builder.build() + }) + """, + unhandledError(runtimeConfig), + ) + } + } writer.rustBlock("impl #T for ${errorType.name}", RuntimeType.Display) { rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { - delegateToVariants(errors, errorType) { + delegateToVariants(errors) { writable { rust("_inner.fmt(f)") } } } } + writer.rustBlock("impl #T for ${errorType.name}", RuntimeType.errorMetadataTrait(runtimeConfig)) { + rustBlock("fn meta(&self) -> &#T", genericError(runtimeConfig)) { + delegateToVariants(errors) { + writable { rust("_inner.meta()") } + } + } + } writer.writeCustomizations(customizations, ErrorSection.OperationErrorAdditionalTraitImpls(errorType)) - val errorKindT = RuntimeType.errorKind(symbolProvider.config().runtimeConfig) + val retryErrorKindT = RuntimeType.retryErrorKind(symbolProvider.config().runtimeConfig) writer.rustBlock( "impl #T for ${errorType.name}", RuntimeType.provideErrorKind(symbolProvider.config().runtimeConfig), ) { rustBlock("fn code(&self) -> Option<&str>") { - rust("${errorType.name}::code(self)") + rust("#T::code(self)", RuntimeType.errorMetadataTrait(runtimeConfig)) } - rustBlock("fn retryable_error_kind(&self) -> Option<#T>", errorKindT) { + rustBlock("fn retryable_error_kind(&self) -> Option<#T>", retryErrorKindT) { val retryableVariants = errors.filter { it.hasTrait() } if (retryableVariants.isEmpty()) { rust("None") } else { - rustBlock("match &self.kind") { + rustBlock("match self") { retryableVariants.forEach { val errorVariantSymbol = symbolProvider.toSymbol(it) - rust("${errorType.name}Kind::${errorVariantSymbol.name}(inner) => Some(inner.retryable_error_kind()),") + rust("Self::${errorVariantSymbol.name}(inner) => Some(inner.retryable_error_kind()),") } rust("_ => None") } @@ -161,60 +170,49 @@ class OperationErrorGenerator( writer.rustBlock("impl ${errorType.name}") { writer.rustTemplate( """ - /// Creates a new `${errorType.name}`. - pub fn new(kind: ${errorType.name}Kind, meta: #{generic_error}) -> Self { - Self { kind, meta } - } - /// Creates the `${errorType.name}::Unhandled` variant from any error type. pub fn unhandled(err: impl Into>) -> Self { - Self { - kind: ${errorType.name}Kind::Unhandled(#{Unhandled}::new(err.into())), - meta: Default::default() - } + Self::Unhandled(#{Unhandled}::builder().source(err).build()) } /// Creates the `${errorType.name}::Unhandled` variant from a `#{generic_error}`. pub fn generic(err: #{generic_error}) -> Self { - Self { - meta: err.clone(), - kind: ${errorType.name}Kind::Unhandled(#{Unhandled}::new(err.into())), - } - } - - /// Returns the error message if one is available. - pub fn message(&self) -> Option<&str> { - self.meta.message() - } - - /// Returns error metadata, which includes the error code, message, - /// request ID, and potentially additional information. - pub fn meta(&self) -> &#{generic_error} { - &self.meta - } - - /// Returns the error code if it's available. - pub fn code(&self) -> Option<&str> { - self.meta.code() + Self::Unhandled(#{Unhandled}::builder().source(err.clone()).meta(err).build()) } """, "generic_error" to genericError, "std_error" to RuntimeType.StdError, - "Unhandled" to unhandledError(), + "Unhandled" to unhandledError(runtimeConfig), + ) + writer.docs( + """ + Returns error metadata, which includes the error code, message, + request ID, and potentially additional information. + """, ) + writer.rustBlock("pub fn meta(&self) -> &#T", genericError) { + rust("use #T;", RuntimeType.errorMetadataTrait(runtimeConfig)) + rustBlock("match self") { + errors.forEach { error -> + val errorVariantSymbol = symbolProvider.toSymbol(error) + rust("Self::${errorVariantSymbol.name}(e) => e.meta(),") + } + rust("Self::Unhandled(e) => e.meta(),") + } + } errors.forEach { error -> val errorVariantSymbol = symbolProvider.toSymbol(error) val fnName = errorVariantSymbol.name.toSnakeCase() - writer.rust("/// Returns `true` if the error kind is `${errorType.name}Kind::${errorVariantSymbol.name}`.") + writer.rust("/// Returns `true` if the error kind is `${errorType.name}::${errorVariantSymbol.name}`.") writer.rustBlock("pub fn is_$fnName(&self) -> bool") { - rust("matches!(&self.kind, ${errorType.name}Kind::${errorVariantSymbol.name}(_))") + rust("matches!(self, Self::${errorVariantSymbol.name}(_))") } } } writer.rustBlock("impl #T for ${errorType.name}", RuntimeType.StdError) { rustBlock("fn source(&self) -> Option<&(dyn #T + 'static)>", RuntimeType.StdError) { - delegateToVariants(errors, errorType) { + delegateToVariants(errors) { writable { rust("Some(_inner)") } @@ -232,11 +230,11 @@ class OperationErrorGenerator( * Generates code to delegate behavior to the variants, for example: * * ```rust - * match &self.kind { - * GreetingWithErrorsError::InvalidGreeting(_inner) => inner.fmt(f), - * GreetingWithErrorsError::ComplexError(_inner) => inner.fmt(f), - * GreetingWithErrorsError::FooError(_inner) => inner.fmt(f), - * GreetingWithErrorsError::Unhandled(_inner) => _inner.fmt(f), + * match self { + * Self::InvalidGreeting(_inner) => inner.fmt(f), + * Self::ComplexError(_inner) => inner.fmt(f), + * Self::FooError(_inner) => inner.fmt(f), + * Self::Unhandled(_inner) => _inner.fmt(f), * } * ``` * @@ -247,18 +245,17 @@ class OperationErrorGenerator( */ private fun RustWriter.delegateToVariants( errors: List, - symbol: RuntimeType, handler: (VariantMatch) -> Writable, ) { - rustBlock("match &self.kind") { + rustBlock("match self") { errors.forEach { val errorSymbol = symbolProvider.toSymbol(it) - rust("""${symbol.name}Kind::${errorSymbol.name}(_inner) => """) + rust("""Self::${errorSymbol.name}(_inner) => """) handler(VariantMatch.Modeled(errorSymbol, it))(this) write(",") } val unhandledHandler = handler(VariantMatch.Unhandled) - rustBlock("${symbol.name}Kind::Unhandled(_inner) =>") { + rustBlock("Self::Unhandled(_inner) =>") { unhandledHandler(this) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt index be63f1245c..dc5e63143a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt @@ -23,6 +23,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.unhandledError import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.transformers.allErrors import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors @@ -108,25 +109,34 @@ class ServiceErrorGenerator( ) { rustBlock("match err") { rust("#T::ServiceError(context) => Self::from(context.into_err()),", sdkError) - rust("_ => Error::Unhandled(#T::new(err.into())),", unhandledError()) + rustTemplate( + """ + _ => { + use #{ErrorMetadata}; + Error::Unhandled(#{Unhandled}::builder().meta(err.meta().clone()).source(err).build()) + } + """, + "ErrorMetadata" to RuntimeType.errorMetadataTrait(codegenContext.runtimeConfig), + "Unhandled" to unhandledError(codegenContext.runtimeConfig), + ) } } } rustBlock("impl From<#T> for Error", errorSymbol) { rustBlock("fn from(err: #T) -> Self", errorSymbol) { - rustBlock("match err.kind") { + rustBlock("match err") { operationErrors.forEach { errorShape -> val errSymbol = symbolProvider.toSymbol(errorShape) rust( - "#TKind::${errSymbol.name}(inner) => Error::${errSymbol.name}(inner),", + "#T::${errSymbol.name}(inner) => Error::${errSymbol.name}(inner),", errorSymbol, ) } rustTemplate( - "#{errorSymbol}Kind::Unhandled(inner) => Error::Unhandled(#{unhandled}::new(inner.into())),", + "#{errorSymbol}::Unhandled(inner) => Error::Unhandled(inner),", "errorSymbol" to errorSymbol, - "unhandled" to unhandledError(), + "unhandled" to unhandledError(codegenContext.runtimeConfig), ) } } @@ -147,8 +157,8 @@ class ServiceErrorGenerator( val sym = symbolProvider.toSymbol(error) rust("${sym.name}(#T),", sym) } - docs(UNHANDLED_ERROR_DOCS) - rust("Unhandled(#T)", unhandledError()) + docs("An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code).") + rust("Unhandled(#T)", unhandledError(codegenContext.runtimeConfig)) } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/UnhandledErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/UnhandledErrorGenerator.kt deleted file mode 100644 index bd28c1a852..0000000000 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/UnhandledErrorGenerator.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.core.smithy.generators.error - -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule -import software.amazon.smithy.rust.codegen.core.rustlang.docs -import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType - -internal const val UNHANDLED_ERROR_DOCS = - """ - An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code). - - When logging an error from the SDK, it is recommended that you either wrap the error in - [`DisplayErrorContext`](crate::types::DisplayErrorContext), use another - error reporter library that visits the error's cause/source chain, or call - [`Error::source`](std::error::Error::source) for more details about the underlying cause. - """ - -internal fun unhandledError(): RuntimeType = RuntimeType.forInlineFun("Unhandled", RustModule.Error) { - docs(UNHANDLED_ERROR_DOCS) - rustTemplate( - """ - ##[derive(Debug)] - pub struct Unhandled { - source: Box, - } - impl Unhandled { - ##[allow(unused)] - pub(crate) fn new(source: Box) -> Self { - Self { source } - } - } - impl std::fmt::Display for Unhandled { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - write!(f, "unhandled error") - } - } - impl #{StdError} for Unhandled { - fn source(&self) -> Option<&(dyn #{StdError} + 'static)> { - Some(self.source.as_ref() as _) - } - } - """, - "StdError" to RuntimeType.StdError, - ) -} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt index bb35605d71..19c24e9c18 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt @@ -126,7 +126,7 @@ object EventStreamTestTools { requirements.renderOperationError(this, model, symbolProvider, operationSymbol, errors) requirements.renderOperationError(this, model, symbolProvider, symbolProvider.toSymbol(unionShape), errors) for (shape in errors) { - StructureGenerator(model, symbolProvider, this, shape, emptyList()).render(codegenTarget) + StructureGenerator(model, symbolProvider, this, shape, emptyList()).render() requirements.renderBuilderForShape(this, codegenContext, shape) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt index dede58c682..60ba2e6c58 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/TestHelpers.kt @@ -103,7 +103,7 @@ fun StructureShape.renderWithModelBuilder( writer: RustWriter, forWhom: CodegenTarget = CodegenTarget.CLIENT, ) { - StructureGenerator(model, symbolProvider, writer, this, emptyList()).render(forWhom) + StructureGenerator(model, symbolProvider, writer, this, emptyList()).render() val modelBuilder = BuilderGenerator(model, symbolProvider, this, emptyList()) modelBuilder.render(writer) writer.implBlock(symbolProvider.toSymbol(this)) { diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt index c80fb13df2..6d78b2db7f 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt @@ -97,7 +97,7 @@ internal class ServiceErrorGeneratorTest { } for (shape in model.structureShapes) { if (shape.id.namespace == "com.example") { - StructureGenerator(model, symbolProvider, this, shape, emptyList()).render(CodegenTarget.CLIENT) + StructureGenerator(model, symbolProvider, this, shape, emptyList()).render() } } } diff --git a/rust-runtime/aws-smithy-http/src/result.rs b/rust-runtime/aws-smithy-http/src/result.rs index 6cf2fc5c78..8af9b7ded7 100644 --- a/rust-runtime/aws-smithy-http/src/result.rs +++ b/rust-runtime/aws-smithy-http/src/result.rs @@ -13,6 +13,7 @@ //! `Result` wrapper types for [success](SdkSuccess) and [failure](SdkError) responses. use crate::operation; +use aws_smithy_types::error::{Error as GenericError, ErrorMetadata, EMPTY_GENERIC_ERROR}; use aws_smithy_types::retry::ErrorKind; use std::error::Error; use std::fmt; @@ -126,8 +127,11 @@ impl ServiceError { /// /// This trait exists so that [`SdkError::into_service_error`] can be infallible. pub trait CreateUnhandledError { - /// Creates an unhandled error variant with the given `source`. - fn create_unhandled_error(source: Box) -> Self; + /// Creates an unhandled error variant with the given `source` and error metadata. + fn create_unhandled_error( + source: Box, + meta: Option, + ) -> Self; } /// Failed SDK Result @@ -200,19 +204,21 @@ impl SdkError { /// /// ```no_run /// # use aws_smithy_http::result::{SdkError, CreateUnhandledError}; - /// # #[derive(Debug)] enum GetObjectErrorKind { NoSuchKey(()), Other(()) } - /// # #[derive(Debug)] struct GetObjectError { kind: GetObjectErrorKind } + /// # #[derive(Debug)] enum GetObjectError { NoSuchKey(()), Other(()) } /// # impl std::fmt::Display for GetObjectError { /// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { unimplemented!() } /// # } /// # impl std::error::Error for GetObjectError {} /// # impl CreateUnhandledError for GetObjectError { - /// # fn create_unhandled_error(_: Box) -> Self { unimplemented!() } + /// # fn create_unhandled_error( + /// # _: Box, + /// # _: Option, + /// # ) -> Self { unimplemented!() } /// # } /// # fn example() -> Result<(), GetObjectError> { - /// # let sdk_err = SdkError::service_error(GetObjectError { kind: GetObjectErrorKind::NoSuchKey(()) }, ()); + /// # let sdk_err = SdkError::service_error(GetObjectError::NoSuchKey(()), ()); /// match sdk_err.into_service_error() { - /// GetObjectError { kind: GetObjectErrorKind::NoSuchKey(_) } => { + /// GetObjectError::NoSuchKey(_) => { /// // handle NoSuchKey /// } /// err @ _ => return Err(err), @@ -227,7 +233,7 @@ impl SdkError { { match self { Self::ServiceError(context) => context.source, - _ => E::create_unhandled_error(self.into()), + _ => E::create_unhandled_error(self.into(), None), } } @@ -278,6 +284,21 @@ where } } +impl ErrorMetadata for SdkError +where + E: ErrorMetadata, +{ + fn meta(&self) -> &aws_smithy_types::Error { + match self { + Self::ConstructionFailure(_) => &EMPTY_GENERIC_ERROR, + Self::TimeoutError(_) => &EMPTY_GENERIC_ERROR, + Self::DispatchFailure(_) => &EMPTY_GENERIC_ERROR, + Self::ResponseError(_) => &EMPTY_GENERIC_ERROR, + Self::ServiceError(err) => err.source.meta(), + } + } +} + #[derive(Debug)] enum ConnectorErrorKind { /// A timeout occurred while processing the request diff --git a/rust-runtime/aws-smithy-types/src/error.rs b/rust-runtime/aws-smithy-types/src/error.rs index 1945b7334a..02472eb150 100644 --- a/rust-runtime/aws-smithy-types/src/error.rs +++ b/rust-runtime/aws-smithy-types/src/error.rs @@ -10,6 +10,34 @@ use std::collections::HashMap; use std::fmt; pub mod display; +mod unhandled; + +pub use unhandled::Unhandled; + +/// Trait to retrieve error metadata from a result +pub trait ErrorMetadata { + /// Returns error metadata, which includes the error code, message, + /// request ID, and potentially additional information. + fn meta(&self) -> &Error; + + /// Returns the error code if it's available. + fn code(&self) -> Option<&str> { + self.meta().code() + } + + /// Returns the error message, if there is one. + fn message(&self) -> Option<&str> { + self.meta().message() + } +} + +/// Empty generic error metadata +#[doc(hidden)] +pub const EMPTY_GENERIC_ERROR: Error = Error { + code: None, + message: None, + extras: None, +}; /// Generic Error type /// @@ -20,7 +48,7 @@ pub mod display; pub struct Error { code: Option, message: Option, - extras: HashMap<&'static str, String>, + extras: Option>, } /// Builder for [`Error`]. @@ -68,7 +96,14 @@ impl Builder { /// } /// ``` pub fn custom(mut self, key: &'static str, value: impl Into) -> Self { - self.inner.extras.insert(key, value.into()); + if self.inner.extras.is_none() { + self.inner.extras = Some(HashMap::new()); + } + self.inner + .extras + .as_mut() + .unwrap() + .insert(key, value.into()); self } @@ -89,7 +124,9 @@ impl Error { } /// Returns additional information about the error if it's present. pub fn extra(&self, key: &'static str) -> Option<&str> { - self.extras.get(key).map(|k| k.as_str()) + self.extras + .as_ref() + .and_then(|extras| extras.get(key).map(|k| k.as_str())) } /// Creates an `Error` builder. @@ -122,8 +159,10 @@ impl fmt::Display for Error { if let Some(message) = &self.message { fmt.field("message", message); } - for (k, v) in &self.extras { - fmt.field(k, &v); + if let Some(extras) = &self.extras { + for (k, v) in extras { + fmt.field(k, &v); + } } fmt.finish() } diff --git a/rust-runtime/aws-smithy-types/src/error/unhandled.rs b/rust-runtime/aws-smithy-types/src/error/unhandled.rs new file mode 100644 index 0000000000..50acc17e0e --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/error/unhandled.rs @@ -0,0 +1,90 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Unhandled error type. + +use crate::error::{Error as GenericError, ErrorMetadata}; +use std::error::Error as StdError; + +/// Builder for [`Unhandled`] +#[derive(Default, Debug)] +pub struct Builder { + source: Option>, + meta: Option, +} + +impl Builder { + /// Sets the error source + pub fn source(mut self, source: impl Into>) -> Self { + self.source = Some(source.into()); + self + } + + /// Sets the error source + pub fn set_source( + &mut self, + source: Option>, + ) -> &mut Self { + self.source = source; + self + } + + /// Sets the error metadata + pub fn meta(mut self, meta: GenericError) -> Self { + self.meta = Some(meta); + self + } + + /// Sets the error metadata + pub fn set_meta(&mut self, meta: Option) -> &mut Self { + self.meta = meta; + self + } + + /// Builds the unhandled error + pub fn build(self) -> Unhandled { + Unhandled { + source: self.source.expect("unhandled errors must have a source"), + meta: self.meta.unwrap_or_default(), + } + } +} + +/// An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code). +/// +/// When logging an error from the SDK, it is recommended that you either wrap the error in +/// [`DisplayErrorContext`](crate::error::DisplayErrorContext), use another +/// error reporter library that visits the error's cause/source chain, or call +/// [`Error::source`](std::error::Error::source) for more details about the underlying cause. +#[derive(Debug)] +pub struct Unhandled { + source: Box, + meta: GenericError, +} + +impl Unhandled { + /// Returns a builder to construct an unhandled error. + pub fn builder() -> Builder { + Default::default() + } +} + +impl std::fmt::Display for Unhandled { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "unhandled error") + } +} + +impl StdError for Unhandled { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(self.source.as_ref() as _) + } +} + +impl ErrorMetadata for Unhandled { + fn meta(&self) -> &GenericError { + &self.meta + } +} From 70547bf5da52cf624538fad57598311fc23bab77 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 11 Jan 2023 13:58:21 -0800 Subject: [PATCH 12/39] Add test for service error conversion --- .../integration-tests/s3/tests/request_id.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/aws/sdk/integration-tests/s3/tests/request_id.rs b/aws/sdk/integration-tests/s3/tests/request_id.rs index c732db7bb8..957dd8cb28 100644 --- a/aws/sdk/integration-tests/s3/tests/request_id.rs +++ b/aws/sdk/integration-tests/s3/tests/request_id.rs @@ -117,3 +117,32 @@ fn get_request_id_from_successful_streaming_response() { output.extended_request_id() ); } + +// Verify that the conversion from operation error to the top-level service error maintains the request ID +#[test] +fn conversion_to_service_error_maintains_request_id() { + let resp = http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") + .status(404) + .body( + r#" + + NoSuchKey + The resource you requested does not exist + /mybucket/myfoto.jpg + incorrect-request-id + "#, + ) + .unwrap(); + let err = GetObject::new() + .parse_loaded(&resp.map(Bytes::from)) + .expect_err("status was 404, this is an error"); + + let service_error: aws_sdk_s3::Error = err.into(); + assert_eq!(Some("correct-request-id"), service_error.request_id()); + assert_eq!( + Some("correct-extended-request-id"), + service_error.extended_request_id() + ); +} From 65367e005a1dab38c4cbb5c4c1c0da9556e19d7c Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 11 Jan 2023 14:11:18 -0800 Subject: [PATCH 13/39] Fix request ID impl for service error --- .../software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt | 1 + .../core/smithy/generators/error/ServiceErrorGenerator.kt | 2 ++ 2 files changed, 3 insertions(+) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt index 4b9c858452..28b48e4b4d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt @@ -126,6 +126,7 @@ abstract class BaseRequestIdDecorator : ClientCodegenDecorator { } } } + else -> {} } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt index dc5e63143a..7d350e92a1 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt @@ -25,6 +25,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.unhandledError import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.transformers.allErrors import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors @@ -77,6 +78,7 @@ class ServiceErrorGenerator( ) } rust("impl #T for Error {}", RuntimeType.StdError) + writeCustomizations(customizations, ErrorSection.ServiceErrorAdditionalTraitImpls(allErrors)) } crate.lib { rust("pub use error_meta::Error;") } } From 7eb87a7ff4aa5a0307a53d539aad70125547fb99 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 17 Jan 2023 17:38:43 -0800 Subject: [PATCH 14/39] Fix event stream marshall/unmarshall generators --- .../parse/EventStreamUnmarshallerGenerator.kt | 6 ++---- .../EventStreamErrorMarshallerGenerator.kt | 13 ++----------- .../codegen/core/testutil/EventStreamTestTools.kt | 6 +++--- .../testutil/EventStreamUnmarshallTestCases.kt | 14 +++++--------- .../codegen/server/smithy/ServerCodegenVisitor.kt | 3 +-- .../server/smithy/testutil/ServerTestHelpers.kt | 3 +-- .../generators/ServerBuilderGeneratorTest.kt | 3 +-- 7 files changed, 15 insertions(+), 33 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 9565a59cfb..46e33247a5 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -343,11 +343,9 @@ class EventStreamUnmarshallerGenerator( .map_err(|err| { #{Error}::unmarshalling(format!("failed to unmarshall ${member.memberName}: {}", err)) })?; + builder._set_meta(Some(generic)); return Ok(#{UnmarshalledMessage}::Error( - #{OpError}::new( - #{OpError}Kind::${member.target.name}(builder.build()), - generic, - ) + #{OpError}::${member.target.name}(builder.build()) )) """, "parser" to parser, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt index e4ec2cec3a..0b60632d2b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt @@ -96,20 +96,11 @@ class EventStreamErrorMarshallerGenerator( ) { rust("let mut headers = Vec::new();") addStringHeader(":message-type", """"exception".into()""") - val kind = when (target) { - CodegenTarget.CLIENT -> ".kind" - CodegenTarget.SERVER -> "" - } if (errorsShape.errorMembers.isEmpty()) { rust("let payload = Vec::new();") } else { - rustBlock("let payload = match _input$kind") { - val symbol = operationErrorSymbol - val errorName = when (target) { - CodegenTarget.CLIENT -> "${symbol}Kind" - CodegenTarget.SERVER -> "$symbol" - } - + rustBlock("let payload = match _input") { + val errorName = operationErrorSymbol.name errorsShape.errorMembers.forEach { error -> val errorSymbol = symbolProvider.toSymbol(error) val errorString = error.memberName diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt index 19c24e9c18..5ffbc60398 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt @@ -22,14 +22,15 @@ import software.amazon.smithy.rust.codegen.core.smithy.ErrorsModule import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule 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.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.testutil.EventStreamMarshallTestCases.writeMarshallTestCases import software.amazon.smithy.rust.codegen.core.testutil.EventStreamUnmarshallTestCases.writeUnmarshallTestCases +import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.core.util.outputShape @@ -126,8 +127,7 @@ object EventStreamTestTools { requirements.renderOperationError(this, model, symbolProvider, operationSymbol, errors) requirements.renderOperationError(this, model, symbolProvider, symbolProvider.toSymbol(unionShape), errors) for (shape in errors) { - StructureGenerator(model, symbolProvider, this, shape, emptyList()).render() - requirements.renderBuilderForShape(this, codegenContext, shape) + ErrorGenerator(model, symbolProvider, this, shape, shape.getTrait()!!, listOf()).render(codegenTarget) } } project.withModule(ModelsModule) { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt index bb27c724e0..e4e6bdb141 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt @@ -20,7 +20,7 @@ internal object EventStreamUnmarshallTestCases { """ use aws_smithy_eventstream::frame::{Header, HeaderValue, Message, UnmarshallMessage, UnmarshalledMessage}; use aws_smithy_types::{Blob, DateTime}; - use crate::error::*; + use crate::error::TestStreamError; use crate::model::*; fn msg( @@ -210,10 +210,6 @@ internal object EventStreamUnmarshallTestCases { """, ) - val (someError, kindSuffix) = when (codegenTarget) { - CodegenTarget.CLIENT -> "TestStreamErrorKind::SomeError" to ".kind" - CodegenTarget.SERVER -> "TestStreamError::SomeError" to "" - } unitTest( "some_error", """ @@ -225,8 +221,8 @@ internal object EventStreamUnmarshallTestCases { ); let result = ${format(generator)}().unmarshall(&message); assert!(result.is_ok(), "expected ok, got: {:?}", result); - match expect_error(result.unwrap())$kindSuffix { - $someError(err) => assert_eq!(Some("some error"), err.message()), + match expect_error(result.unwrap()) { + TestStreamError::SomeError(err) => assert_eq!(Some("some error"), err.message()), kind => panic!("expected SomeError, but got {:?}", kind), } """, @@ -244,8 +240,8 @@ internal object EventStreamUnmarshallTestCases { ); let result = ${format(generator)}().unmarshall(&message); assert!(result.is_ok(), "expected ok, got: {:?}", result); - match expect_error(result.unwrap())$kindSuffix { - TestStreamErrorKind::Unhandled(err) => { + match expect_error(result.unwrap()) { + TestStreamError::Unhandled(err) => { let message = format!("{}", aws_smithy_types::error::display::DisplayErrorContext(&err)); let expected = "message: \"unmodeled error\""; assert!(message.contains(expected), "Expected '{message}' to contain '{expected}'"); diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index d87e764e72..898fbf96f2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -32,7 +32,6 @@ import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.implBlock -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.ConstrainedModule import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule @@ -255,7 +254,7 @@ open class ServerCodegenVisitor( this, shape, codegenDecorator.structureCustomizations(codegenContext, emptyList()), - ).render(CodegenTarget.SERVER) + ).render() renderStructureShapeBuilder(shape, this) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt index 4452d6d017..3be216f92d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/testutil/ServerTestHelpers.kt @@ -13,7 +13,6 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.implBlock -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig @@ -118,7 +117,7 @@ fun serverTestCodegenContext( * In tests, we frequently need to generate a struct, a builder, and an impl block to access said builder. */ fun StructureShape.serverRenderWithModelBuilder(model: Model, symbolProvider: RustSymbolProvider, writer: RustWriter) { - StructureGenerator(model, symbolProvider, writer, this, emptyList()).render(CodegenTarget.SERVER) + StructureGenerator(model, symbolProvider, writer, this, emptyList()).render() val serverCodegenContext = serverTestCodegenContext(model) // Note that this always uses `ServerBuilderGenerator` and _not_ `ServerBuilderGeneratorWithoutPublicConstrainedTypes`, // regardless of the `publicConstrainedTypes` setting. diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt index cc3ba0193e..c728ee17f3 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerBuilderGeneratorTest.kt @@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.implBlock -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest @@ -43,7 +42,7 @@ class ServerBuilderGeneratorTest { writer, shape, emptyList(), - ).render(CodegenTarget.SERVER) + ).render() val builderGenerator = ServerBuilderGenerator(codegenContext, shape) builderGenerator.render(writer) writer.implBlock(codegenContext.symbolProvider.toSymbol(shape)) { From e9eee00bb6b86dc3df5e9f1acfdfea981c4118f5 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 17 Jan 2023 18:12:26 -0800 Subject: [PATCH 15/39] Fix `codegen-core` tests --- .../generators/StructureGeneratorTest.kt | 14 ------ .../generators/error/ErrorGeneratorTest.kt | 45 +++++++++++++++++++ .../error/ServiceErrorGeneratorTest.kt | 16 ++++++- 3 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGeneratorTest.kt diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt index 3c9787fd9b..370837bcf5 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt @@ -139,20 +139,6 @@ class StructureGeneratorTest { project.compileAndTest() } - @Test - fun `generate error structures`() { - val provider = testSymbolProvider(model) - val writer = RustWriter.forModule("error") - val generator = StructureGenerator(model, provider, writer, error, emptyList()) - generator.render() - writer.compileAndTest( - """ - let err = MyError { message: None }; - assert_eq!(err.retryable_error_kind(), aws_smithy_types::retry::ErrorKind::ServerError); - """, - ) - } - @Test fun `generate a custom debug implementation when the sensitive trait is applied to some members`() { val provider = testSymbolProvider(model) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGeneratorTest.kt new file mode 100644 index 0000000000..8c01ce5329 --- /dev/null +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGeneratorTest.kt @@ -0,0 +1,45 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.smithy.generators.error + +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.traits.ErrorTrait +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest +import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider +import software.amazon.smithy.rust.codegen.core.util.getTrait + +class ErrorGeneratorTest { + val model = + """ + namespace com.test + + @error("server") + @retryable + structure MyError { + message: String + } + """.asSmithyModel() + + @Test + fun `generate error structures`() { + val provider = testSymbolProvider(model) + val writer = RustWriter.forModule("error") + val errorShape = model.expectShape(ShapeId.from("com.test#MyError")) as StructureShape + val errorTrait = errorShape.getTrait()!! + ErrorGenerator(model, provider, writer, errorShape, errorTrait, emptyList()).render(CodegenTarget.CLIENT) + writer.compileAndTest( + """ + let err = MyError::builder().build(); + assert_eq!(err.retryable_error_kind(), aws_smithy_types::retry::ErrorKind::ServerError); + """, + ) + } +} diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt index 6d78b2db7f..edc24a88b9 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt @@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.AttributeKind import software.amazon.smithy.rust.codegen.core.rustlang.RustModule @@ -21,6 +22,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErr import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.generatePluginContext import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider +import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.runCommand import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.createDirectory @@ -97,7 +99,19 @@ internal class ServiceErrorGeneratorTest { } for (shape in model.structureShapes) { if (shape.id.namespace == "com.example") { - StructureGenerator(model, symbolProvider, this, shape, emptyList()).render() + val errorTrait = shape.getTrait() + if (errorTrait != null) { + ErrorGenerator( + model, + symbolProvider, + this, + shape, + errorTrait, + listOf(), + ).render(CodegenTarget.CLIENT) + } else { + StructureGenerator(model, symbolProvider, this, shape, emptyList()).render() + } } } } From d3c9c808904f1d2b009759cdc05aefef92f61f4c Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 18 Jan 2023 14:32:44 -0800 Subject: [PATCH 16/39] Refactor error generation to fix server tests --- .../client/smithy/ClientCodegenVisitor.kt | 7 +- .../client/smithy/RustClientCodegenPlugin.kt | 2 + .../customizations/ErrorMetadataDecorator.kt | 52 ++++++++ .../generators/error/ClientErrorGenerator.kt | 118 ++++++++++++++++++ .../protocol/ProtocolTestGenerator.kt | 2 +- .../error/ClientErrorGeneratorTest.kt | 60 +++++++++ .../ClientEventStreamBaseRequirements.kt | 19 +++ .../generators/error/ErrorCustomization.kt | 7 +- ...rrorGenerator.kt => ErrorImplGenerator.kt} | 71 +---------- .../error/OperationErrorGenerator.kt | 86 ++++++------- .../generators/error/ServiceErrorGenerator.kt | 21 ++-- .../EventStreamErrorMarshallerGenerator.kt | 6 +- .../core/testutil/EventStreamTestTools.kt | 7 +- ...ratorTest.kt => ErrorImplGeneratorTest.kt} | 14 ++- .../error/ServiceErrorGeneratorTest.kt | 16 ++- .../smithy/PythonServerCodegenVisitor.kt | 3 +- .../server/smithy/ServerCodegenVisitor.kt | 16 +++ .../ServerEventStreamBaseRequirements.kt | 20 +++ .../tests/simple_integration_test.rs | 15 +-- 19 files changed, 379 insertions(+), 163 deletions(-) create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ErrorMetadataDecorator.kt create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGenerator.kt create mode 100644 codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGeneratorTest.kt rename codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/{ErrorGenerator.kt => ErrorImplGenerator.kt} (69%) rename codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/{ErrorGeneratorTest.kt => ErrorImplGeneratorTest.kt} (67%) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index bb1802619e..700d650dc0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -21,13 +21,13 @@ import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ClientErrorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientProtocolLoader import software.amazon.smithy.rust.codegen.client.smithy.transformers.AddErrorMessage import software.amazon.smithy.rust.codegen.client.smithy.transformers.RemoveEventStreamOperations 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.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig @@ -35,7 +35,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerat import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.OperationErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory @@ -216,14 +215,14 @@ class ClientCodegenVisitor( } } else -> { - ErrorGenerator( + ClientErrorGenerator( model, symbolProvider, this, shape, errorTrait, codegenDecorator.errorCustomizations(codegenContext, emptyList()), - ).render(CodegenTarget.CLIENT) + ).render() } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt index 8750fa0c96..ed88ed9b8c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.codegen.core.ReservedWordSymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.customizations.ClientCustomizations +import software.amazon.smithy.rust.codegen.client.smithy.customizations.ErrorMetadataDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.NoOpEventStreamSigningDecorator @@ -58,6 +59,7 @@ class RustClientCodegenPlugin : DecoratableBuildPlugin() { FluentClientDecorator(), EndpointsDecorator(), NoOpEventStreamSigningDecorator(), + ErrorMetadataDecorator(), *decorator, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ErrorMetadataDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ErrorMetadataDecorator.kt new file mode 100644 index 0000000000..628ca07299 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ErrorMetadataDecorator.kt @@ -0,0 +1,52 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +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.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.genericError +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.delegateToVariants + +class ErrorMetadataDecorator : ClientCodegenDecorator { + override val name: String = "ErrorMetadataDecorator" + override val order: Byte = 0 + + override fun errorCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations + listOf( + object : ErrorCustomization() { + override fun section(section: ErrorSection): Writable = writable { + when (section) { + is ErrorSection.ServiceErrorAdditionalUnhandledErrorBuildFields -> { + rust(".meta(#T::meta(&err).clone())", RuntimeType.errorMetadataTrait(codegenContext.runtimeConfig)) + } + + is ErrorSection.OperationErrorAdditionalTraitImpls -> { + rustBlock( + "impl #T for ${section.errorType.name}", + RuntimeType.errorMetadataTrait(codegenContext.runtimeConfig), + ) { + rustBlock("fn meta(&self) -> &#T", genericError(codegenContext.runtimeConfig)) { + delegateToVariants(codegenContext.symbolProvider, section.allErrors) { + writable { rust("_inner.meta()") } + } + } + } + } + } + } + }, + ) +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGenerator.kt new file mode 100644 index 0000000000..7463946bd6 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGenerator.kt @@ -0,0 +1,118 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators.error + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.traits.ErrorTrait +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +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.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.genericError +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorSection + +class ClientErrorGenerator( + private val model: Model, + private val symbolProvider: RustSymbolProvider, + private val writer: RustWriter, + private val shape: StructureShape, + private val error: ErrorTrait, + private val customizations: List, +) { + private val runtimeConfig = symbolProvider.config().runtimeConfig + + fun render() { + val symbol = symbolProvider.toSymbol(shape) + + StructureGenerator( + model, symbolProvider, writer, shape, + listOf(object : StructureCustomization() { + override fun section(section: StructureSection): Writable = writable { + when (section) { + is StructureSection.AdditionalFields -> { + rust("pub(crate) _meta: #T,", RuntimeType.genericError(runtimeConfig)) + } + is StructureSection.AdditionalDebugFields -> { + rust("""${section.formatterName}.field("_meta", &self._meta);""") + } + } + } + }, + ), + ).render() + + BuilderGenerator( + model, symbolProvider, shape, + listOf( + object : BuilderCustomization() { + override fun section(section: BuilderSection): Writable = writable { + when (section) { + is BuilderSection.AdditionalFields -> { + rust("_meta: Option<#T>,", RuntimeType.genericError(runtimeConfig)) + } + + is BuilderSection.AdditionalMethods -> { + rustTemplate( + """ + pub(crate) fn _meta(mut self, _meta: #{generic_error}) -> Self { + self._meta = Some(_meta); + self + } + + pub(crate) fn _set_meta(&mut self, _meta: Option<#{generic_error}>) -> &mut Self { + self._meta = _meta; + self + } + """, + "generic_error" to RuntimeType.genericError(runtimeConfig), + ) + } + + is BuilderSection.AdditionalFieldsInBuild -> { + rust("_meta: self._meta.unwrap_or_default(),") + } + } + } + }, + ), + ).let { builderGen -> + writer.implBlock(symbol) { + builderGen.renderConvenienceMethod(this) + } + builderGen.render(writer) + } + + ErrorImplGenerator( + model, symbolProvider, writer, shape, error, + listOf( + object : ErrorCustomization() { + override fun section(section: ErrorSection): Writable = writable { + if (section is ErrorSection.ErrorAdditionalTraitImpls) { + rustBlock("impl #T for ${symbol.name}", RuntimeType.errorMetadataTrait(runtimeConfig)) { + rust("fn meta(&self) -> &#T { &self._meta }", genericError(runtimeConfig)) + } + } + } + }, + ) + customizations, + ).render(CodegenTarget.CLIENT) + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index 98d229558e..7b83d5094b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -304,7 +304,7 @@ class ProtocolTestGenerator( val errorSymbol = operationShape.errorSymbol(codegenContext.symbolProvider) val errorVariant = codegenContext.symbolProvider.toSymbol(expectedShape).name rust("""let parsed = parsed.expect_err("should be error response");""") - rustBlock("if let #TKind::$errorVariant(actual_error) = parsed.kind", errorSymbol) { + rustBlock("if let #T::$errorVariant(actual_error) = parsed", errorSymbol) { rustTemplate("#{AssertEq}(expected_output, actual_error);", *codegenScope) } rustBlock("else") { diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGeneratorTest.kt new file mode 100644 index 0000000000..e737223ab2 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGeneratorTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators.error + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel + +class ClientErrorGeneratorTest { + val model = + """ + namespace com.test + use aws.protocols#awsJson1_1 + + @awsJson1_1 + service TestService { + operations: [TestOp] + } + + operation TestOp { + errors: [MyError] + } + + @error("server") + @retryable + structure MyError { + message: String + } + """.asSmithyModel() + + @Test + fun `generate error structure and builder`() { + clientIntegrationTest(model) { _, rustCrate -> + rustCrate.withFile("src/error.rs") { + rust( + """ + ##[test] + fn test_error_generator() { + use aws_smithy_types::error::{Error as GenericError, ErrorMetadata}; + use aws_smithy_types::retry::ErrorKind; + + let err = MyError::builder() + ._meta(GenericError::builder().code("test").message("testmsg").build()) + .message("testmsg") + .build(); + assert_eq!(err.retryable_error_kind(), ErrorKind::ServerError); + assert_eq!("test", err.meta().code().unwrap()); + assert_eq!("testmsg", err.meta().message().unwrap()); + assert_eq!("testmsg", err.message().unwrap()); + } + """, + ) + } + } + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt index 84b59bc28a..b993a06efe 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt @@ -13,8 +13,10 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ClientErrorGenerator import software.amazon.smithy.rust.codegen.client.testutil.clientTestRustSettings import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -25,6 +27,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerat import software.amazon.smithy.rust.codegen.core.smithy.generators.error.OperationErrorGenerator import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestModels import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestRequirements +import software.amazon.smithy.rust.codegen.core.util.expectTrait import java.util.stream.Stream class TestCasesProvider : ArgumentsProvider { @@ -69,4 +72,20 @@ abstract class ClientEventStreamBaseRequirements : EventStreamTestRequirements() + ClientErrorGenerator( + codegenContext.model, + codegenContext.symbolProvider, + writer, + shape, + errorTrait, + emptyList(), + ).render() + } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt index 786a9e798e..8d42c89d3d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt @@ -14,12 +14,17 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.Section /** Error customization sections */ sealed class ErrorSection(name: String) : Section(name) { /** Use this section to add additional trait implementations to the generated operation errors */ - data class OperationErrorAdditionalTraitImpls(val errorType: RuntimeType) : ErrorSection("OperationErrorAdditionalTraitImpls") + data class OperationErrorAdditionalTraitImpls(val errorType: RuntimeType, val allErrors: List) : + ErrorSection("OperationErrorAdditionalTraitImpls") /** Use this section to add additional trait implementations to the generated service error */ class ServiceErrorAdditionalTraitImpls(val allErrors: List) : ErrorSection("ServiceErrorAdditionalTraitImpls") + /** Use this section to add additional fields to unhandled error construction */ + class ServiceErrorAdditionalUnhandledErrorBuildFields(errorVariableName: String) : + ErrorSection("ServiceErrorAdditionalUnhandledErrorBuildFields") + /** Use this section to add additional trait implementations to the generated error structures */ class ErrorAdditionalTraitImpls(val errorType: Symbol) : ErrorSection("ErrorAdditionalTraitImpls") } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt similarity index 69% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt rename to codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt index 36d7057237..1fc94f79e5 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt @@ -13,26 +13,17 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asDeref -import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.rustlang.render 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.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget 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.Companion.StdError -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.genericError import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations -import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.mapRustType import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.ValueExpression @@ -79,7 +70,7 @@ fun StructureShape.modeledRetryKind(errorTrait: ErrorTrait): ErrorKind? { } } -class ErrorGenerator( +class ErrorImplGenerator( private val model: Model, private val symbolProvider: RustSymbolProvider, private val writer: RustWriter, @@ -91,62 +82,6 @@ class ErrorGenerator( fun render(forWhom: CodegenTarget = CodegenTarget.CLIENT) { val symbol = symbolProvider.toSymbol(shape) - - StructureGenerator( - model, symbolProvider, writer, shape, - listOf(object : StructureCustomization() { - override fun section(section: StructureSection): Writable = writable { - when (section) { - is StructureSection.AdditionalFields -> { - rust("pub(crate) _meta: #T,", genericError(runtimeConfig)) - } - is StructureSection.AdditionalDebugFields -> { - rust("""${section.formatterName}.field("_meta", &self._meta);""") - } - } - } - }, - ), - ).render() - - BuilderGenerator( - model, symbolProvider, shape, - listOf(object : BuilderCustomization() { - override fun section(section: BuilderSection): Writable = writable { - when (section) { - is BuilderSection.AdditionalFields -> { - rust("_meta: Option<#T>,", genericError(runtimeConfig)) - } - is BuilderSection.AdditionalMethods -> { - rustTemplate( - """ - pub(crate) fn _meta(mut self, _meta: #{generic_error}) -> Self { - self._meta = Some(_meta); - self - } - - pub(crate) fn _set_meta(&mut self, _meta: Option<#{generic_error}>) -> &mut Self { - self._meta = _meta; - self - } - """, - "generic_error" to genericError(runtimeConfig), - ) - } - is BuilderSection.AdditionalFieldsInBuild -> { - rust("_meta: self._meta.unwrap_or_default(),") - } - } - } - }, - ), - ).let { builderGen -> - writer.implBlock(symbol) { - builderGen.renderConvenienceMethod(this) - } - builderGen.render(writer) - } - val messageShape = shape.errorMessageMember() val errorKindT = RuntimeType.retryErrorKind(runtimeConfig) writer.rustBlock("impl ${symbol.name}") { @@ -225,10 +160,6 @@ class ErrorGenerator( writer.write("impl #T for ${symbol.name} {}", StdError) - writer.rustBlock("impl #T for ${symbol.name}", RuntimeType.errorMetadataTrait(runtimeConfig)) { - rust("fn meta(&self) -> &#T { &self._meta }", genericError(runtimeConfig)) - } - writer.writeCustomizations(customizations, ErrorSection.ErrorAdditionalTraitImpls(symbol)) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt index 650f18d01d..feee563039 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt @@ -127,20 +127,13 @@ class OperationErrorGenerator( } writer.rustBlock("impl #T for ${errorType.name}", RuntimeType.Display) { rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { - delegateToVariants(errors) { + delegateToVariants(symbolProvider, errors) { writable { rust("_inner.fmt(f)") } } } } - writer.rustBlock("impl #T for ${errorType.name}", RuntimeType.errorMetadataTrait(runtimeConfig)) { - rustBlock("fn meta(&self) -> &#T", genericError(runtimeConfig)) { - delegateToVariants(errors) { - writable { rust("_inner.meta()") } - } - } - } - writer.writeCustomizations(customizations, ErrorSection.OperationErrorAdditionalTraitImpls(errorType)) + writer.writeCustomizations(customizations, ErrorSection.OperationErrorAdditionalTraitImpls(errorType, errors)) val retryErrorKindT = RuntimeType.retryErrorKind(symbolProvider.config().runtimeConfig) writer.rustBlock( @@ -212,7 +205,7 @@ class OperationErrorGenerator( writer.rustBlock("impl #T for ${errorType.name}", RuntimeType.StdError) { rustBlock("fn source(&self) -> Option<&(dyn #T + 'static)>", RuntimeType.StdError) { - delegateToVariants(errors) { + delegateToVariants(symbolProvider, errors) { writable { rust("Some(_inner)") } @@ -220,44 +213,45 @@ class OperationErrorGenerator( } } } +} - private sealed class VariantMatch(name: String) : Section(name) { - object Unhandled : VariantMatch("Unhandled") - data class Modeled(val symbol: Symbol, val shape: Shape) : VariantMatch("Modeled") - } +sealed class VariantMatch(name: String) : Section(name) { + object Unhandled : VariantMatch("Unhandled") + data class Modeled(val symbol: Symbol, val shape: Shape) : VariantMatch("Modeled") +} - /** - * Generates code to delegate behavior to the variants, for example: - * - * ```rust - * match self { - * Self::InvalidGreeting(_inner) => inner.fmt(f), - * Self::ComplexError(_inner) => inner.fmt(f), - * Self::FooError(_inner) => inner.fmt(f), - * Self::Unhandled(_inner) => _inner.fmt(f), - * } - * ``` - * - * [handler] is passed an instance of [VariantMatch]—a [writable] should be returned containing the content to be - * written for this variant. - * - * The field will always be bound as `_inner`. - */ - private fun RustWriter.delegateToVariants( - errors: List, - handler: (VariantMatch) -> Writable, - ) { - rustBlock("match self") { - errors.forEach { - val errorSymbol = symbolProvider.toSymbol(it) - rust("""Self::${errorSymbol.name}(_inner) => """) - handler(VariantMatch.Modeled(errorSymbol, it))(this) - write(",") - } - val unhandledHandler = handler(VariantMatch.Unhandled) - rustBlock("Self::Unhandled(_inner) =>") { - unhandledHandler(this) - } +/** + * Generates code to delegate behavior to the variants, for example: + * + * ```rust + * match self { + * Self::InvalidGreeting(_inner) => inner.fmt(f), + * Self::ComplexError(_inner) => inner.fmt(f), + * Self::FooError(_inner) => inner.fmt(f), + * Self::Unhandled(_inner) => _inner.fmt(f), + * } + * ``` + * + * [handler] is passed an instance of [VariantMatch]—a [writable] should be returned containing the content to be + * written for this variant. + * + * The field will always be bound as `_inner`. + */ +fun RustWriter.delegateToVariants( + symbolProvider: RustSymbolProvider, + errors: List, + handler: (VariantMatch) -> Writable, +) { + rustBlock("match self") { + errors.forEach { + val errorSymbol = symbolProvider.toSymbol(it) + rust("""Self::${errorSymbol.name}(_inner) => """) + handler(VariantMatch.Modeled(errorSymbol, it))(this) + write(",") + } + val unhandledHandler = handler(VariantMatch.Unhandled) + rustBlock("Self::Unhandled(_inner) =>") { + unhandledHandler(this) } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt index 7d350e92a1..5d3bb0d752 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt @@ -20,6 +20,7 @@ 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.withBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType @@ -111,16 +112,16 @@ class ServiceErrorGenerator( ) { rustBlock("match err") { rust("#T::ServiceError(context) => Self::from(context.into_err()),", sdkError) - rustTemplate( - """ - _ => { - use #{ErrorMetadata}; - Error::Unhandled(#{Unhandled}::builder().meta(err.meta().clone()).source(err).build()) - } - """, - "ErrorMetadata" to RuntimeType.errorMetadataTrait(codegenContext.runtimeConfig), - "Unhandled" to unhandledError(codegenContext.runtimeConfig), - ) + withBlock( + "_ => Error::Unhandled(#T::builder()", + ".source(err).build()),", + unhandledError(codegenContext.runtimeConfig), + ) { + writeCustomizations( + customizations, + ErrorSection.ServiceErrorAdditionalUnhandledErrorBuildFields("err"), + ) + } } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt index 0b60632d2b..696dcc2311 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt @@ -100,12 +100,11 @@ class EventStreamErrorMarshallerGenerator( rust("let payload = Vec::new();") } else { rustBlock("let payload = match _input") { - val errorName = operationErrorSymbol.name errorsShape.errorMembers.forEach { error -> val errorSymbol = symbolProvider.toSymbol(error) val errorString = error.memberName val target = model.expectShape(error.target, StructureShape::class.java) - rustBlock("$errorName::${errorSymbol.name}(inner) => ") { + rustBlock("#T::${errorSymbol.name}(inner) => ", operationErrorSymbol) { addStringHeader(":exception-type", "${errorString.dq()}.into()") renderMarshallEvent(error, target) } @@ -113,11 +112,12 @@ class EventStreamErrorMarshallerGenerator( if (target.renderUnknownVariant()) { rustTemplate( """ - $errorName::Unhandled(_inner) => return Err( + #{OperationError}::Unhandled(_inner) => return Err( #{Error}::marshalling(${unknownVariantError(unionSymbol.rustType().name).dq()}.to_owned()) ), """, *codegenScope, + "OperationError" to operationErrorSymbol, ) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt index 5ffbc60398..ea09d8a7a1 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamTestTools.kt @@ -23,14 +23,12 @@ import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule 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.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.testutil.EventStreamMarshallTestCases.writeMarshallTestCases import software.amazon.smithy.rust.codegen.core.testutil.EventStreamUnmarshallTestCases.writeUnmarshallTestCases -import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.core.util.outputShape @@ -80,6 +78,9 @@ interface EventStreamTestRequirements { operationSymbol: Symbol, errors: List, ) + + /** Render an error struct and builder */ + fun renderError(writer: RustWriter, codegenContext: C, shape: StructureShape) } object EventStreamTestTools { @@ -127,7 +128,7 @@ object EventStreamTestTools { requirements.renderOperationError(this, model, symbolProvider, operationSymbol, errors) requirements.renderOperationError(this, model, symbolProvider, symbolProvider.toSymbol(unionShape), errors) for (shape in errors) { - ErrorGenerator(model, symbolProvider, this, shape, shape.getTrait()!!, listOf()).render(codegenTarget) + requirements.renderError(this, codegenContext, shape) } } project.withModule(ModelsModule) { diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGeneratorTest.kt similarity index 67% rename from codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGeneratorTest.kt rename to codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGeneratorTest.kt index 8c01ce5329..38960810d5 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGeneratorTest.kt @@ -10,13 +10,16 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.util.getTrait -class ErrorGeneratorTest { +class ErrorImplGeneratorTest { val model = """ namespace com.test @@ -34,7 +37,14 @@ class ErrorGeneratorTest { val writer = RustWriter.forModule("error") val errorShape = model.expectShape(ShapeId.from("com.test#MyError")) as StructureShape val errorTrait = errorShape.getTrait()!! - ErrorGenerator(model, provider, writer, errorShape, errorTrait, emptyList()).render(CodegenTarget.CLIENT) + StructureGenerator(model, provider, writer, errorShape, emptyList()).render() + BuilderGenerator(model, provider, errorShape, emptyList()).let { builderGen -> + writer.implBlock(provider.toSymbol(errorShape)) { + builderGen.renderConvenienceMethod(writer) + } + builderGen.render(writer) + } + ErrorImplGenerator(model, provider, writer, errorShape, errorTrait, emptyList()).render(CodegenTarget.CLIENT) writer.compileAndTest( """ let err = MyError::builder().build(); diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt index edc24a88b9..6c1322170c 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt @@ -16,14 +16,14 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings -import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors +import software.amazon.smithy.rust.codegen.core.testutil.TestWriterDelegator import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.generatePluginContext import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.util.getTrait -import software.amazon.smithy.rust.codegen.core.util.runCommand import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.createDirectory import kotlin.io.path.writeText @@ -76,7 +76,7 @@ internal class ServiceErrorGeneratorTest { CodegenTarget.CLIENT, ) - val rustCrate = RustCrate( + val rustCrate = TestWriterDelegator( pluginContext.fileManifest, symbolProvider, codegenContext.settings.codegenConfig, @@ -99,9 +99,11 @@ internal class ServiceErrorGeneratorTest { } for (shape in model.structureShapes) { if (shape.id.namespace == "com.example") { + StructureGenerator(model, symbolProvider, this, shape, emptyList()).render() + val errorTrait = shape.getTrait() if (errorTrait != null) { - ErrorGenerator( + ErrorImplGenerator( model, symbolProvider, this, @@ -109,8 +111,6 @@ internal class ServiceErrorGeneratorTest { errorTrait, listOf(), ).render(CodegenTarget.CLIENT) - } else { - StructureGenerator(model, symbolProvider, this, shape, emptyList()).render() } } } @@ -127,8 +127,6 @@ internal class ServiceErrorGeneratorTest { } """, ) - rustCrate.finalize(settings, model, emptyMap(), emptyList(), false) - - "cargo test".runCommand(testDir) + rustCrate.compileAndTest() } } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index 00ea40dc7a..91251f0b64 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -16,7 +16,6 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEnumGenerator @@ -116,7 +115,7 @@ class PythonServerCodegenVisitor( rustCrate.useShapeWriter(shape) { // Use Python specific structure generator that adds the #[pyclass] attribute // and #[pymethods] implementation. - PythonServerStructureGenerator(model, codegenContext.symbolProvider, this, shape).render(CodegenTarget.SERVER) + PythonServerStructureGenerator(model, codegenContext.symbolProvider, this, shape).render() renderStructureShapeBuilder(shape, this) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 898fbf96f2..97c18e0cfd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -27,11 +27,13 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.implBlock +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.ConstrainedModule import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule @@ -40,6 +42,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.core.smithy.UnconstrainedModule import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer @@ -48,6 +51,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.transformers.RecursiveSha import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasEventStreamMember import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.isEventStream @@ -257,6 +261,18 @@ open class ServerCodegenVisitor( ).render() renderStructureShapeBuilder(shape, this) + + val errorTrait = shape.getTrait() + if (errorTrait != null) { + ErrorImplGenerator( + model, + codegenContext.symbolProvider, + this, + shape, + errorTrait, + codegenDecorator.errorCustomizations(codegenContext, emptyList()), + ).render(CodegenTarget.SERVER) + } } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamBaseRequirements.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamBaseRequirements.kt index 25b56033fb..13c83cfa4c 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamBaseRequirements.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/eventstream/ServerEventStreamBaseRequirements.kt @@ -17,8 +17,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestModels import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestRequirements +import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenConfig import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator @@ -93,4 +96,21 @@ abstract class ServerEventStreamBaseRequirements : EventStreamTestRequirements Date: Thu, 19 Jan 2023 17:33:24 -0800 Subject: [PATCH 17/39] Move error generators into `codegen-client` and fix tests --- .../smithy/rustsdk/AwsPresigningDecorator.kt | 2 +- .../smithy/rustsdk/BaseRequestIdDecorator.kt | 20 ++- .../customize/ServiceSpecificDecorator.kt | 10 +- .../lambda/tests/request_id.rs | 4 +- .../sts/tests/retry_idp_comms_err.rs | 21 ++- .../transcribestreaming/tests/test.rs | 9 +- .../client/smithy/ClientCodegenVisitor.kt | 10 +- .../client/smithy/RustClientCodegenPlugin.kt | 2 - .../customizations/ErrorMetadataDecorator.kt | 52 ------- .../customize/ClientCodegenDecorator.kt | 16 +++ .../smithy/generators/PaginatorGenerator.kt | 2 +- .../smithy/generators/ServiceGenerator.kt | 2 +- .../client/FluentClientGenerator.kt | 2 +- .../generators/error/ErrorCustomization.kt | 10 +- ...entErrorGenerator.kt => ErrorGenerator.kt} | 38 ++--- .../error/OperationErrorGenerator.kt | 94 +++++++------ .../generators/error/ServiceErrorGenerator.kt | 26 ++-- .../protocol/ProtocolTestGenerator.kt | 72 +++++----- .../protocols/HttpBoundProtocolGenerator.kt | 2 +- .../smithy/transformers/AddErrorMessage.kt | 4 +- ...GeneratorTest.kt => ErrorGeneratorTest.kt} | 2 +- .../error/OperationErrorGeneratorTest.kt | 4 +- .../error/ServiceErrorGeneratorTest.kt | 63 +++++++++ .../protocol/ProtocolTestGeneratorTest.kt | 5 +- .../ClientEventStreamBaseRequirements.kt | 6 +- .../core/smithy/EventStreamSymbolProvider.kt | 8 +- .../smithy/customize/CoreCodegenDecorator.kt | 32 ++--- .../generators/error/ErrorImplGenerator.kt | 16 ++- .../parse/EventStreamUnmarshallerGenerator.kt | 2 +- .../EventStreamErrorMarshallerGenerator.kt | 2 +- .../error/ServiceErrorGeneratorTest.kt | 132 ------------------ .../PythonServerOperationErrorGenerator.kt | 2 +- .../server/smithy/ServerCodegenVisitor.kt | 4 +- .../smithy/generators/DocHandlerGenerator.kt | 1 - .../ServerOperationErrorGenerator.kt | 17 +++ .../ServerOperationHandlerGenerator.kt | 1 - .../ServerOperationRegistryGenerator.kt | 1 - .../ServerHttpBoundProtocolGenerator.kt | 2 +- gradle.properties | 3 + 39 files changed, 318 insertions(+), 383 deletions(-) delete mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ErrorMetadataDecorator.kt rename {codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core => codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client}/smithy/generators/error/ErrorCustomization.kt (66%) rename codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/{ClientErrorGenerator.kt => ErrorGenerator.kt} (74%) rename {codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core => codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client}/smithy/generators/error/OperationErrorGenerator.kt (82%) rename {codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core => codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client}/smithy/generators/error/ServiceErrorGenerator.kt (89%) rename codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/{ClientErrorGeneratorTest.kt => ErrorGeneratorTest.kt} (98%) rename {codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core => codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client}/smithy/generators/error/OperationErrorGeneratorTest.kt (96%) create mode 100644 codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt delete mode 100644 codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index c733138076..1300ec6815 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -20,6 +20,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -35,7 +36,6 @@ 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.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.util.cloneOperation import software.amazon.smithy.rust.codegen.core.util.expectTrait diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt index 28b48e4b4d..b943eb770c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt @@ -8,6 +8,8 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorSection import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -22,8 +24,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomi import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorSection +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplSection import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -48,6 +50,11 @@ abstract class BaseRequestIdDecorator : ClientCodegenDecorator { ): List = baseCustomizations + listOf(RequestIdErrorCustomization(codegenContext)) + override fun errorImplCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + listOf(RequestIdErrorImplCustomization(codegenContext)) + override fun structureCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -117,8 +124,15 @@ abstract class BaseRequestIdDecorator : ClientCodegenDecorator { } } } + } + } + } - is ErrorSection.ErrorAdditionalTraitImpls -> { + private inner class RequestIdErrorImplCustomization(private val codegenContext: ClientCodegenContext) : + ErrorImplCustomization() { + override fun section(section: ErrorImplSection): Writable = writable { + when (section) { + is ErrorImplSection.ErrorAdditionalTraitImpls -> { rustBlock("impl #1T for #2T", accessorTrait(codegenContext), section.errorType) { rustBlock("fn $accessorFunctionName(&self) -> Option<&str>") { rust("use #T;", RuntimeType.errorMetadataTrait(codegenContext.runtimeConfig)) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index 8490265ed7..8e957b3f59 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -15,13 +15,14 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientProtocolMap import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplCustomization /** Only apply this decorator to the given service ID */ fun ClientCodegenDecorator.onlyApplyTo(serviceId: String): List = @@ -85,6 +86,13 @@ class ServiceSpecificDecorator( delegateTo.errorCustomizations(codegenContext, baseCustomizations) } + override fun errorImplCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.errorImplCustomizations(codegenContext, baseCustomizations) + } + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { maybeApply(codegenContext.serviceShape) { delegateTo.extras(codegenContext, rustCrate) diff --git a/aws/sdk/integration-tests/lambda/tests/request_id.rs b/aws/sdk/integration-tests/lambda/tests/request_id.rs index 34fdba0d44..ab3ede5f0a 100644 --- a/aws/sdk/integration-tests/lambda/tests/request_id.rs +++ b/aws/sdk/integration-tests/lambda/tests/request_id.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_lambda::error::ListFunctionsErrorKind; +use aws_sdk_lambda::error::ListFunctionsError; use aws_sdk_lambda::operation::ListFunctions; use aws_sdk_lambda::types::RequestId; use aws_smithy_http::response::ParseHttpResponse; @@ -20,7 +20,7 @@ fn get_request_id_from_unmodeled_error() { let err = ListFunctions::new() .parse_loaded(&resp.map(Bytes::from)) .expect_err("status was 500, this is an error"); - assert!(matches!(err.kind, ListFunctionsErrorKind::Unhandled(_))); + assert!(matches!(err, ListFunctionsError::Unhandled(_))); assert_eq!(Some("correct-request-id"), err.request_id()); assert_eq!(Some("correct-request-id"), err.meta().request_id()); } diff --git a/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs b/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs index 6fe9895cd3..03d80d3361 100644 --- a/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs +++ b/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs @@ -6,22 +6,19 @@ use aws_sdk_sts as sts; use aws_smithy_types::error::Error as ErrorMeta; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; -use sts::error::{ - AssumeRoleWithWebIdentityError, AssumeRoleWithWebIdentityErrorKind, - IdpCommunicationErrorException, -}; +use sts::error::{AssumeRoleWithWebIdentityError, IdpCommunicationErrorException}; #[tokio::test] async fn idp_comms_err_retryable() { - let error = AssumeRoleWithWebIdentityError::new( - AssumeRoleWithWebIdentityErrorKind::IdpCommunicationErrorException( - IdpCommunicationErrorException::builder() - .message("test") - .build(), - ), - ErrorMeta::builder() - .code("IDPCommunicationError") + let error = AssumeRoleWithWebIdentityError::IdpCommunicationErrorException( + IdpCommunicationErrorException::builder() .message("test") + ._meta( + ErrorMeta::builder() + .code("IDPCommunicationError") + .message("test") + .build(), + ) .build(), ); assert_eq!( diff --git a/aws/sdk/integration-tests/transcribestreaming/tests/test.rs b/aws/sdk/integration-tests/transcribestreaming/tests/test.rs index f48515038a..fe6b028820 100644 --- a/aws/sdk/integration-tests/transcribestreaming/tests/test.rs +++ b/aws/sdk/integration-tests/transcribestreaming/tests/test.rs @@ -4,9 +4,7 @@ */ use async_stream::stream; -use aws_sdk_transcribestreaming::error::{ - AudioStreamError, TranscriptResultStreamError, TranscriptResultStreamErrorKind, -}; +use aws_sdk_transcribestreaming::error::{AudioStreamError, TranscriptResultStreamError}; use aws_sdk_transcribestreaming::model::{ AudioEvent, AudioStream, LanguageCode, MediaEncoding, TranscriptResultStream, }; @@ -76,10 +74,7 @@ async fn test_error() { match output.transcript_result_stream.recv().await { Err(SdkError::ServiceError(context)) => match context.err() { - TranscriptResultStreamError { - kind: TranscriptResultStreamErrorKind::BadRequestException(err), - .. - } => { + TranscriptResultStreamError::BadRequestException(err) => { assert_eq!( Some("A complete signal was sent without the preceding empty frame."), err.message() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index 700d650dc0..b07cf20720 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -21,7 +21,8 @@ import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ClientErrorGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.OperationErrorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientProtocolLoader import software.amazon.smithy.rust.codegen.client.smithy.transformers.AddErrorMessage @@ -31,12 +32,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.core.smithy.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.OperationErrorGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer @@ -215,13 +215,13 @@ class ClientCodegenVisitor( } } else -> { - ClientErrorGenerator( + ErrorGenerator( model, symbolProvider, this, shape, errorTrait, - codegenDecorator.errorCustomizations(codegenContext, emptyList()), + codegenDecorator.errorImplCustomizations(codegenContext, emptyList()), ).render() } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt index ed88ed9b8c..8750fa0c96 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt @@ -10,7 +10,6 @@ import software.amazon.smithy.codegen.core.ReservedWordSymbolProvider import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.customizations.ClientCustomizations -import software.amazon.smithy.rust.codegen.client.smithy.customizations.ErrorMetadataDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.NoOpEventStreamSigningDecorator @@ -59,7 +58,6 @@ class RustClientCodegenPlugin : DecoratableBuildPlugin() { FluentClientDecorator(), EndpointsDecorator(), NoOpEventStreamSigningDecorator(), - ErrorMetadataDecorator(), *decorator, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ErrorMetadataDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ErrorMetadataDecorator.kt deleted file mode 100644 index 628ca07299..0000000000 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ErrorMetadataDecorator.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.client.smithy.customizations - -import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.core.rustlang.Writable -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.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.genericError -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorSection -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.delegateToVariants - -class ErrorMetadataDecorator : ClientCodegenDecorator { - override val name: String = "ErrorMetadataDecorator" - override val order: Byte = 0 - - override fun errorCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List = - baseCustomizations + listOf( - object : ErrorCustomization() { - override fun section(section: ErrorSection): Writable = writable { - when (section) { - is ErrorSection.ServiceErrorAdditionalUnhandledErrorBuildFields -> { - rust(".meta(#T::meta(&err).clone())", RuntimeType.errorMetadataTrait(codegenContext.runtimeConfig)) - } - - is ErrorSection.OperationErrorAdditionalTraitImpls -> { - rustBlock( - "impl #T for ${section.errorType.name}", - RuntimeType.errorMetadataTrait(codegenContext.runtimeConfig), - ) { - rustBlock("fn meta(&self) -> &#T", genericError(codegenContext.runtimeConfig)) { - delegateToVariants(codegenContext.symbolProvider, section.allErrors) { - writable { rust("_inner.meta()") } - } - } - } - } - } - } - }, - ) -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt index f4034c1b9b..c893ff078f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator import software.amazon.smithy.rust.codegen.core.smithy.customize.CombinedCoreCodegenDecorator import software.amazon.smithy.rust.codegen.core.smithy.customize.CoreCodegenDecorator @@ -40,6 +41,14 @@ interface ClientCodegenDecorator : CoreCodegenDecorator { baseCustomizations: List, ): List = baseCustomizations + /** + * Hook to customize generated errors. + */ + fun errorCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + fun protocols(serviceId: ShapeId, currentProtocols: ClientProtocolMap): ClientProtocolMap = currentProtocols fun endpointCustomizations(codegenContext: ClientCodegenContext): List = listOf() @@ -72,6 +81,13 @@ open class CombinedClientCodegenDecorator(decorators: List, + ): List = combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.errorCustomizations(codegenContext, customizations) + } + override fun protocols(serviceId: ShapeId, currentProtocols: ClientProtocolMap): ClientProtocolMap = combineCustomizations(currentProtocols) { decorator, protocolMap -> decorator.protocols(serviceId, protocolMap) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt index 54982643e8..e8e649b535 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.traits.IdempotencyTokenTrait import software.amazon.smithy.model.traits.PaginatedTrait import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerics +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -24,7 +25,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext 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.generators.builderSymbol -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.findMemberWithTrait diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index 46e98cd755..ee82febf9c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -9,12 +9,12 @@ import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfigGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ServiceErrorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ServiceErrorGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.util.inputShape diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index a1e1c73957..c3603791e9 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.DocumentationTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.generators.PaginatorGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.client.smithy.generators.isPaginated import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.derive @@ -44,7 +45,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.inputShape diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorCustomization.kt similarity index 66% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt rename to codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorCustomization.kt index 8d42c89d3d..2cf1b62000 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorCustomization.kt @@ -3,9 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.core.smithy.generators.error +package software.amazon.smithy.rust.codegen.client.smithy.generators.error -import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedSectionGenerator @@ -20,13 +19,6 @@ sealed class ErrorSection(name: String) : Section(name) { /** Use this section to add additional trait implementations to the generated service error */ class ServiceErrorAdditionalTraitImpls(val allErrors: List) : ErrorSection("ServiceErrorAdditionalTraitImpls") - - /** Use this section to add additional fields to unhandled error construction */ - class ServiceErrorAdditionalUnhandledErrorBuildFields(errorVariableName: String) : - ErrorSection("ServiceErrorAdditionalUnhandledErrorBuildFields") - - /** Use this section to add additional trait implementations to the generated error structures */ - class ErrorAdditionalTraitImpls(val errorType: Symbol) : ErrorSection("ErrorAdditionalTraitImpls") } /** Customizations for generated errors */ diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt similarity index 74% rename from codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGenerator.kt rename to codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt index 7463946bd6..5c7c9a8855 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt @@ -25,17 +25,16 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorSection -class ClientErrorGenerator( +class ErrorGenerator( private val model: Model, private val symbolProvider: RustSymbolProvider, private val writer: RustWriter, private val shape: StructureShape, private val error: ErrorTrait, - private val customizations: List, + private val implCustomizations: List, ) { private val runtimeConfig = symbolProvider.config().runtimeConfig @@ -48,7 +47,7 @@ class ClientErrorGenerator( override fun section(section: StructureSection): Writable = writable { when (section) { is StructureSection.AdditionalFields -> { - rust("pub(crate) _meta: #T,", RuntimeType.genericError(runtimeConfig)) + rust("pub(crate) _meta: #T,", genericError(runtimeConfig)) } is StructureSection.AdditionalDebugFields -> { rust("""${section.formatterName}.field("_meta", &self._meta);""") @@ -66,23 +65,25 @@ class ClientErrorGenerator( override fun section(section: BuilderSection): Writable = writable { when (section) { is BuilderSection.AdditionalFields -> { - rust("_meta: Option<#T>,", RuntimeType.genericError(runtimeConfig)) + rust("_meta: Option<#T>,", genericError(runtimeConfig)) } is BuilderSection.AdditionalMethods -> { rustTemplate( """ - pub(crate) fn _meta(mut self, _meta: #{generic_error}) -> Self { + ##[doc(hidden)] + pub fn _meta(mut self, _meta: #{generic_error}) -> Self { self._meta = Some(_meta); self } - pub(crate) fn _set_meta(&mut self, _meta: Option<#{generic_error}>) -> &mut Self { + ##[doc(hidden)] + pub fn _set_meta(&mut self, _meta: Option<#{generic_error}>) -> &mut Self { self._meta = _meta; self } """, - "generic_error" to RuntimeType.genericError(runtimeConfig), + "generic_error" to genericError(runtimeConfig), ) } @@ -100,19 +101,10 @@ class ClientErrorGenerator( builderGen.render(writer) } - ErrorImplGenerator( - model, symbolProvider, writer, shape, error, - listOf( - object : ErrorCustomization() { - override fun section(section: ErrorSection): Writable = writable { - if (section is ErrorSection.ErrorAdditionalTraitImpls) { - rustBlock("impl #T for ${symbol.name}", RuntimeType.errorMetadataTrait(runtimeConfig)) { - rust("fn meta(&self) -> &#T { &self._meta }", genericError(runtimeConfig)) - } - } - } - }, - ) + customizations, - ).render(CodegenTarget.CLIENT) + ErrorImplGenerator(model, symbolProvider, writer, shape, error, implCustomizations).render(CodegenTarget.CLIENT) + + writer.rustBlock("impl #T for ${symbol.name}", RuntimeType.errorMetadataTrait(runtimeConfig)) { + rust("fn meta(&self) -> &#T { &self._meta }", genericError(runtimeConfig)) + } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt similarity index 82% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt rename to codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt index feee563039..c9433812d4 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt @@ -3,14 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.core.smithy.generators.error +package software.amazon.smithy.rust.codegen.client.smithy.generators.error import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.RetryableTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata @@ -49,11 +48,6 @@ fun OperationShape.errorSymbol(symbolProvider: RustSymbolProvider): RuntimeType return RustModule.Error.toType().resolve("${operationSymbol.name}Error") } -fun UnionShape.eventStreamErrorSymbol(symbolProvider: RustSymbolProvider): RuntimeType { - val unionSymbol = symbolProvider.toSymbol(this) - return RustModule.Error.toType().resolve("${unionSymbol.name}Error") -} - /** * Generates a unified error enum for [operation]. [ErrorGenerator] handles generating the individual variants, * but we must still combine those variants into an enum covering all possible errors for a given operation. @@ -127,12 +121,21 @@ class OperationErrorGenerator( } writer.rustBlock("impl #T for ${errorType.name}", RuntimeType.Display) { rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { - delegateToVariants(symbolProvider, errors) { + delegateToVariants(errors) { writable { rust("_inner.fmt(f)") } } } } + val errorMetadataTrait = RuntimeType.errorMetadataTrait(runtimeConfig) + writer.rustBlock("impl #T for ${errorType.name}", errorMetadataTrait) { + rustBlock("fn meta(&self) -> &#T", genericError(runtimeConfig)) { + delegateToVariants(errors) { + writable { rust("#T::meta(_inner)", errorMetadataTrait) } + } + } + } + writer.writeCustomizations(customizations, ErrorSection.OperationErrorAdditionalTraitImpls(errorType, errors)) val retryErrorKindT = RuntimeType.retryErrorKind(symbolProvider.config().runtimeConfig) @@ -205,7 +208,7 @@ class OperationErrorGenerator( writer.rustBlock("impl #T for ${errorType.name}", RuntimeType.StdError) { rustBlock("fn source(&self) -> Option<&(dyn #T + 'static)>", RuntimeType.StdError) { - delegateToVariants(symbolProvider, errors) { + delegateToVariants(errors) { writable { rust("Some(_inner)") } @@ -213,45 +216,44 @@ class OperationErrorGenerator( } } } -} -sealed class VariantMatch(name: String) : Section(name) { - object Unhandled : VariantMatch("Unhandled") - data class Modeled(val symbol: Symbol, val shape: Shape) : VariantMatch("Modeled") -} + sealed class VariantMatch(name: String) : Section(name) { + object Unhandled : VariantMatch("Unhandled") + data class Modeled(val symbol: Symbol, val shape: Shape) : VariantMatch("Modeled") + } -/** - * Generates code to delegate behavior to the variants, for example: - * - * ```rust - * match self { - * Self::InvalidGreeting(_inner) => inner.fmt(f), - * Self::ComplexError(_inner) => inner.fmt(f), - * Self::FooError(_inner) => inner.fmt(f), - * Self::Unhandled(_inner) => _inner.fmt(f), - * } - * ``` - * - * [handler] is passed an instance of [VariantMatch]—a [writable] should be returned containing the content to be - * written for this variant. - * - * The field will always be bound as `_inner`. - */ -fun RustWriter.delegateToVariants( - symbolProvider: RustSymbolProvider, - errors: List, - handler: (VariantMatch) -> Writable, -) { - rustBlock("match self") { - errors.forEach { - val errorSymbol = symbolProvider.toSymbol(it) - rust("""Self::${errorSymbol.name}(_inner) => """) - handler(VariantMatch.Modeled(errorSymbol, it))(this) - write(",") - } - val unhandledHandler = handler(VariantMatch.Unhandled) - rustBlock("Self::Unhandled(_inner) =>") { - unhandledHandler(this) + /** + * Generates code to delegate behavior to the variants, for example: + * + * ```rust + * match self { + * Self::InvalidGreeting(_inner) => inner.fmt(f), + * Self::ComplexError(_inner) => inner.fmt(f), + * Self::FooError(_inner) => inner.fmt(f), + * Self::Unhandled(_inner) => _inner.fmt(f), + * } + * ``` + * + * [handler] is passed an instance of [VariantMatch]—a [writable] should be returned containing the content to be + * written for this variant. + * + * The field will always be bound as `_inner`. + */ + fun RustWriter.delegateToVariants( + errors: List, + handler: (VariantMatch) -> Writable, + ) { + rustBlock("match self") { + errors.forEach { + val errorSymbol = symbolProvider.toSymbol(it) + rust("""Self::${errorSymbol.name}(_inner) => """) + handler(VariantMatch.Modeled(errorSymbol, it))(this) + write(",") + } + val unhandledHandler = handler(VariantMatch.Unhandled) + rustBlock("Self::Unhandled(_inner) =>") { + unhandledHandler(this) + } } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt similarity index 89% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt rename to codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt index 5d3bb0d752..7be341cd1e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.core.smithy.generators.error +package software.amazon.smithy.rust.codegen.client.smithy.generators.error import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId @@ -20,13 +20,13 @@ 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.withBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.unhandledError import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations +import software.amazon.smithy.rust.codegen.core.smithy.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.transformers.allErrors import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors @@ -112,16 +112,18 @@ class ServiceErrorGenerator( ) { rustBlock("match err") { rust("#T::ServiceError(context) => Self::from(context.into_err()),", sdkError) - withBlock( - "_ => Error::Unhandled(#T::builder()", - ".source(err).build()),", - unhandledError(codegenContext.runtimeConfig), - ) { - writeCustomizations( - customizations, - ErrorSection.ServiceErrorAdditionalUnhandledErrorBuildFields("err"), - ) - } + rustTemplate( + """ + _ => Error::Unhandled( + #{Unhandled}::builder() + .meta(#{ErrorMetadata}::meta(&err).clone()) + .source(err) + .build() + ), + """, + "Unhandled" to unhandledError(codegenContext.runtimeConfig), + "ErrorMetadata" to RuntimeType.errorMetadataTrait(codegenContext.runtimeConfig), + ) } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index 7b83d5094b..8a0b1d3904 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -21,6 +21,7 @@ import software.amazon.smithy.protocoltests.traits.HttpResponseTestCase import software.amazon.smithy.protocoltests.traits.HttpResponseTestsTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.generators.clientInstantiator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.allow import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata @@ -35,7 +36,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.findMemberWithTrait @@ -304,46 +304,50 @@ class ProtocolTestGenerator( val errorSymbol = operationShape.errorSymbol(codegenContext.symbolProvider) val errorVariant = codegenContext.symbolProvider.toSymbol(expectedShape).name rust("""let parsed = parsed.expect_err("should be error response");""") - rustBlock("if let #T::$errorVariant(actual_error) = parsed", errorSymbol) { - rustTemplate("#{AssertEq}(expected_output, actual_error);", *codegenScope) + rustBlock("if let #T::$errorVariant(parsed) = parsed", errorSymbol) { + compareMembers(expectedShape) } rustBlock("else") { rust("panic!(\"wrong variant: Got: {:?}. Expected: {:?}\", parsed, expected_output);") } } else { rust("let parsed = parsed.unwrap();") - outputShape.members().forEach { member -> - val memberName = codegenContext.symbolProvider.toMemberName(member) - if (member.isStreaming(codegenContext.model)) { - rustTemplate( - """ - #{AssertEq}( - parsed.$memberName.collect().await.unwrap().into_bytes(), - expected_output.$memberName.collect().await.unwrap().into_bytes() - ); - """, - *codegenScope, - ) - } else { - when (codegenContext.model.expectShape(member.target)) { - is DoubleShape, is FloatShape -> { - addUseImports( - RuntimeType.protocolTest(codegenContext.runtimeConfig, "FloatEquals").toSymbol(), - ) - rust( - """ - assert!(parsed.$memberName.float_equals(&expected_output.$memberName), - "Unexpected value for `$memberName` {:?} vs. {:?}", expected_output.$memberName, parsed.$memberName); - """, - ) - } - - else -> - rustTemplate( - """#{AssertEq}(parsed.$memberName, expected_output.$memberName, "Unexpected value for `$memberName`");""", - *codegenScope, - ) + compareMembers(outputShape) + } + } + + private fun RustWriter.compareMembers(shape: StructureShape) { + shape.members().forEach { member -> + val memberName = codegenContext.symbolProvider.toMemberName(member) + if (member.isStreaming(codegenContext.model)) { + rustTemplate( + """ + #{AssertEq}( + parsed.$memberName.collect().await.unwrap().into_bytes(), + expected_output.$memberName.collect().await.unwrap().into_bytes() + ); + """, + *codegenScope, + ) + } else { + when (codegenContext.model.expectShape(member.target)) { + is DoubleShape, is FloatShape -> { + addUseImports( + RuntimeType.protocolTest(codegenContext.runtimeConfig, "FloatEquals").toSymbol(), + ) + rust( + """ + assert!(parsed.$memberName.float_equals(&expected_output.$memberName), + "Unexpected value for `$memberName` {:?} vs. {:?}", expected_output.$memberName, parsed.$memberName); + """, + ) } + + else -> + rustTemplate( + """#{AssertEq}(parsed.$memberName, expected_output.$memberName, "Unexpected value for `$memberName`");""", + *codegenScope, + ) } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index b8f8b08bdd..ef9d118173 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.client.smithy.generators.http.ResponseBindingGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator @@ -30,7 +31,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSectio import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AddErrorMessage.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AddErrorMessage.kt index 7e69ce6995..02d61d9905 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AddErrorMessage.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/transformers/AddErrorMessage.kt @@ -19,7 +19,7 @@ import java.util.logging.Logger * * Not all errors are modeled with an error message field. However, in many cases, the server can still send an error. * If an error, specifically, a structure shape with the error trait does not have a member `message` or `Message`, - * this transformer will add a `message` member targeting a string. + * this transformer will add a `Message` member targeting a string. * * This ensures that we always generate a modeled error message field enabling end users to easily extract the error * message when present. @@ -37,7 +37,7 @@ object AddErrorMessage { val addMessageField = shape.hasTrait() && shape is StructureShape && shape.errorMessageMember() == null if (addMessageField && shape is StructureShape) { logger.info("Adding message field to ${shape.id}") - shape.toBuilder().addMember("message", ShapeId.from("smithy.api#String")).build() + shape.toBuilder().addMember("Message", ShapeId.from("smithy.api#String")).build() } else { shape } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt similarity index 98% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGeneratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt index e737223ab2..7538474a83 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ClientErrorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt @@ -10,7 +10,7 @@ import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel -class ClientErrorGeneratorTest { +class ErrorGeneratorTest { val model = """ namespace com.test diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGeneratorTest.kt similarity index 96% rename from codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGeneratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGeneratorTest.kt index 788f29bf7a..cefc1a6a25 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/OperationErrorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGeneratorTest.kt @@ -3,17 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.core.smithy.generators.error +package software.amazon.smithy.rust.codegen.client.smithy.generators.error import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.ErrorsModule import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer 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.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.renderWithModelBuilder -import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt new file mode 100644 index 0000000000..1cbb274cfb --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.generators.error + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.integrationTest + +internal class ServiceErrorGeneratorTest { + @Test + fun `top level errors are send + sync`() { + val model = """ + namespace com.example + + use aws.protocols#restJson1 + + @restJson1 + service HelloService { + operations: [SayHello], + version: "1" + } + + @http(uri: "/", method: "POST") + operation SayHello { + input: EmptyStruct, + output: EmptyStruct, + errors: [SorryBusy, CanYouRepeatThat, MeDeprecated] + } + + structure EmptyStruct { } + + @error("server") + structure SorryBusy { } + + @error("client") + structure CanYouRepeatThat { } + + @error("client") + @deprecated + structure MeDeprecated { } + """.asSmithyModel() + + clientIntegrationTest(model) { codegenContext, rustCrate -> + rustCrate.integrationTest("validate_errors") { + rust( + """ + fn check_send_sync() {} + + ##[test] + fn service_errors_are_send_sync() { + check_send_sync::<${codegenContext.moduleUseName()}::Error>() + } + """, + ) + } + } + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt index 1da709422d..fd908d1310 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.escape @@ -21,7 +22,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolTraitImplGenerator @@ -62,7 +62,8 @@ private class TestProtocolTraitImplGenerator( fn parse(&self, _response: &#{Response}<#{Bytes}>) -> Self::Output { ${operationWriter.escape(correctResponse)} } - }""", + } + """, "parse_strict" to RuntimeType.parseStrictResponse(codegenContext.runtimeConfig), "Output" to symbolProvider.toSymbol(operationShape.outputShape(codegenContext.model)), "Error" to operationShape.errorSymbol(symbolProvider), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt index b993a06efe..37489588f0 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/eventstream/ClientEventStreamBaseRequirements.kt @@ -16,7 +16,8 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ClientErrorGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.error.OperationErrorGenerator import software.amazon.smithy.rust.codegen.client.testutil.clientTestRustSettings import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -24,7 +25,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.OperationErrorGenerator import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestModels import software.amazon.smithy.rust.codegen.core.testutil.EventStreamTestRequirements import software.amazon.smithy.rust.codegen.core.util.expectTrait @@ -79,7 +79,7 @@ abstract class ClientEventStreamBaseRequirements : EventStreamTestRequirements() - ClientErrorGenerator( + ErrorGenerator( codegenContext.model, codegenContext.symbolProvider, writer, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt index 1aff86f7d4..e7fd2b195d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt @@ -10,11 +10,12 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.UnionShape 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.RustType import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors @@ -23,6 +24,11 @@ import software.amazon.smithy.rust.codegen.core.util.isEventStream import software.amazon.smithy.rust.codegen.core.util.isInputEventStream import software.amazon.smithy.rust.codegen.core.util.isOutputEventStream +fun UnionShape.eventStreamErrorSymbol(symbolProvider: RustSymbolProvider): RuntimeType { + val unionSymbol = symbolProvider.toSymbol(this) + return RustModule.Error.toType().resolve("${unionSymbol.name}Error") +} + /** * Wrapping symbol provider to wrap modeled types with the aws-smithy-http Event Stream send/receive types. */ diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt index b5e9145626..3acf119d9a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt @@ -14,7 +14,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomi import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplCustomization import software.amazon.smithy.rust.codegen.core.util.deepMergeWith import java.util.ServiceLoader import java.util.logging.Logger @@ -66,14 +66,6 @@ interface CoreCodegenDecorator { baseCustomizations: List, ): List = baseCustomizations - /** - * Hook to customize generated errors. - */ - fun errorCustomizations( - codegenContext: CodegenContext, - baseCustomizations: List, - ): List = baseCustomizations - /** * Hook to customize generated structures. */ @@ -91,6 +83,14 @@ interface CoreCodegenDecorator { baseCustomizations: List, ): List = baseCustomizations + /** + * Hook to customize error struct `impl` blocks. + */ + fun errorImplCustomizations( + codegenContext: CodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + /** * Extra sections allow one decorator to influence another. This is intended to be used by querying the `rootDecorator` */ @@ -127,13 +127,6 @@ abstract class CombinedCoreCodegenDecorator, - ): List = combineCustomizations(baseCustomizations) { decorator, customizations -> - decorator.errorCustomizations(codegenContext, customizations) - } - override fun structureCustomizations( codegenContext: CodegenContext, baseCustomizations: List, @@ -148,6 +141,13 @@ abstract class CombinedCoreCodegenDecorator, + ): List = combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.errorImplCustomizations(codegenContext, customizations) + } + final override fun extraSections(codegenContext: CodegenContext): List, (Section) -> Writable>> = addCustomizations { decorator -> decorator.extraSections(codegenContext) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt index 1fc94f79e5..2965683a04 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators.error +import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait @@ -23,6 +24,8 @@ 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.Companion.StdError import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedSectionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.mapRustType @@ -35,6 +38,15 @@ import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.shouldRedact +/** Error customization sections */ +sealed class ErrorImplSection(name: String) : Section(name) { + /** Use this section to add additional trait implementations to the generated error structures */ + class ErrorAdditionalTraitImpls(val errorType: Symbol) : ErrorImplSection("ErrorAdditionalTraitImpls") +} + +/** Customizations for generated errors */ +abstract class ErrorImplCustomization : NamedSectionGenerator() + sealed class ErrorKind { abstract fun writable(runtimeConfig: RuntimeConfig): Writable @@ -76,7 +88,7 @@ class ErrorImplGenerator( private val writer: RustWriter, private val shape: StructureShape, private val error: ErrorTrait, - private val customizations: List, + private val customizations: List, ) { private val runtimeConfig = symbolProvider.config().runtimeConfig @@ -160,6 +172,6 @@ class ErrorImplGenerator( writer.write("impl #T for ${symbol.name} {}", StdError) - writer.writeCustomizations(customizations, ErrorSection.ErrorAdditionalTraitImpls(symbol)) + writer.writeCustomizations(customizations, ErrorImplSection.ErrorAdditionalTraitImpls(symbol)) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 46e33247a5..4571abe080 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -32,8 +32,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt index 696dcc2311..f9cdea5108 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/EventStreamErrorMarshallerGenerator.kt @@ -23,7 +23,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget 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.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol +import software.amazon.smithy.rust.codegen.core.smithy.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.generators.unknownVariantError import software.amazon.smithy.rust.codegen.core.smithy.rustType diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt deleted file mode 100644 index 6c1322170c..0000000000 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ServiceErrorGeneratorTest.kt +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.core.smithy.generators.error - -import org.junit.jupiter.api.Test -import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.model.traits.ErrorTrait -import software.amazon.smithy.rust.codegen.core.rustlang.Attribute -import software.amazon.smithy.rust.codegen.core.rustlang.AttributeKind -import software.amazon.smithy.rust.codegen.core.rustlang.RustModule -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget -import software.amazon.smithy.rust.codegen.core.smithy.CoreRustSettings -import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator -import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors -import software.amazon.smithy.rust.codegen.core.testutil.TestWriterDelegator -import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest -import software.amazon.smithy.rust.codegen.core.testutil.generatePluginContext -import software.amazon.smithy.rust.codegen.core.testutil.testSymbolProvider -import software.amazon.smithy.rust.codegen.core.util.getTrait -import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.createDirectory -import kotlin.io.path.writeText - -internal class ServiceErrorGeneratorTest { - @ExperimentalPathApi - @Test - fun `top level errors are send + sync`() { - val model = """ - namespace com.example - - use aws.protocols#restJson1 - - @restJson1 - service HelloService { - operations: [SayHello], - version: "1" - } - - @http(uri: "/", method: "POST") - operation SayHello { - input: EmptyStruct, - output: EmptyStruct, - errors: [SorryBusy, CanYouRepeatThat, MeDeprecated] - } - - structure EmptyStruct { } - - @error("server") - structure SorryBusy { } - - @error("client") - structure CanYouRepeatThat { } - - @error("client") - @deprecated - structure MeDeprecated { } - """.asSmithyModel() - - val (pluginContext, testDir) = generatePluginContext(model) - val moduleName = pluginContext.settings.expectStringMember("module").value.replace('-', '_') - val symbolProvider = testSymbolProvider(model) - val settings = CoreRustSettings.from(model, pluginContext.settings) - val codegenContext = CodegenContext( - model, - symbolProvider, - model.expectShape(ShapeId.from("com.example#HelloService")) as ServiceShape, - ShapeId.from("aws.protocols#restJson1"), - settings, - CodegenTarget.CLIENT, - ) - - val rustCrate = TestWriterDelegator( - pluginContext.fileManifest, - symbolProvider, - codegenContext.settings.codegenConfig, - ) - - rustCrate.lib { - Attribute.AllowDeprecated.render(this, AttributeKind.Inner) - } - rustCrate.withModule(RustModule.Error) { - for (operation in model.operationShapes) { - if (operation.id.namespace == "com.example") { - OperationErrorGenerator( - model, - symbolProvider, - symbolProvider.toSymbol(operation), - operation.operationErrors(model).map { it as StructureShape }, - emptyList(), - ).render(this) - } - } - for (shape in model.structureShapes) { - if (shape.id.namespace == "com.example") { - StructureGenerator(model, symbolProvider, this, shape, emptyList()).render() - - val errorTrait = shape.getTrait() - if (errorTrait != null) { - ErrorImplGenerator( - model, - symbolProvider, - this, - shape, - errorTrait, - listOf(), - ).render(CodegenTarget.CLIENT) - } - } - } - } - ServiceErrorGenerator(codegenContext, model.operationShapes.toList(), emptyList()).render(rustCrate) - - testDir.resolve("tests").createDirectory() - testDir.resolve("tests/validate_errors.rs").writeText( - """ - fn check_send_sync() {} - #[test] - fn tl_errors_are_send_sync() { - check_send_sync::<$moduleName::Error>() - } - """, - ) - rustCrate.compileAndTest() - } -} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationErrorGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationErrorGenerator.kt index 65e53adb6c..908f37820b 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationErrorGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationErrorGenerator.kt @@ -15,9 +15,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable 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.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerOperationErrorGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.errorSymbol /** * Generates a unified error enum for [operation]. It depends on [ServerOperationErrorGenerator] diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 97c18e0cfd..4fc89901e8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -40,10 +40,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.ModelsModule import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig import software.amazon.smithy.rust.codegen.core.smithy.UnconstrainedModule +import software.amazon.smithy.rust.codegen.core.smithy.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.eventStreamErrorSymbol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer @@ -270,7 +270,7 @@ open class ServerCodegenVisitor( this, shape, errorTrait, - codegenDecorator.errorCustomizations(codegenContext, emptyList()), + codegenDecorator.errorImplCustomizations(codegenContext, emptyList()), ).render(CodegenTarget.SERVER) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt index 759f887088..c754d9bb72 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt @@ -15,7 +15,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.ErrorsModule import software.amazon.smithy.rust.codegen.core.smithy.InputsModule import software.amazon.smithy.rust.codegen.core.smithy.OutputsModule -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt index 44048276d8..14ed6443cb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt @@ -7,8 +7,10 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -20,6 +22,21 @@ 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.util.toSnakeCase +/** + * For a given Operation ([this]), return the symbol referring to the operation error. This can be used + * if you, e.g. want to return an operation error from a function: + * + * ```kotlin + * rustWriter.rustBlock("fn get_error() -> #T", operation.errorSymbol(symbolProvider)) { + * write("todo!() // function body") + * } + * ``` + */ +fun OperationShape.errorSymbol(symbolProvider: RustSymbolProvider): RuntimeType { + val operationSymbol = symbolProvider.toSymbol(this) + return RustModule.Error.toType().resolve("${operationSymbol.name}Error") +} + /** * Generates a unified error enum for [operation]. [ErrorGenerator] handles generating the individual variants, * but we must still combine those variants into an enum covering all possible errors for a given operation. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt index 308f2f0470..868b87a6f2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt @@ -11,7 +11,6 @@ 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.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.transformers.operationErrors import software.amazon.smithy.rust.codegen.core.util.hasStreamingMember import software.amazon.smithy.rust.codegen.core.util.inputShape diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt index 10b11978d0..d88b4c989e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt @@ -26,7 +26,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.ErrorsModule import software.amazon.smithy.rust.codegen.core.smithy.InputsModule import software.amazon.smithy.rust.codegen.core.smithy.OutputsModule import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 390eab3020..1183cd8e10 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -43,7 +43,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.TypeConversionGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.error.errorSymbol import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName @@ -71,6 +70,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.errorSymbol import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerRequestBindingGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.http.ServerResponseBindingGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol diff --git a/gradle.properties b/gradle.properties index 502ab6a3ba..a493e3ec69 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,6 +6,9 @@ # Rust MSRV (entered into the generated README) rust.msrv=1.62.1 +# To enable debug, swap out the two lines below. +# When changing this value, be sure to run `./gradlew --stop` to kill the Gradle daemon. +# org.gradle.jvmargs=-Xmx1024M -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:5006 org.gradle.jvmargs=-Xmx1024M # Version number to use for the generated runtime crates From a1a426b33a945847ea4d4d2181fe5bf06316a87e Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 20 Jan 2023 10:36:01 -0800 Subject: [PATCH 18/39] Re-export `ErrorMetadata` --- .../codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt index 02a8843c19..03f2763170 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt @@ -55,6 +55,7 @@ internal fun pubUseTypes(runtimeConfig: RuntimeConfig, model: Model): List listOf( + PubUseType(http.resolve("result::ErrorMetadata")) { true }, PubUseType(http.resolve("result::SdkError")) { true }, PubUseType(http.resolve("byte_stream::ByteStream"), ::hasStreamingOperations), PubUseType(http.resolve("byte_stream::AggregatedBytes"), ::hasStreamingOperations), From daf7a382db736c78cd268fcb10b2c02bc125e029 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 20 Jan 2023 10:36:18 -0800 Subject: [PATCH 19/39] Update changelog --- CHANGELOG.next.toml | 136 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index debf76da7a..b68c442aa8 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -200,3 +200,139 @@ message = "Generic clients no longer expose a `request_id()` function on errors. references = ["smithy-rs#76", "smithy-rs#2129"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client"} author = "jdisanti" + +[[aws-sdk-rust]] +message = "The `message()` and `code()` methods on errors have been moved into `ErrorMetadata` trait. This trait will need to be imported to continue calling these." +references = ["smithy-rs#76", "smithy-rs#2129"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "jdisanti" + +[[smithy-rs]] +message = "The `message()` and `code()` methods on errors have been moved into `ErrorMetadata` trait. This trait will need to be imported to continue calling these." +references = ["smithy-rs#76", "smithy-rs#2129"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client"} +author = "jdisanti" + +[[aws-sdk-rust]] +message = """ +The `*Error` and `*ErrorKind` types have been combined to make error matching simpler. + +
+Example with S3 + +**Before:** + +```rust +let result = client + .get_object() + .bucket(BUCKET_NAME) + .key("some-key") + .send() + .await; +match result { + Ok(_output) => { /* Do something with the output */ } + Err(err) => match err.into_service_error() { + GetObjectError { kind, .. } => match kind { + GetObjectErrorKind::InvalidObjectState(value) => println!("invalid object state: {:?}", value), + GetObjectErrorKind::NoSuchKey(_) => println!("object didn't exist"), + } + err @ GetObjectError { .. } if err.code() == Some("SomeUnmodeledError") => {} + err @ _ => return Err(err.into()), + }, +} +``` + +**After:** + +```rust +// Needed to access the `.code()` function on the error type: +use aws_sdk_s3::types::ErrorMetadata; + +let result = client + .get_object() + .bucket(BUCKET_NAME) + .key("some-key") + .send() + .await; +match result { + Ok(_output) => { /* Do something with the output */ } + Err(err) => match err.into_service_error() { + GetObjectError::InvalidObjectState(value) => { + println!("invalid object state: {:?}", value); + } + GetObjectError::NoSuchKey(_) => { + println!("object didn't exist"); + } + err if err.code() == Some("SomeUnmodeledError") => {} + err @ _ => return Err(err.into()), + }, +} +``` + +
+""" +references = ["smithy-rs#76", "smithy-rs#2129", "smithy-rs#2075"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "jdisanti" + +[[smithy-rs]] +message = """ +The `*Error` and `*ErrorKind` types have been combined to make error matching simpler. + +
+Example with S3 + +**Before:** + +```rust +let result = client + .get_object() + .bucket(BUCKET_NAME) + .key("some-key") + .send() + .await; +match result { + Ok(_output) => { /* Do something with the output */ } + Err(err) => match err.into_service_error() { + GetObjectError { kind, .. } => match kind { + GetObjectErrorKind::InvalidObjectState(value) => println!("invalid object state: {:?}", value), + GetObjectErrorKind::NoSuchKey(_) => println!("object didn't exist"), + } + err @ GetObjectError { .. } if err.code() == Some("SomeUnmodeledError") => {} + err @ _ => return Err(err.into()), + }, +} +``` + +**After:** + +```rust +// Needed to access the `.code()` function on the error type: +use aws_sdk_s3::types::ErrorMetadata; + +let result = client + .get_object() + .bucket(BUCKET_NAME) + .key("some-key") + .send() + .await; +match result { + Ok(_output) => { /* Do something with the output */ } + Err(err) => match err.into_service_error() { + GetObjectError::InvalidObjectState(value) => { + println!("invalid object state: {:?}", value); + } + GetObjectError::NoSuchKey(_) => { + println!("object didn't exist"); + } + err if err.code() == Some("SomeUnmodeledError") => {} + err @ _ => return Err(err.into()), + }, +} +``` + +
+""" +references = ["smithy-rs#76", "smithy-rs#2129", "smithy-rs#2075"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client"} +author = "jdisanti" From ef0614f412aa44b83130a6d78918b72949e1f1b4 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 25 Jan 2023 12:48:32 -0800 Subject: [PATCH 20/39] Incorporate feedback --- aws/rust-runtime/aws-http/src/request_id.rs | 2 +- .../client/smithy/generators/EndpointTraitBindingsTest.kt | 6 +++--- .../codegen/core/smithy/customize/CoreCodegenDecorator.kt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aws/rust-runtime/aws-http/src/request_id.rs b/aws/rust-runtime/aws-http/src/request_id.rs index 358c735c9e..9ad99377b2 100644 --- a/aws/rust-runtime/aws-http/src/request_id.rs +++ b/aws/rust-runtime/aws-http/src/request_id.rs @@ -16,7 +16,7 @@ const AWS_REQUEST_ID: &str = "aws_request_id"; /// Implementers add a function to return an AWS request ID pub trait RequestId { - /// Returns the request ID if it's available. + /// Returns the request ID, or `None` if the service could not be reached. fn request_id(&self) -> Option<&str>; } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt index 1ca39357ec..12285e4364 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt @@ -50,10 +50,10 @@ internal class EndpointTraitBindingsTest { } """.asSmithyModel() val operationShape: OperationShape = model.lookup("test#GetStatus") - val sym = testSymbolProvider(model) + val symbolProvider = testSymbolProvider(model) val endpointBindingGenerator = EndpointTraitBindings( model, - sym, + symbolProvider, TestRuntimeConfig, operationShape, operationShape.expectTrait(EndpointTrait::class.java), @@ -67,7 +67,7 @@ internal class EndpointTraitBindingsTest { } """, ) - implBlock(sym.toSymbol(model.lookup("test#GetStatusInput"))) { + implBlock(symbolProvider.toSymbol(model.lookup("test#GetStatusInput"))) { rustBlock( "fn endpoint_prefix(&self) -> std::result::Result<#T::endpoint::EndpointPrefix, #T>", RuntimeType.smithyHttp(TestRuntimeConfig), diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt index 3acf119d9a..52dc052067 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt @@ -67,7 +67,7 @@ interface CoreCodegenDecorator { ): List = baseCustomizations /** - * Hook to customize generated structures. + * Hook to customize structures generated by `StructureGenerator`. */ fun structureCustomizations( codegenContext: CodegenContext, From 23380742ddd0f8341bb7f791e206bd391bf62869 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 25 Jan 2023 13:20:18 -0800 Subject: [PATCH 21/39] Add request IDs to trace logs --- .../amazon/smithy/rustsdk/BaseRequestIdDecorator.kt | 7 +++++++ .../client/smithy/protocols/HttpBoundProtocolGenerator.kt | 4 ++++ .../core/smithy/customize/OperationCustomization.kt | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt index b943eb770c..4c8365d06c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt @@ -88,6 +88,13 @@ abstract class BaseRequestIdDecorator : ClientCodegenDecorator { accessorTrait(codegenContext), ) } + is OperationSection.BeforeParseResponse -> { + rustTemplate( + "#{tracing}::debug!($fieldName = #{trait}::$accessorFunctionName(${section.responseName}));", + "tracing" to RuntimeType.Tracing, + "trait" to accessorTrait(codegenContext), + ) + } else -> {} } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index ef9d118173..5483709cce 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -116,6 +116,7 @@ class HttpBoundProtocolTraitImplGenerator( impl #{ParseStrict} for $operationName { type Output = std::result::Result<#{O}, #{E}>; fn parse(&self, response: &#{http}::Response<#{Bytes}>) -> Self::Output { + #{BeforeParseResponse} if !response.status().is_success() && response.status().as_u16() != $successCode { #{parse_error}(response) } else { @@ -128,6 +129,9 @@ class HttpBoundProtocolTraitImplGenerator( "E" to operationShape.errorSymbol(symbolProvider), "parse_error" to parseError(operationShape, customizations), "parse_response" to parseResponse(operationShape, customizations), + "BeforeParseResponse" to writable { + writeCustomizations(customizations, OperationSection.BeforeParseResponse(customizations, "response")) + }, ) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt index f8f064a10b..88afcac4b4 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt @@ -65,6 +65,14 @@ sealed class OperationSection(name: String) : Section(name) { /** Name of the response (for referring to it in Rust code) */ val responseName: String, ) : OperationSection("PopulateGenericErrorExtras") + + /** + * Hook to add custom code right before the response is parsed. + */ + data class BeforeParseResponse( + override val customizations: List, + val responseName: String, + ) : OperationSection("BeforeParseResponse") } abstract class OperationCustomization : NamedSectionGenerator() { From cbff72105b45baedcca8cfe113d50d4359d3258d Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 26 Jan 2023 17:19:27 -0800 Subject: [PATCH 22/39] Fix doc link --- rust-runtime/aws-smithy-types/src/error/unhandled.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-runtime/aws-smithy-types/src/error/unhandled.rs b/rust-runtime/aws-smithy-types/src/error/unhandled.rs index 50acc17e0e..5a0bde0595 100644 --- a/rust-runtime/aws-smithy-types/src/error/unhandled.rs +++ b/rust-runtime/aws-smithy-types/src/error/unhandled.rs @@ -55,7 +55,7 @@ impl Builder { /// An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code). /// /// When logging an error from the SDK, it is recommended that you either wrap the error in -/// [`DisplayErrorContext`](crate::error::DisplayErrorContext), use another +/// [`DisplayErrorContext`](crate::error::display::DisplayErrorContext), use another /// error reporter library that visits the error's cause/source chain, or call /// [`Error::source`](std::error::Error::source) for more details about the underlying cause. #[derive(Debug)] From b038943b2e327bdbdff5213bac60ba1e43c08be6 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 26 Jan 2023 17:23:12 -0800 Subject: [PATCH 23/39] Fix the canary build --- tools/ci-cdk/canary-lambda/src/s3_canary.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/ci-cdk/canary-lambda/src/s3_canary.rs b/tools/ci-cdk/canary-lambda/src/s3_canary.rs index cb56797f01..830a760481 100644 --- a/tools/ci-cdk/canary-lambda/src/s3_canary.rs +++ b/tools/ci-cdk/canary-lambda/src/s3_canary.rs @@ -8,7 +8,7 @@ use crate::{mk_canary, CanaryEnv}; use anyhow::Context; use aws_config::SdkConfig; use aws_sdk_s3 as s3; -use s3::error::{GetObjectError, GetObjectErrorKind}; +use s3::error::GetObjectError; use s3::types::ByteStream; use uuid::Uuid; @@ -36,10 +36,7 @@ pub async fn s3_canary(client: s3::Client, s3_bucket_name: String) -> anyhow::Re ); } Err(err) => match err.into_service_error() { - GetObjectError { - kind: GetObjectErrorKind::NoSuchKey(..), - .. - } => { + GetObjectError::NoSuchKey(..) => { // good } err => Err(err).context("unexpected s3::GetObject failure")?, From f11225f5a48b6c78f6c9e778cddd530248c50893 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 26 Jan 2023 17:43:37 -0800 Subject: [PATCH 24/39] Simplify some error trait handling --- .../rust/codegen/server/smithy/ServerCodegenVisitor.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index bd7ea20bdf..4ee8fb0264 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -260,10 +260,7 @@ open class ServerCodegenVisitor( codegenDecorator.structureCustomizations(codegenContext, emptyList()), ).render() - renderStructureShapeBuilder(shape, this) - - val errorTrait = shape.getTrait() - if (errorTrait != null) { + shape.getTrait()?.also { errorTrait -> ErrorImplGenerator( model, codegenContext.symbolProvider, @@ -273,6 +270,8 @@ open class ServerCodegenVisitor( codegenDecorator.errorImplCustomizations(codegenContext, emptyList()), ).render(CodegenTarget.SERVER) } + + renderStructureShapeBuilder(shape, this) } } From 87e0fef9ed7128bab8c56eb0cb508a02f430107b Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 27 Jan 2023 10:08:06 -0800 Subject: [PATCH 25/39] Fix tracing statement --- .../software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt index 4c8365d06c..c34722c6d2 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt @@ -90,7 +90,7 @@ abstract class BaseRequestIdDecorator : ClientCodegenDecorator { } is OperationSection.BeforeParseResponse -> { rustTemplate( - "#{tracing}::debug!($fieldName = #{trait}::$accessorFunctionName(${section.responseName}));", + "#{tracing}::debug!($fieldName = ?#{trait}::$accessorFunctionName(${section.responseName}));", "tracing" to RuntimeType.Tracing, "trait" to accessorTrait(codegenContext), ) From 6578643793abf21bf5fd13adb3d1cbcbb405919f Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 27 Jan 2023 12:25:59 -0800 Subject: [PATCH 26/39] Rename `ClientContextParamDecorator` to `ClientContextConfigCustomization` --- ...extParamDecorator.kt => ClientContextConfigCustomization.kt} | 0 ...DecoratorTest.kt => ClientContextConfigCustomizationTest.kt} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/{ClientContextParamDecorator.kt => ClientContextConfigCustomization.kt} (100%) rename codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/{ClientContextParamsDecoratorTest.kt => ClientContextConfigCustomizationTest.kt} (97%) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextParamDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt similarity index 100% rename from codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextParamDecorator.kt rename to codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextParamsDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt similarity index 97% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextParamsDecoratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt index cb96490209..78d77d01db 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextParamsDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt @@ -14,7 +14,7 @@ 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.unitTest -class ClientContextParamsDecoratorTest { +class ClientContextConfigCustomizationTest { val model = """ namespace test use smithy.rules#clientContextParams From f41fbae23d57ecd61812286b7ea670edcfb395b9 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 27 Jan 2023 15:36:35 -0800 Subject: [PATCH 27/39] Fix S3Control endpoint tests --- .../customize/s3control/S3ControlDecorator.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt index 7d7f31a1c9..3534258b18 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt @@ -6,9 +6,18 @@ package software.amazon.smithy.rustsdk.customize.s3control import software.amazon.smithy.model.Model +import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rustsdk.endpoints.stripEndpointTrait +import software.amazon.smithy.rustsdk.getBuiltIn +import software.amazon.smithy.rustsdk.toWritable class S3ControlDecorator : ClientCodegenDecorator { override val name: String = "S3Control" @@ -16,4 +25,22 @@ class S3ControlDecorator : ClientCodegenDecorator { override fun transformModel(service: ServiceShape, model: Model): Model = stripEndpointTrait("AccountId")(model) + + override fun endpointCustomizations(codegenContext: ClientCodegenContext): List { + return listOf(object : EndpointCustomization { + override fun setBuiltInOnServiceConfig(name: String, value: Node, configBuilderRef: String): Writable? { + if (!name.startsWith("AWS::S3Control")) { + return null + } + val builtIn = codegenContext.getBuiltIn(name) ?: return null + return writable { + rustTemplate( + "let $configBuilderRef = $configBuilderRef.${builtIn.name.rustName()}(#{value});", + "value" to value.toWritable(), + ) + } + } + }, + ) + } } From 69f367d345e65e6246f4b9c0994d22787bdabf34 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 30 Jan 2023 18:10:26 -0800 Subject: [PATCH 28/39] temp: Check if Gradle caching is causing the server codegen issue --- .github/actions/docker-build/action.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/actions/docker-build/action.yml b/.github/actions/docker-build/action.yml index 837bb3fbc1..f5df3b121f 100644 --- a/.github/actions/docker-build/action.yml +++ b/.github/actions/docker-build/action.yml @@ -15,15 +15,15 @@ inputs: runs: using: composite steps: - - uses: actions/cache@v3 - name: Gradle Cache - with: - path: | - gradle/caches - gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('gradle/caches/**/*', 'gradle/wrapper/**/*') }} - restore-keys: | - ${{ runner.os }}-gradle- +# - uses: actions/cache@v3 +# name: Gradle Cache +# with: +# path: | +# gradle/caches +# gradle/wrapper +# key: ${{ runner.os }}-gradle-${{ hashFiles('gradle/caches/**/*', 'gradle/wrapper/**/*') }} +# restore-keys: | +# ${{ runner.os }}-gradle- # Pinned to the commit hash of v2.1.0 - uses: Swatinem/rust-cache@b894d59a8d236e2979b247b80dac8d053ab340dd with: From 164b2f0ab228ec0295ec6a391a216e8fbf303fbc Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 31 Jan 2023 15:31:53 -0800 Subject: [PATCH 29/39] Revert "temp: Check if Gradle caching is causing the server codegen issue" This reverts commit 69f367d345e65e6246f4b9c0994d22787bdabf34. --- .github/actions/docker-build/action.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/actions/docker-build/action.yml b/.github/actions/docker-build/action.yml index f5df3b121f..837bb3fbc1 100644 --- a/.github/actions/docker-build/action.yml +++ b/.github/actions/docker-build/action.yml @@ -15,15 +15,15 @@ inputs: runs: using: composite steps: -# - uses: actions/cache@v3 -# name: Gradle Cache -# with: -# path: | -# gradle/caches -# gradle/wrapper -# key: ${{ runner.os }}-gradle-${{ hashFiles('gradle/caches/**/*', 'gradle/wrapper/**/*') }} -# restore-keys: | -# ${{ runner.os }}-gradle- + - uses: actions/cache@v3 + name: Gradle Cache + with: + path: | + gradle/caches + gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('gradle/caches/**/*', 'gradle/wrapper/**/*') }} + restore-keys: | + ${{ runner.os }}-gradle- # Pinned to the commit hash of v2.1.0 - uses: Swatinem/rust-cache@b894d59a8d236e2979b247b80dac8d053ab340dd with: From e667ddaf9a840abf04294470719121c3547e1573 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 31 Jan 2023 15:32:05 -0800 Subject: [PATCH 30/39] Fix Python server errors --- .../python/smithy/PythonServerCodegenVisitor.kt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index f814478b18..3cc0f98730 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -16,10 +16,14 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitorConfig +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator +import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEnumGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerOperationHandlerGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerServiceGenerator @@ -40,7 +44,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtoco */ class PythonServerCodegenVisitor( context: PluginContext, - codegenDecorator: ServerCodegenDecorator, + private val codegenDecorator: ServerCodegenDecorator, ) : ServerCodegenVisitor(context, codegenDecorator) { init { @@ -120,6 +124,17 @@ class PythonServerCodegenVisitor( // and #[pymethods] implementation. PythonServerStructureGenerator(model, codegenContext.symbolProvider, this, shape).render() + shape.getTrait()?.also { errorTrait -> + ErrorImplGenerator( + model, + codegenContext.symbolProvider, + this, + shape, + errorTrait, + codegenDecorator.errorImplCustomizations(codegenContext, emptyList()), + ).render(CodegenTarget.SERVER) + } + renderStructureShapeBuilder(shape, this) } } From 1bce4420a28834ced58c68b1bce74e2f7bba6344 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 1 Feb 2023 11:25:29 -0800 Subject: [PATCH 31/39] Add deprecated alias to guide customers through upgrading --- .../generators/error/OperationErrorGenerator.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt index c9433812d4..9ba29dfa6b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt @@ -30,6 +30,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.unh import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations +import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.toSnakeCase @@ -80,6 +81,22 @@ class OperationErrorGenerator( visibility = Visibility.PUBLIC, ) + // TODO(deprecated): Remove this temporary alias. This was added so that the compiler + // points customers in the right direction when they are upgrading. Unfortunately there's no + // way to provide better backwards compatibility on this change. + val kindDeprecationMessage = "Operation `*Error/*ErrorKind` types were combined into a single `*Error` enum. " + + "The `.kind` field on `*Error` no longer exists and isn't needed anymore (you can just match on the " + + "error directly since it's an enum now)." + writer.rust( + """ + /// Do not use this. + /// + /// $kindDeprecationMessage + ##[deprecated(note = ${kindDeprecationMessage.dq()})] + pub type ${errorType.name}Kind = ${errorType.name}; + """, + ) + writer.rust("/// Error type for the `${operationSymbol.name}` operation.") meta.render(writer) writer.rustBlock("enum ${errorType.name}") { From 63ca8b0b51f6876ce4afb2c5f5b8ce681dc3c05e Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 1 Feb 2023 11:56:47 -0800 Subject: [PATCH 32/39] Rename `_meta` to `meta` --- .../sts/tests/retry_idp_comms_err.rs | 2 +- .../smithy/generators/error/ErrorGenerator.kt | 22 +++++++++---------- .../protocols/HttpBoundProtocolGenerator.kt | 2 +- .../generators/error/ErrorGeneratorTest.kt | 2 +- .../core/rustlang/RustReservedWords.kt | 2 ++ .../parse/EventStreamUnmarshallerGenerator.kt | 2 +- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs b/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs index 03d80d3361..edb6f52fb2 100644 --- a/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs +++ b/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs @@ -13,7 +13,7 @@ async fn idp_comms_err_retryable() { let error = AssumeRoleWithWebIdentityError::IdpCommunicationErrorException( IdpCommunicationErrorException::builder() .message("test") - ._meta( + .meta( ErrorMeta::builder() .code("IDPCommunicationError") .message("test") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt index 5c7c9a8855..48fc95d10f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt @@ -47,10 +47,10 @@ class ErrorGenerator( override fun section(section: StructureSection): Writable = writable { when (section) { is StructureSection.AdditionalFields -> { - rust("pub(crate) _meta: #T,", genericError(runtimeConfig)) + rust("pub(crate) meta: #T,", genericError(runtimeConfig)) } is StructureSection.AdditionalDebugFields -> { - rust("""${section.formatterName}.field("_meta", &self._meta);""") + rust("""${section.formatterName}.field("meta", &self.meta);""") } } } @@ -65,21 +65,21 @@ class ErrorGenerator( override fun section(section: BuilderSection): Writable = writable { when (section) { is BuilderSection.AdditionalFields -> { - rust("_meta: Option<#T>,", genericError(runtimeConfig)) + rust("meta: Option<#T>,", genericError(runtimeConfig)) } is BuilderSection.AdditionalMethods -> { rustTemplate( """ - ##[doc(hidden)] - pub fn _meta(mut self, _meta: #{generic_error}) -> Self { - self._meta = Some(_meta); + /// Sets error metadata + pub fn meta(mut self, meta: #{generic_error}) -> Self { + self.meta = Some(meta); self } - ##[doc(hidden)] - pub fn _set_meta(&mut self, _meta: Option<#{generic_error}>) -> &mut Self { - self._meta = _meta; + /// Sets error metadata + pub fn set_meta(&mut self, meta: Option<#{generic_error}>) -> &mut Self { + self.meta = meta; self } """, @@ -88,7 +88,7 @@ class ErrorGenerator( } is BuilderSection.AdditionalFieldsInBuild -> { - rust("_meta: self._meta.unwrap_or_default(),") + rust("meta: self.meta.unwrap_or_default(),") } } } @@ -104,7 +104,7 @@ class ErrorGenerator( ErrorImplGenerator(model, symbolProvider, writer, shape, error, implCustomizations).render(CodegenTarget.CLIENT) writer.rustBlock("impl #T for ${symbol.name}", RuntimeType.errorMetadataTrait(runtimeConfig)) { - rust("fn meta(&self) -> &#T { &self._meta }", genericError(runtimeConfig)) + rust("fn meta(&self) -> &#T { &self.meta }", genericError(runtimeConfig)) } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index 5483709cce..bf5b48df27 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -225,7 +225,7 @@ class HttpBoundProtocolTraitImplGenerator( listOf(object : OperationCustomization() { override fun section(section: OperationSection): Writable = writable { if (section is OperationSection.MutateOutput) { - rust("let output = output._meta(generic);") + rust("let output = output.meta(generic);") } } }, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt index 7538474a83..4a4f6da8b0 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt @@ -44,7 +44,7 @@ class ErrorGeneratorTest { use aws_smithy_types::retry::ErrorKind; let err = MyError::builder() - ._meta(GenericError::builder().code("test").message("testmsg").build()) + .meta(GenericError::builder().code("test").message("testmsg").build()) .message("testmsg") .build(); assert_eq!(err.retryable_error_kind(), ErrorKind::ServerError); diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWords.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWords.kt index efe9ae7cc8..34a4382302 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWords.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustReservedWords.kt @@ -40,6 +40,8 @@ class RustReservedWordSymbolProvider(private val base: RustSymbolProvider, priva "make_operation" -> "make_operation_value" "presigned" -> "presigned_value" "customize" -> "customize_value" + // To avoid conflicts with the error metadata `meta` field + "meta" -> "meta_value" else -> baseName } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 4571abe080..ad5e91fd31 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -343,7 +343,7 @@ class EventStreamUnmarshallerGenerator( .map_err(|err| { #{Error}::unmarshalling(format!("failed to unmarshall ${member.memberName}: {}", err)) })?; - builder._set_meta(Some(generic)); + builder.set_meta(Some(generic)); return Ok(#{UnmarshalledMessage}::Error( #{OpError}::${member.target.name}(builder.build()) )) From 44c1dd0437bb9717283b57770d8bddf77b61f588 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 1 Feb 2023 12:10:53 -0800 Subject: [PATCH 33/39] Rename the `ErrorMetadata` trait to `ProvideErrorMetadata` --- CHANGELOG.next.toml | 8 ++++---- aws/rust-runtime/aws-http/src/request_id.rs | 2 +- aws/rust-runtime/aws-inlineable/src/s3_request_id.rs | 2 +- .../amazon/smithy/rustsdk/BaseRequestIdDecorator.kt | 2 +- .../client/smithy/generators/error/ErrorGenerator.kt | 2 +- .../smithy/generators/error/OperationErrorGenerator.kt | 6 +++--- .../smithy/generators/error/ServiceErrorGenerator.kt | 4 ++-- .../client/smithy/generators/error/ErrorGeneratorTest.kt | 2 +- .../amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt | 2 +- .../core/smithy/customizations/SmithyTypesPubUseExtra.kt | 2 +- rust-runtime/aws-smithy-http/src/result.rs | 6 +++--- rust-runtime/aws-smithy-types/src/error.rs | 2 +- rust-runtime/aws-smithy-types/src/error/unhandled.rs | 4 ++-- 13 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 0ae4f65921..d25567dbd1 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -86,13 +86,13 @@ meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client"} author = "jdisanti" [[aws-sdk-rust]] -message = "The `message()` and `code()` methods on errors have been moved into `ErrorMetadata` trait. This trait will need to be imported to continue calling these." +message = "The `message()` and `code()` methods on errors have been moved into `ProvideErrorMetadata` trait. This trait will need to be imported to continue calling these." references = ["smithy-rs#76", "smithy-rs#2129"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "jdisanti" [[smithy-rs]] -message = "The `message()` and `code()` methods on errors have been moved into `ErrorMetadata` trait. This trait will need to be imported to continue calling these." +message = "The `message()` and `code()` methods on errors have been moved into `ProvideErrorMetadata` trait. This trait will need to be imported to continue calling these." references = ["smithy-rs#76", "smithy-rs#2129"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client"} author = "jdisanti" @@ -125,7 +125,7 @@ match result { **After:** ```rust // Needed to access the `.code()` function on the error type: -use aws_sdk_s3::types::ErrorMetadata; +use aws_sdk_s3::types::ProvideErrorMetadata; let result = client .get_object() .bucket(BUCKET_NAME) @@ -180,7 +180,7 @@ match result { **After:** ```rust // Needed to access the `.code()` function on the error type: -use aws_sdk_s3::types::ErrorMetadata; +use aws_sdk_s3::types::ProvideErrorMetadata; let result = client .get_object() .bucket(BUCKET_NAME) diff --git a/aws/rust-runtime/aws-http/src/request_id.rs b/aws/rust-runtime/aws-http/src/request_id.rs index 9ad99377b2..9741b7b01e 100644 --- a/aws/rust-runtime/aws-http/src/request_id.rs +++ b/aws/rust-runtime/aws-http/src/request_id.rs @@ -7,7 +7,7 @@ use aws_smithy_http::http::HttpHeaders; use aws_smithy_http::operation; use aws_smithy_http::result::SdkError; use aws_smithy_types::error::{ - Builder as GenericErrorBuilder, Error as GenericError, ErrorMetadata, Unhandled, + Builder as GenericErrorBuilder, Error as GenericError, ProvideErrorMetadata, Unhandled, }; use http::{HeaderMap, HeaderValue}; diff --git a/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs index b4eae20345..b2e31ca8f5 100644 --- a/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs +++ b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs @@ -7,7 +7,7 @@ use aws_smithy_client::SdkError; use aws_smithy_http::http::HttpHeaders; use aws_smithy_http::operation; use aws_smithy_types::error::{ - Builder as GenericErrorBuilder, Error as GenericError, ErrorMetadata, Unhandled, + Builder as GenericErrorBuilder, Error as GenericError, ProvideErrorMetadata, Unhandled, }; use http::{HeaderMap, HeaderValue}; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt index c34722c6d2..4a09d1de9f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt @@ -142,7 +142,7 @@ abstract class BaseRequestIdDecorator : ClientCodegenDecorator { is ErrorImplSection.ErrorAdditionalTraitImpls -> { rustBlock("impl #1T for #2T", accessorTrait(codegenContext), section.errorType) { rustBlock("fn $accessorFunctionName(&self) -> Option<&str>") { - rust("use #T;", RuntimeType.errorMetadataTrait(codegenContext.runtimeConfig)) + rust("use #T;", RuntimeType.provideErrorMetadataTrait(codegenContext.runtimeConfig)) rust("self.meta().$accessorFunctionName()") } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt index 48fc95d10f..b66b6f25a6 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt @@ -103,7 +103,7 @@ class ErrorGenerator( ErrorImplGenerator(model, symbolProvider, writer, shape, error, implCustomizations).render(CodegenTarget.CLIENT) - writer.rustBlock("impl #T for ${symbol.name}", RuntimeType.errorMetadataTrait(runtimeConfig)) { + writer.rustBlock("impl #T for ${symbol.name}", RuntimeType.provideErrorMetadataTrait(runtimeConfig)) { rust("fn meta(&self) -> &#T { &self.meta }", genericError(runtimeConfig)) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt index 9ba29dfa6b..71a02fe9e5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt @@ -144,7 +144,7 @@ class OperationErrorGenerator( } } - val errorMetadataTrait = RuntimeType.errorMetadataTrait(runtimeConfig) + val errorMetadataTrait = RuntimeType.provideErrorMetadataTrait(runtimeConfig) writer.rustBlock("impl #T for ${errorType.name}", errorMetadataTrait) { rustBlock("fn meta(&self) -> &#T", genericError(runtimeConfig)) { delegateToVariants(errors) { @@ -161,7 +161,7 @@ class OperationErrorGenerator( RuntimeType.provideErrorKind(symbolProvider.config().runtimeConfig), ) { rustBlock("fn code(&self) -> Option<&str>") { - rust("#T::code(self)", RuntimeType.errorMetadataTrait(runtimeConfig)) + rust("#T::code(self)", RuntimeType.provideErrorMetadataTrait(runtimeConfig)) } rustBlock("fn retryable_error_kind(&self) -> Option<#T>", retryErrorKindT) { @@ -204,7 +204,7 @@ class OperationErrorGenerator( """, ) writer.rustBlock("pub fn meta(&self) -> &#T", genericError) { - rust("use #T;", RuntimeType.errorMetadataTrait(runtimeConfig)) + rust("use #T;", RuntimeType.provideErrorMetadataTrait(runtimeConfig)) rustBlock("match self") { errors.forEach { error -> val errorVariantSymbol = symbolProvider.toSymbol(error) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt index 7be341cd1e..349e83731c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt @@ -116,13 +116,13 @@ class ServiceErrorGenerator( """ _ => Error::Unhandled( #{Unhandled}::builder() - .meta(#{ErrorMetadata}::meta(&err).clone()) + .meta(#{ProvideErrorMetadata}::meta(&err).clone()) .source(err) .build() ), """, "Unhandled" to unhandledError(codegenContext.runtimeConfig), - "ErrorMetadata" to RuntimeType.errorMetadataTrait(codegenContext.runtimeConfig), + "ProvideErrorMetadata" to RuntimeType.provideErrorMetadataTrait(codegenContext.runtimeConfig), ) } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt index 4a4f6da8b0..8d043bc61b 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt @@ -40,7 +40,7 @@ class ErrorGeneratorTest { """ ##[test] fn test_error_generator() { - use aws_smithy_types::error::{Error as GenericError, ErrorMetadata}; + use aws_smithy_types::error::{Error as GenericError, ProvideErrorMetadata}; use aws_smithy_types::retry::ErrorKind; let err = MyError::builder() diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index b48a8e1cec..7d75c41f56 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -264,7 +264,7 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::Receiver") fun genericError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Error") fun genericErrorBuilder(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Builder") - fun errorMetadataTrait(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::ErrorMetadata") + fun provideErrorMetadataTrait(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::ProvideErrorMetadata") fun unhandledError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Unhandled") fun jsonErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.jsonErrors(runtimeConfig)) fun labelFormat(runtimeConfig: RuntimeConfig, func: String) = smithyHttp(runtimeConfig).resolve("label::$func") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt index 2c11de4bfc..8a9d7bd162 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt @@ -54,7 +54,7 @@ internal fun pubUseTypes(runtimeConfig: RuntimeConfig, model: Model): List listOf( PubUseType(types.resolve("error::display::DisplayErrorContext")) { true }, - PubUseType(types.resolve("error::ErrorMetadata")) { true }, + PubUseType(types.resolve("error::ProvideErrorMetadata")) { true }, ) } + RuntimeType.smithyHttp(runtimeConfig).let { http -> listOf( diff --git a/rust-runtime/aws-smithy-http/src/result.rs b/rust-runtime/aws-smithy-http/src/result.rs index 8af9b7ded7..6404483123 100644 --- a/rust-runtime/aws-smithy-http/src/result.rs +++ b/rust-runtime/aws-smithy-http/src/result.rs @@ -13,7 +13,7 @@ //! `Result` wrapper types for [success](SdkSuccess) and [failure](SdkError) responses. use crate::operation; -use aws_smithy_types::error::{Error as GenericError, ErrorMetadata, EMPTY_GENERIC_ERROR}; +use aws_smithy_types::error::{Error as GenericError, ProvideErrorMetadata, EMPTY_GENERIC_ERROR}; use aws_smithy_types::retry::ErrorKind; use std::error::Error; use std::fmt; @@ -284,9 +284,9 @@ where } } -impl ErrorMetadata for SdkError +impl ProvideErrorMetadata for SdkError where - E: ErrorMetadata, + E: ProvideErrorMetadata, { fn meta(&self) -> &aws_smithy_types::Error { match self { diff --git a/rust-runtime/aws-smithy-types/src/error.rs b/rust-runtime/aws-smithy-types/src/error.rs index 02472eb150..dccba61126 100644 --- a/rust-runtime/aws-smithy-types/src/error.rs +++ b/rust-runtime/aws-smithy-types/src/error.rs @@ -15,7 +15,7 @@ mod unhandled; pub use unhandled::Unhandled; /// Trait to retrieve error metadata from a result -pub trait ErrorMetadata { +pub trait ProvideErrorMetadata { /// Returns error metadata, which includes the error code, message, /// request ID, and potentially additional information. fn meta(&self) -> &Error; diff --git a/rust-runtime/aws-smithy-types/src/error/unhandled.rs b/rust-runtime/aws-smithy-types/src/error/unhandled.rs index 5a0bde0595..f925f78cb6 100644 --- a/rust-runtime/aws-smithy-types/src/error/unhandled.rs +++ b/rust-runtime/aws-smithy-types/src/error/unhandled.rs @@ -5,7 +5,7 @@ //! Unhandled error type. -use crate::error::{Error as GenericError, ErrorMetadata}; +use crate::error::{Error as GenericError, ProvideErrorMetadata}; use std::error::Error as StdError; /// Builder for [`Unhandled`] @@ -83,7 +83,7 @@ impl StdError for Unhandled { } } -impl ErrorMetadata for Unhandled { +impl ProvideErrorMetadata for Unhandled { fn meta(&self) -> &GenericError { &self.meta } From fe1ec5aee3f8f85dbd219c8b7d66e7005466aa9c Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 1 Feb 2023 13:18:31 -0800 Subject: [PATCH 34/39] Rename `aws_smithy_types::Error` to `ErrorMetadata` --- CHANGELOG.next.toml | 12 ++ aws/rust-runtime/aws-http/src/request_id.rs | 23 +-- .../aws-inlineable/src/s3_request_id.rs | 23 +-- .../smithy/rustsdk/BaseRequestIdDecorator.kt | 2 +- .../rustsdk/customize/s3/S3Decorator.kt | 12 +- .../sts/tests/retry_idp_comms_err.rs | 4 +- .../smithy/generators/error/ErrorGenerator.kt | 14 +- .../error/OperationErrorGenerator.kt | 16 +- .../protocols/HttpBoundProtocolGenerator.kt | 4 +- .../generators/error/ErrorGeneratorTest.kt | 4 +- .../rust/codegen/core/smithy/RuntimeType.kt | 6 +- .../customizations/SmithyTypesPubUseExtra.kt | 2 +- .../customize/OperationCustomization.kt | 6 +- .../codegen/core/smithy/protocols/AwsJson.kt | 18 +- .../codegen/core/smithy/protocols/AwsQuery.kt | 18 +- .../codegen/core/smithy/protocols/Ec2Query.kt | 18 +- .../codegen/core/smithy/protocols/Protocol.kt | 8 +- .../codegen/core/smithy/protocols/RestJson.kt | 18 +- .../codegen/core/smithy/protocols/RestXml.kt | 18 +- .../parse/EventStreamUnmarshallerGenerator.kt | 4 +- .../EventStreamUnmarshallTestCases.kt | 4 +- rust-runtime/aws-smithy-http/src/result.rs | 15 +- rust-runtime/aws-smithy-types/src/error.rs | 162 +---------------- .../aws-smithy-types/src/error/metadata.rs | 166 ++++++++++++++++++ .../aws-smithy-types/src/error/unhandled.rs | 12 +- rust-runtime/aws-smithy-types/src/lib.rs | 8 +- .../inlineable/src/ec2_query_errors.rs | 10 +- rust-runtime/inlineable/src/json_errors.rs | 16 +- .../src/rest_xml_unwrapped_errors.rs | 10 +- .../inlineable/src/rest_xml_wrapped_errors.rs | 10 +- 30 files changed, 337 insertions(+), 306 deletions(-) create mode 100644 rust-runtime/aws-smithy-types/src/error/metadata.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index d25567dbd1..3ab5e15e9d 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -206,3 +206,15 @@ match result { references = ["smithy-rs#76", "smithy-rs#2129", "smithy-rs#2075"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client"} author = "jdisanti" + +[[smithy-rs]] +message = "`aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`." +references = ["smithy-rs#76", "smithy-rs#2129"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client"} +author = "jdisanti" + +[[aws-sdk-rust]] +message = "`aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`." +references = ["smithy-rs#76", "smithy-rs#2129"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "jdisanti" diff --git a/aws/rust-runtime/aws-http/src/request_id.rs b/aws/rust-runtime/aws-http/src/request_id.rs index 9741b7b01e..829077092a 100644 --- a/aws/rust-runtime/aws-http/src/request_id.rs +++ b/aws/rust-runtime/aws-http/src/request_id.rs @@ -6,9 +6,10 @@ use aws_smithy_http::http::HttpHeaders; use aws_smithy_http::operation; use aws_smithy_http::result::SdkError; -use aws_smithy_types::error::{ - Builder as GenericErrorBuilder, Error as GenericError, ProvideErrorMetadata, Unhandled, +use aws_smithy_types::error::metadata::{ + Builder as ErrorMetadataBuilder, ErrorMetadata, ProvideErrorMetadata, }; +use aws_smithy_types::error::Unhandled; use http::{HeaderMap, HeaderValue}; /// Constant for the [`aws_smithy_types::error::Error`] extra field that contains the request ID @@ -33,7 +34,7 @@ where } } -impl RequestId for GenericError { +impl RequestId for ErrorMetadata { fn request_id(&self) -> Option<&str> { self.extra(AWS_REQUEST_ID) } @@ -73,9 +74,9 @@ where /// Applies a request ID to a generic error builder #[doc(hidden)] pub fn apply_request_id( - builder: GenericErrorBuilder, + builder: ErrorMetadataBuilder, headers: &HeaderMap, -) -> GenericErrorBuilder { +) -> ErrorMetadataBuilder { if let Some(request_id) = extract_request_id(headers) { builder.custom(AWS_REQUEST_ID, request_id) } else { @@ -155,8 +156,8 @@ mod tests { fn test_apply_request_id() { let mut headers = HeaderMap::new(); assert_eq!( - GenericError::builder().build(), - apply_request_id(GenericError::builder(), &headers).build(), + ErrorMetadata::builder().build(), + apply_request_id(ErrorMetadata::builder(), &headers).build(), ); headers.append( @@ -164,16 +165,16 @@ mod tests { HeaderValue::from_static("some-request-id"), ); assert_eq!( - GenericError::builder() + ErrorMetadata::builder() .custom(AWS_REQUEST_ID, "some-request-id") .build(), - apply_request_id(GenericError::builder(), &headers).build(), + apply_request_id(ErrorMetadata::builder(), &headers).build(), ); } #[test] - fn test_generic_error_request_id_impl() { - let err = GenericError::builder() + fn test_error_metadata_request_id_impl() { + let err = ErrorMetadata::builder() .custom(AWS_REQUEST_ID, "some-request-id") .build(); assert_eq!(Some("some-request-id"), err.request_id()); diff --git a/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs index b2e31ca8f5..909dcbcd7a 100644 --- a/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs +++ b/aws/rust-runtime/aws-inlineable/src/s3_request_id.rs @@ -6,9 +6,10 @@ use aws_smithy_client::SdkError; use aws_smithy_http::http::HttpHeaders; use aws_smithy_http::operation; -use aws_smithy_types::error::{ - Builder as GenericErrorBuilder, Error as GenericError, ProvideErrorMetadata, Unhandled, +use aws_smithy_types::error::metadata::{ + Builder as ErrorMetadataBuilder, ErrorMetadata, ProvideErrorMetadata, }; +use aws_smithy_types::error::Unhandled; use http::{HeaderMap, HeaderValue}; const EXTENDED_REQUEST_ID: &str = "s3_extended_request_id"; @@ -34,7 +35,7 @@ where } } -impl RequestIdExt for GenericError { +impl RequestIdExt for ErrorMetadata { fn extended_request_id(&self) -> Option<&str> { self.extra(EXTENDED_REQUEST_ID) } @@ -74,9 +75,9 @@ where /// Applies the extended request ID to a generic error builder #[doc(hidden)] pub fn apply_extended_request_id( - builder: GenericErrorBuilder, + builder: ErrorMetadataBuilder, headers: &HeaderMap, -) -> GenericErrorBuilder { +) -> ErrorMetadataBuilder { if let Some(extended_request_id) = extract_extended_request_id(headers) { builder.custom(EXTENDED_REQUEST_ID, extended_request_id) } else { @@ -154,22 +155,22 @@ mod test { fn test_apply_extended_request_id() { let mut headers = HeaderMap::new(); assert_eq!( - GenericError::builder().build(), - apply_extended_request_id(GenericError::builder(), &headers).build(), + ErrorMetadata::builder().build(), + apply_extended_request_id(ErrorMetadata::builder(), &headers).build(), ); headers.append("x-amz-id-2", HeaderValue::from_static("some-request-id")); assert_eq!( - GenericError::builder() + ErrorMetadata::builder() .custom(EXTENDED_REQUEST_ID, "some-request-id") .build(), - apply_extended_request_id(GenericError::builder(), &headers).build(), + apply_extended_request_id(ErrorMetadata::builder(), &headers).build(), ); } #[test] - fn test_generic_error_extended_request_id_impl() { - let err = GenericError::builder() + fn test_error_metadata_extended_request_id_impl() { + let err = ErrorMetadata::builder() .custom(EXTENDED_REQUEST_ID, "some-request-id") .build(); assert_eq!(Some("some-request-id"), err.extended_request_id()); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt index 4a09d1de9f..accf010045 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt @@ -76,7 +76,7 @@ abstract class BaseRequestIdDecorator : ClientCodegenDecorator { OperationCustomization() { override fun section(section: OperationSection): Writable = writable { when (section) { - is OperationSection.PopulateGenericErrorExtras -> { + is OperationSection.PopulateErrorMetadataExtras -> { rustTemplate( "${section.builderName} = #{apply_to_error}(${section.builderName}, ${section.responseName}.headers());", "apply_to_error" to applyToError(codegenContext), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index 2221fcfd1b..7b5f90037e 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -92,18 +92,18 @@ class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContex private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.genericError(runtimeConfig), - "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), + "Error" to RuntimeType.errorMetadata(runtimeConfig), + "ErrorBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), "base_errors" to restXmlErrors, ) - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType { - return RuntimeType.forInlineFun("parse_http_generic_error", RustModule.private("xml_deser")) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType { + return RuntimeType.forInlineFun("parse_http_error_metadata", RustModule.private("xml_deser")) { rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", + "pub fn parse_http_error_metadata(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", *errorScope, ) { rustTemplate( @@ -117,7 +117,7 @@ class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContex } Ok(builder) } else { - #{base_errors}::parse_generic_error(response.body().as_ref()) + #{base_errors}::parse_error_metadata(response.body().as_ref()) } """, *errorScope, diff --git a/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs b/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs index edb6f52fb2..3b546bbb0b 100644 --- a/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs +++ b/aws/sdk/integration-tests/sts/tests/retry_idp_comms_err.rs @@ -4,7 +4,7 @@ */ use aws_sdk_sts as sts; -use aws_smithy_types::error::Error as ErrorMeta; +use aws_smithy_types::error::ErrorMetadata; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; use sts::error::{AssumeRoleWithWebIdentityError, IdpCommunicationErrorException}; @@ -14,7 +14,7 @@ async fn idp_comms_err_retryable() { IdpCommunicationErrorException::builder() .message("test") .meta( - ErrorMeta::builder() + ErrorMetadata::builder() .code("IDPCommunicationError") .message("test") .build(), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt index b66b6f25a6..ffd4c9a287 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGenerator.kt @@ -17,7 +17,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.genericError +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.errorMetadata import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator @@ -47,7 +47,7 @@ class ErrorGenerator( override fun section(section: StructureSection): Writable = writable { when (section) { is StructureSection.AdditionalFields -> { - rust("pub(crate) meta: #T,", genericError(runtimeConfig)) + rust("pub(crate) meta: #T,", errorMetadata(runtimeConfig)) } is StructureSection.AdditionalDebugFields -> { rust("""${section.formatterName}.field("meta", &self.meta);""") @@ -65,25 +65,25 @@ class ErrorGenerator( override fun section(section: BuilderSection): Writable = writable { when (section) { is BuilderSection.AdditionalFields -> { - rust("meta: Option<#T>,", genericError(runtimeConfig)) + rust("meta: Option<#T>,", errorMetadata(runtimeConfig)) } is BuilderSection.AdditionalMethods -> { rustTemplate( """ /// Sets error metadata - pub fn meta(mut self, meta: #{generic_error}) -> Self { + pub fn meta(mut self, meta: #{error_metadata}) -> Self { self.meta = Some(meta); self } /// Sets error metadata - pub fn set_meta(&mut self, meta: Option<#{generic_error}>) -> &mut Self { + pub fn set_meta(&mut self, meta: Option<#{error_metadata}>) -> &mut Self { self.meta = meta; self } """, - "generic_error" to genericError(runtimeConfig), + "error_metadata" to errorMetadata(runtimeConfig), ) } @@ -104,7 +104,7 @@ class ErrorGenerator( ErrorImplGenerator(model, symbolProvider, writer, shape, error, implCustomizations).render(CodegenTarget.CLIENT) writer.rustBlock("impl #T for ${symbol.name}", RuntimeType.provideErrorMetadataTrait(runtimeConfig)) { - rust("fn meta(&self) -> &#T { &self.meta }", genericError(runtimeConfig)) + rust("fn meta(&self) -> &#T { &self.meta }", errorMetadata(runtimeConfig)) } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt index 71a02fe9e5..3180851ef4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt @@ -25,7 +25,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.genericError +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.errorMetadata import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.unhandledError import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.Section @@ -61,7 +61,7 @@ class OperationErrorGenerator( private val customizations: List, ) { private val runtimeConfig = symbolProvider.config().runtimeConfig - private val genericError = genericError(symbolProvider.config().runtimeConfig) + private val errorMetadata = errorMetadata(symbolProvider.config().runtimeConfig) private val createUnhandledError = RuntimeType.smithyHttp(runtimeConfig).resolve("result::CreateUnhandledError") @@ -122,7 +122,7 @@ class OperationErrorGenerator( meta: Option<#T> ) -> Self """, - genericError, + errorMetadata, ) { rust( """ @@ -146,7 +146,7 @@ class OperationErrorGenerator( val errorMetadataTrait = RuntimeType.provideErrorMetadataTrait(runtimeConfig) writer.rustBlock("impl #T for ${errorType.name}", errorMetadataTrait) { - rustBlock("fn meta(&self) -> &#T", genericError(runtimeConfig)) { + rustBlock("fn meta(&self) -> &#T", errorMetadata(runtimeConfig)) { delegateToVariants(errors) { writable { rust("#T::meta(_inner)", errorMetadataTrait) } } @@ -188,12 +188,12 @@ class OperationErrorGenerator( Self::Unhandled(#{Unhandled}::builder().source(err).build()) } - /// Creates the `${errorType.name}::Unhandled` variant from a `#{generic_error}`. - pub fn generic(err: #{generic_error}) -> Self { + /// Creates the `${errorType.name}::Unhandled` variant from a `#{error_metadata}`. + pub fn generic(err: #{error_metadata}) -> Self { Self::Unhandled(#{Unhandled}::builder().source(err.clone()).meta(err).build()) } """, - "generic_error" to genericError, + "error_metadata" to errorMetadata, "std_error" to RuntimeType.StdError, "Unhandled" to unhandledError(runtimeConfig), ) @@ -203,7 +203,7 @@ class OperationErrorGenerator( request ID, and potentially additional information. """, ) - writer.rustBlock("pub fn meta(&self) -> &#T", genericError) { + writer.rustBlock("pub fn meta(&self) -> &#T", errorMetadata) { rust("use #T;", RuntimeType.provideErrorMetadataTrait(runtimeConfig)) rustBlock("match self") { errors.forEach { error -> diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index bf5b48df27..3625f6ebda 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -183,12 +183,12 @@ class HttpBoundProtocolTraitImplGenerator( Attribute.AllowUnusedMut.render(this) rust( "let mut generic_builder = #T(response).map_err(#T::unhandled)?;", - protocol.parseHttpGenericError(operationShape), + protocol.parseHttpErrorMetadata(operationShape), errorSymbol, ) writeCustomizations( customizations, - OperationSection.PopulateGenericErrorExtras(customizations, "generic_builder", "response"), + OperationSection.PopulateErrorMetadataExtras(customizations, "generic_builder", "response"), ) rust("let generic = generic_builder.build();") if (operationShape.operationErrors(model).isNotEmpty()) { diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt index 8d043bc61b..cca1dcb3d5 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ErrorGeneratorTest.kt @@ -40,11 +40,11 @@ class ErrorGeneratorTest { """ ##[test] fn test_error_generator() { - use aws_smithy_types::error::{Error as GenericError, ProvideErrorMetadata}; + use aws_smithy_types::error::metadata::{ErrorMetadata, ProvideErrorMetadata}; use aws_smithy_types::retry::ErrorKind; let err = MyError::builder() - .meta(GenericError::builder().code("test").message("testmsg").build()) + .meta(ErrorMetadata::builder().code("test").message("testmsg").build()) .message("testmsg") .build(); assert_eq!(err.retryable_error_kind(), ErrorKind::ServerError); diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 7d75c41f56..62b959e4c3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -262,9 +262,9 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun document(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("Document") fun retryErrorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ErrorKind") fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::Receiver") - fun genericError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Error") - fun genericErrorBuilder(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Builder") - fun provideErrorMetadataTrait(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::ProvideErrorMetadata") + fun errorMetadata(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::ErrorMetadata") + fun errorMetadataBuilder(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::metadata::Builder") + fun provideErrorMetadataTrait(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::metadata::ProvideErrorMetadata") fun unhandledError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Unhandled") fun jsonErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.jsonErrors(runtimeConfig)) fun labelFormat(runtimeConfig: RuntimeConfig, func: String) = smithyHttp(runtimeConfig).resolve("label::$func") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt index 8a9d7bd162..307e91b19a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt @@ -54,7 +54,7 @@ internal fun pubUseTypes(runtimeConfig: RuntimeConfig, model: Model): List listOf( PubUseType(types.resolve("error::display::DisplayErrorContext")) { true }, - PubUseType(types.resolve("error::ProvideErrorMetadata")) { true }, + PubUseType(types.resolve("error::metadata::ProvideErrorMetadata")) { true }, ) } + RuntimeType.smithyHttp(runtimeConfig).let { http -> listOf( diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt index 88afcac4b4..d8fe3ab18b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt @@ -56,15 +56,15 @@ sealed class OperationSection(name: String) : Section(name) { /** * Allows for adding additional properties to the `extras` field on the - * `aws_smithy_types::error::Error` generic error type. + * `aws_smithy_types::error::ErrorMetadata`. */ - data class PopulateGenericErrorExtras( + data class PopulateErrorMetadataExtras( override val customizations: List, /** Name of the generic error builder (for referring to it in Rust code) */ val builderName: String, /** Name of the response (for referring to it in Rust code) */ val responseName: String, - ) : OperationSection("PopulateGenericErrorExtras") + ) : OperationSection("PopulateErrorMetadataExtras") /** * Hook to add custom code right before the response is parsed. diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt index baa26e7747..6ed98c4de3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt @@ -128,7 +128,7 @@ open class AwsJson( private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), + "ErrorMetadataBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.Http.resolve("HeaderMap"), "JsonError" to CargoDependency.smithyJson(runtimeConfig).toType() .resolve("deserialize::error::DeserializeError"), @@ -159,25 +159,25 @@ open class AwsJson( override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = AwsJsonSerializerGenerator(codegenContext, httpBindingResolver) - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_http_generic_error", jsonDeserModule) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = + RuntimeType.forInlineFun("parse_http_error_metadata", jsonDeserModule) { rustTemplate( """ - pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{JsonError}> { - #{json_errors}::parse_generic_error(response.body(), response.headers()) + pub fn parse_http_error_metadata(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorMetadataBuilder}, #{JsonError}> { + #{json_errors}::parse_error_metadata(response.body(), response.headers()) } """, *errorScope, ) } - override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_event_stream_generic_error", jsonDeserModule) { + override fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType = + RuntimeType.forInlineFun("parse_event_stream_error_metadata", jsonDeserModule) { rustTemplate( """ - pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{ErrorBuilder}, #{JsonError}> { + pub fn parse_event_stream_error_metadata(payload: &#{Bytes}) -> Result<#{ErrorMetadataBuilder}, #{JsonError}> { // Note: HeaderMap::new() doesn't allocate - #{json_errors}::parse_generic_error(payload, &#{HeaderMap}::new()) + #{json_errors}::parse_error_metadata(payload, &#{HeaderMap}::new()) } """, *errorScope, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt index d3334ec91a..f5728510e0 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt @@ -45,7 +45,7 @@ class AwsQueryProtocol(private val codegenContext: CodegenContext) : Protocol { private val awsQueryErrors: RuntimeType = RuntimeType.wrappedXmlErrors(runtimeConfig) private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), + "ErrorMetadataBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), @@ -65,23 +65,23 @@ class AwsQueryProtocol(private val codegenContext: CodegenContext) : Protocol { override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = AwsQuerySerializerGenerator(codegenContext) - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_http_generic_error", xmlDeserModule) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = + RuntimeType.forInlineFun("parse_http_error_metadata", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", + "pub fn parse_http_error_metadata(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(response.body().as_ref())", awsQueryErrors) + rust("#T::parse_error_metadata(response.body().as_ref())", awsQueryErrors) } } - override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_event_stream_generic_error", xmlDeserModule) { + override fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType = + RuntimeType.forInlineFun("parse_event_stream_error_metadata", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", + "pub fn parse_event_stream_error_metadata(payload: &#{Bytes}) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(payload.as_ref())", awsQueryErrors) + rust("#T::parse_error_metadata(payload.as_ref())", awsQueryErrors) } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt index 05e14bdac9..473ec06f52 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt @@ -27,7 +27,7 @@ class Ec2QueryProtocol(private val codegenContext: CodegenContext) : Protocol { private val ec2QueryErrors: RuntimeType = RuntimeType.ec2QueryErrors(runtimeConfig) private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), + "ErrorMetadataBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), @@ -56,23 +56,23 @@ class Ec2QueryProtocol(private val codegenContext: CodegenContext) : Protocol { override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = Ec2QuerySerializerGenerator(codegenContext) - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_http_generic_error", xmlDeserModule) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = + RuntimeType.forInlineFun("parse_http_error_metadata", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", + "pub fn parse_http_error_metadata(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(response.body().as_ref())", ec2QueryErrors) + rust("#T::parse_error_metadata(response.body().as_ref())", ec2QueryErrors) } } - override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_event_stream_generic_error", xmlDeserModule) { + override fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType = + RuntimeType.forInlineFun("parse_event_stream_error_metadata", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", + "pub fn parse_event_stream_error_metadata(payload: &#{Bytes}) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(payload.as_ref())", ec2QueryErrors) + rust("#T::parse_error_metadata(payload.as_ref())", ec2QueryErrors) } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt index 046cd76a7d..1b3e4b4a9a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt @@ -46,21 +46,21 @@ interface Protocol { /** * Generates a function signature like the following: * ```rust - * fn parse_http_generic_error(response: &Response) -> aws_smithy_types::error::Builder + * fn parse_http_error_metadata(response: &Response) -> aws_smithy_types::error::Builder * ``` */ - fun parseHttpGenericError(operationShape: OperationShape): RuntimeType + fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType /** * Generates a function signature like the following: * ```rust - * fn parse_event_stream_generic_error(payload: &Bytes) -> aws_smithy_types::error::Error + * fn parse_event_stream_error_metadata(payload: &Bytes) -> aws_smithy_types::error::Error * ``` * * Event Stream generic errors are almost identical to HTTP generic errors, except that * there are no response headers or statuses available to further inform the error parsing. */ - fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType + fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType } typealias ProtocolMap = Map> diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt index 9eeee79038..665d9dee85 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt @@ -66,7 +66,7 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), + "ErrorMetadataBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.Http.resolve("HeaderMap"), "JsonError" to CargoDependency.smithyJson(runtimeConfig).toType() .resolve("deserialize::error::DeserializeError"), @@ -102,25 +102,25 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = JsonSerializerGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_http_generic_error", jsonDeserModule) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = + RuntimeType.forInlineFun("parse_http_error_metadata", jsonDeserModule) { rustTemplate( """ - pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{JsonError}> { - #{json_errors}::parse_generic_error(response.body(), response.headers()) + pub fn parse_http_error_metadata(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorMetadataBuilder}, #{JsonError}> { + #{json_errors}::parse_error_metadata(response.body(), response.headers()) } """, *errorScope, ) } - override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_event_stream_generic_error", jsonDeserModule) { + override fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType = + RuntimeType.forInlineFun("parse_event_stream_error_metadata", jsonDeserModule) { rustTemplate( """ - pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{ErrorBuilder}, #{JsonError}> { + pub fn parse_event_stream_error_metadata(payload: &#{Bytes}) -> Result<#{ErrorMetadataBuilder}, #{JsonError}> { // Note: HeaderMap::new() doesn't allocate - #{json_errors}::parse_generic_error(payload, &#{HeaderMap}::new()) + #{json_errors}::parse_error_metadata(payload, &#{HeaderMap}::new()) } """, *errorScope, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt index c42fefd3b4..3b4d10ddbf 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt @@ -27,7 +27,7 @@ open class RestXml(val codegenContext: CodegenContext) : Protocol { private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "ErrorBuilder" to RuntimeType.genericErrorBuilder(runtimeConfig), + "ErrorMetadataBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, "XmlDecodeError" to RuntimeType.smithyXml(runtimeConfig).resolve("decode::XmlDecodeError"), @@ -55,23 +55,23 @@ open class RestXml(val codegenContext: CodegenContext) : Protocol { return XmlBindingTraitSerializerGenerator(codegenContext, httpBindingResolver) } - override fun parseHttpGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_http_generic_error", xmlDeserModule) { + override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = + RuntimeType.forInlineFun("parse_http_error_metadata", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_http_generic_error(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", + "pub fn parse_http_error_metadata(response: &#{Response}<#{Bytes}>) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(response.body().as_ref())", restXmlErrors) + rust("#T::parse_error_metadata(response.body().as_ref())", restXmlErrors) } } - override fun parseEventStreamGenericError(operationShape: OperationShape): RuntimeType = - RuntimeType.forInlineFun("parse_event_stream_generic_error", xmlDeserModule) { + override fun parseEventStreamErrorMetadata(operationShape: OperationShape): RuntimeType = + RuntimeType.forInlineFun("parse_event_stream_error_metadata", xmlDeserModule) { rustBlockTemplate( - "pub fn parse_event_stream_generic_error(payload: &#{Bytes}) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", + "pub fn parse_event_stream_error_metadata(payload: &#{Bytes}) -> Result<#{ErrorMetadataBuilder}, #{XmlDecodeError}>", *errorScope, ) { - rust("#T::parse_generic_error(payload.as_ref())", restXmlErrors) + rust("#T::parse_error_metadata(payload.as_ref())", restXmlErrors) } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index ad5e91fd31..c3d442187a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -306,12 +306,12 @@ class EventStreamUnmarshallerGenerator( CodegenTarget.CLIENT -> { rustTemplate( """ - let generic = match #{parse_generic_error}(message.payload()) { + let generic = match #{parse_error_metadata}(message.payload()) { Ok(builder) => builder.build(), Err(err) => return Ok(#{UnmarshalledMessage}::Error(#{OpError}::unhandled(err))), }; """, - "parse_generic_error" to protocol.parseEventStreamGenericError(operationShape), + "parse_error_metadata" to protocol.parseEventStreamErrorMetadata(operationShape), *codegenScope, ) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt index e4e6bdb141..36b3efafd0 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/EventStreamUnmarshallTestCases.kt @@ -230,7 +230,7 @@ internal object EventStreamUnmarshallTestCases { if (codegenTarget == CodegenTarget.CLIENT) { unitTest( - "generic_error", + "error_metadata", """ let message = msg( "exception", @@ -246,7 +246,7 @@ internal object EventStreamUnmarshallTestCases { let expected = "message: \"unmodeled error\""; assert!(message.contains(expected), "Expected '{message}' to contain '{expected}'"); } - kind => panic!("expected generic error, but got {:?}", kind), + kind => panic!("expected error metadata, but got {:?}", kind), } """, ) diff --git a/rust-runtime/aws-smithy-http/src/result.rs b/rust-runtime/aws-smithy-http/src/result.rs index 6404483123..b00667d2c9 100644 --- a/rust-runtime/aws-smithy-http/src/result.rs +++ b/rust-runtime/aws-smithy-http/src/result.rs @@ -13,7 +13,8 @@ //! `Result` wrapper types for [success](SdkSuccess) and [failure](SdkError) responses. use crate::operation; -use aws_smithy_types::error::{Error as GenericError, ProvideErrorMetadata, EMPTY_GENERIC_ERROR}; +use aws_smithy_types::error::metadata::{ProvideErrorMetadata, EMPTY_ERROR_METADATA}; +use aws_smithy_types::error::ErrorMetadata; use aws_smithy_types::retry::ErrorKind; use std::error::Error; use std::fmt; @@ -130,7 +131,7 @@ pub trait CreateUnhandledError { /// Creates an unhandled error variant with the given `source` and error metadata. fn create_unhandled_error( source: Box, - meta: Option, + meta: Option, ) -> Self; } @@ -212,7 +213,7 @@ impl SdkError { /// # impl CreateUnhandledError for GetObjectError { /// # fn create_unhandled_error( /// # _: Box, - /// # _: Option, + /// # _: Option, /// # ) -> Self { unimplemented!() } /// # } /// # fn example() -> Result<(), GetObjectError> { @@ -290,10 +291,10 @@ where { fn meta(&self) -> &aws_smithy_types::Error { match self { - Self::ConstructionFailure(_) => &EMPTY_GENERIC_ERROR, - Self::TimeoutError(_) => &EMPTY_GENERIC_ERROR, - Self::DispatchFailure(_) => &EMPTY_GENERIC_ERROR, - Self::ResponseError(_) => &EMPTY_GENERIC_ERROR, + Self::ConstructionFailure(_) => &EMPTY_ERROR_METADATA, + Self::TimeoutError(_) => &EMPTY_ERROR_METADATA, + Self::DispatchFailure(_) => &EMPTY_ERROR_METADATA, + Self::ResponseError(_) => &EMPTY_ERROR_METADATA, Self::ServiceError(err) => err.source.meta(), } } diff --git a/rust-runtime/aws-smithy-types/src/error.rs b/rust-runtime/aws-smithy-types/src/error.rs index dccba61126..b83d6ffe07 100644 --- a/rust-runtime/aws-smithy-types/src/error.rs +++ b/rust-runtime/aws-smithy-types/src/error.rs @@ -3,173 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Generic errors for Smithy codegen +//! Errors for Smithy codegen -use crate::retry::{ErrorKind, ProvideErrorKind}; -use std::collections::HashMap; use std::fmt; pub mod display; +pub mod metadata; mod unhandled; +pub use metadata::ErrorMetadata; pub use unhandled::Unhandled; -/// Trait to retrieve error metadata from a result -pub trait ProvideErrorMetadata { - /// Returns error metadata, which includes the error code, message, - /// request ID, and potentially additional information. - fn meta(&self) -> &Error; - - /// Returns the error code if it's available. - fn code(&self) -> Option<&str> { - self.meta().code() - } - - /// Returns the error message, if there is one. - fn message(&self) -> Option<&str> { - self.meta().message() - } -} - -/// Empty generic error metadata -#[doc(hidden)] -pub const EMPTY_GENERIC_ERROR: Error = Error { - code: None, - message: None, - extras: None, -}; - -/// Generic Error type -/// -/// For many services, Errors are modeled. However, many services only partially model errors or don't -/// model errors at all. In these cases, the SDK will return this generic error type to expose the -/// `code`, `message` and `request_id`. -#[derive(Debug, Eq, PartialEq, Default, Clone)] -pub struct Error { - code: Option, - message: Option, - extras: Option>, -} - -/// Builder for [`Error`]. -#[derive(Debug, Default)] -pub struct Builder { - inner: Error, -} - -impl Builder { - /// Sets the error message. - pub fn message(mut self, message: impl Into) -> Self { - self.inner.message = Some(message.into()); - self - } - - /// Sets the error code. - pub fn code(mut self, code: impl Into) -> Self { - self.inner.code = Some(code.into()); - self - } - - /// Set a custom field on the error metadata - /// - /// Typically, these will be accessed with an extension trait: - /// ```rust - /// use aws_smithy_types::Error; - /// const HOST_ID: &str = "host_id"; - /// trait S3ErrorExt { - /// fn extended_request_id(&self) -> Option<&str>; - /// } - /// - /// impl S3ErrorExt for Error { - /// fn extended_request_id(&self) -> Option<&str> { - /// self.extra(HOST_ID) - /// } - /// } - /// - /// fn main() { - /// // Extension trait must be brought into scope - /// use S3ErrorExt; - /// let sdk_response: Result<(), Error> = Err(Error::builder().custom(HOST_ID, "x-1234").build()); - /// if let Err(err) = sdk_response { - /// println!("extended request id: {:?}", err.extended_request_id()); - /// } - /// } - /// ``` - pub fn custom(mut self, key: &'static str, value: impl Into) -> Self { - if self.inner.extras.is_none() { - self.inner.extras = Some(HashMap::new()); - } - self.inner - .extras - .as_mut() - .unwrap() - .insert(key, value.into()); - self - } - - /// Creates the error. - pub fn build(self) -> Error { - self.inner - } -} - -impl Error { - /// Returns the error code. - pub fn code(&self) -> Option<&str> { - self.code.as_deref() - } - /// Returns the error message. - pub fn message(&self) -> Option<&str> { - self.message.as_deref() - } - /// Returns additional information about the error if it's present. - pub fn extra(&self, key: &'static str) -> Option<&str> { - self.extras - .as_ref() - .and_then(|extras| extras.get(key).map(|k| k.as_str())) - } - - /// Creates an `Error` builder. - pub fn builder() -> Builder { - Builder::default() - } - - /// Converts an `Error` into a builder. - pub fn into_builder(self) -> Builder { - Builder { inner: self } - } -} - -impl ProvideErrorKind for Error { - fn retryable_error_kind(&self) -> Option { - None - } - - fn code(&self) -> Option<&str> { - Error::code(self) - } -} - -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); - } - if let Some(message) = &self.message { - fmt.field("message", message); - } - if let Some(extras) = &self.extras { - for (k, v) in extras { - fmt.field(k, &v); - } - } - fmt.finish() - } -} - -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. diff --git a/rust-runtime/aws-smithy-types/src/error/metadata.rs b/rust-runtime/aws-smithy-types/src/error/metadata.rs new file mode 100644 index 0000000000..06925e13f9 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/error/metadata.rs @@ -0,0 +1,166 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Error metadata + +use crate::retry::{ErrorKind, ProvideErrorKind}; +use std::collections::HashMap; +use std::fmt; + +/// Trait to retrieve error metadata from a result +pub trait ProvideErrorMetadata { + /// Returns error metadata, which includes the error code, message, + /// request ID, and potentially additional information. + fn meta(&self) -> &ErrorMetadata; + + /// Returns the error code if it's available. + fn code(&self) -> Option<&str> { + self.meta().code() + } + + /// Returns the error message, if there is one. + fn message(&self) -> Option<&str> { + self.meta().message() + } +} + +/// Empty error metadata +#[doc(hidden)] +pub const EMPTY_ERROR_METADATA: ErrorMetadata = ErrorMetadata { + code: None, + message: None, + extras: None, +}; + +/// Generic Error type +/// +/// For many services, Errors are modeled. However, many services only partially model errors or don't +/// model errors at all. In these cases, the SDK will return this generic error type to expose the +/// `code`, `message` and `request_id`. +#[derive(Debug, Eq, PartialEq, Default, Clone)] +pub struct ErrorMetadata { + code: Option, + message: Option, + extras: Option>, +} + +/// Builder for [`ErrorMetadata`]. +#[derive(Debug, Default)] +pub struct Builder { + inner: ErrorMetadata, +} + +impl Builder { + /// Sets the error message. + pub fn message(mut self, message: impl Into) -> Self { + self.inner.message = Some(message.into()); + self + } + + /// Sets the error code. + pub fn code(mut self, code: impl Into) -> Self { + self.inner.code = Some(code.into()); + self + } + + /// Set a custom field on the error metadata + /// + /// Typically, these will be accessed with an extension trait: + /// ```rust + /// use aws_smithy_types::Error; + /// const HOST_ID: &str = "host_id"; + /// trait S3ErrorExt { + /// fn extended_request_id(&self) -> Option<&str>; + /// } + /// + /// impl S3ErrorExt for Error { + /// fn extended_request_id(&self) -> Option<&str> { + /// self.extra(HOST_ID) + /// } + /// } + /// + /// fn main() { + /// // Extension trait must be brought into scope + /// use S3ErrorExt; + /// let sdk_response: Result<(), Error> = Err(Error::builder().custom(HOST_ID, "x-1234").build()); + /// if let Err(err) = sdk_response { + /// println!("extended request id: {:?}", err.extended_request_id()); + /// } + /// } + /// ``` + pub fn custom(mut self, key: &'static str, value: impl Into) -> Self { + if self.inner.extras.is_none() { + self.inner.extras = Some(HashMap::new()); + } + self.inner + .extras + .as_mut() + .unwrap() + .insert(key, value.into()); + self + } + + /// Creates the error. + pub fn build(self) -> ErrorMetadata { + self.inner + } +} + +impl ErrorMetadata { + /// Returns the error code. + pub fn code(&self) -> Option<&str> { + self.code.as_deref() + } + /// Returns the error message. + pub fn message(&self) -> Option<&str> { + self.message.as_deref() + } + /// Returns additional information about the error if it's present. + pub fn extra(&self, key: &'static str) -> Option<&str> { + self.extras + .as_ref() + .and_then(|extras| extras.get(key).map(|k| k.as_str())) + } + + /// Creates an `Error` builder. + pub fn builder() -> Builder { + Builder::default() + } + + /// Converts an `Error` into a builder. + pub fn into_builder(self) -> Builder { + Builder { inner: self } + } +} + +impl ProvideErrorKind for ErrorMetadata { + fn retryable_error_kind(&self) -> Option { + None + } + + fn code(&self) -> Option<&str> { + ErrorMetadata::code(self) + } +} + +impl fmt::Display for ErrorMetadata { + 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); + } + if let Some(message) = &self.message { + fmt.field("message", message); + } + if let Some(extras) = &self.extras { + for (k, v) in extras { + fmt.field(k, &v); + } + } + fmt.finish() + } +} + +impl std::error::Error for ErrorMetadata {} diff --git a/rust-runtime/aws-smithy-types/src/error/unhandled.rs b/rust-runtime/aws-smithy-types/src/error/unhandled.rs index f925f78cb6..2397d700ff 100644 --- a/rust-runtime/aws-smithy-types/src/error/unhandled.rs +++ b/rust-runtime/aws-smithy-types/src/error/unhandled.rs @@ -5,14 +5,14 @@ //! Unhandled error type. -use crate::error::{Error as GenericError, ProvideErrorMetadata}; +use crate::error::{metadata::ProvideErrorMetadata, ErrorMetadata}; use std::error::Error as StdError; /// Builder for [`Unhandled`] #[derive(Default, Debug)] pub struct Builder { source: Option>, - meta: Option, + meta: Option, } impl Builder { @@ -32,13 +32,13 @@ impl Builder { } /// Sets the error metadata - pub fn meta(mut self, meta: GenericError) -> Self { + pub fn meta(mut self, meta: ErrorMetadata) -> Self { self.meta = Some(meta); self } /// Sets the error metadata - pub fn set_meta(&mut self, meta: Option) -> &mut Self { + pub fn set_meta(&mut self, meta: Option) -> &mut Self { self.meta = meta; self } @@ -61,7 +61,7 @@ impl Builder { #[derive(Debug)] pub struct Unhandled { source: Box, - meta: GenericError, + meta: ErrorMetadata, } impl Unhandled { @@ -84,7 +84,7 @@ impl StdError for Unhandled { } impl ProvideErrorMetadata for Unhandled { - fn meta(&self) -> &GenericError { + fn meta(&self) -> &ErrorMetadata { &self.meta } } diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index 1e45b94183..bf1e22ae08 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -25,7 +25,13 @@ pub mod retry; pub mod timeout; pub use crate::date_time::DateTime; -pub use error::Error; + +// TODO(deprecated): Remove deprecated re-export +/// Use [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 /// diff --git a/rust-runtime/inlineable/src/ec2_query_errors.rs b/rust-runtime/inlineable/src/ec2_query_errors.rs index 55aa5554a8..3355dbe004 100644 --- a/rust-runtime/inlineable/src/ec2_query_errors.rs +++ b/rust-runtime/inlineable/src/ec2_query_errors.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; +use aws_smithy_types::error::metadata::{Builder as ErrorMetadataBuilder, ErrorMetadata}; use aws_smithy_xml::decode::{try_data, Document, ScopedDecoder, XmlDecodeError}; use std::convert::TryFrom; @@ -14,10 +14,10 @@ pub fn body_is_error(body: &[u8]) -> Result { Ok(scoped.start_el().matches("Response")) } -pub fn parse_generic_error(body: &[u8]) -> Result { +pub fn parse_error_metadata(body: &[u8]) -> Result { let mut doc = Document::try_from(body)?; let mut root = doc.root_element()?; - let mut err_builder = GenericError::builder(); + let mut err_builder = ErrorMetadata::builder(); while let Some(mut tag) = root.next_tag() { if tag.start_el().local() == "Errors" { while let Some(mut error_tag) = tag.next_tag() { @@ -66,7 +66,7 @@ pub fn error_scope<'a, 'b>( #[cfg(test)] mod test { - use super::{body_is_error, parse_generic_error}; + use super::{body_is_error, parse_error_metadata}; use crate::ec2_query_errors::error_scope; use aws_smithy_xml::decode::Document; use std::convert::TryFrom; @@ -87,7 +87,7 @@ mod test { "#; assert!(body_is_error(xml).unwrap()); - let parsed = parse_generic_error(xml).expect("valid xml").build(); + let parsed = parse_error_metadata(xml).expect("valid xml").build(); assert_eq!(parsed.message(), Some("Hi")); assert_eq!(parsed.code(), Some("InvalidGreeting")); } diff --git a/rust-runtime/inlineable/src/json_errors.rs b/rust-runtime/inlineable/src/json_errors.rs index 065e5cae1a..1973f59d79 100644 --- a/rust-runtime/inlineable/src/json_errors.rs +++ b/rust-runtime/inlineable/src/json_errors.rs @@ -5,7 +5,7 @@ use aws_smithy_json::deserialize::token::skip_value; use aws_smithy_json::deserialize::{error::DeserializeError, json_token_iter, Token}; -use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; +use aws_smithy_types::error::metadata::{Builder as ErrorMetadataBuilder, ErrorMetadata}; use bytes::Bytes; use http::header::ToStrError; use http::{HeaderMap, HeaderValue}; @@ -82,13 +82,13 @@ fn error_type_from_header(headers: &HeaderMap) -> Result, -) -> Result { +) -> Result { let ErrorBody { code, message } = parse_error_body(payload.as_ref())?; - let mut err_builder = GenericError::builder(); + let mut err_builder = ErrorMetadata::builder(); if let Some(code) = error_type_from_header(headers) .map_err(|_| DeserializeError::custom("X-Amzn-Errortype header was not valid UTF-8"))? .or(code.as_deref()) @@ -104,20 +104,20 @@ pub fn parse_generic_error( #[cfg(test)] mod test { - use crate::json_errors::{parse_error_body, parse_generic_error, sanitize_error_code}; + use crate::json_errors::{parse_error_body, parse_error_metadata, sanitize_error_code}; use aws_smithy_types::Error; use bytes::Bytes; use std::borrow::Cow; #[test] - fn generic_error() { + fn error_metadata() { let response = http::Response::builder() .body(Bytes::from_static( br#"{ "__type": "FooError", "message": "Go to foo" }"#, )) .unwrap(); assert_eq!( - parse_generic_error(response.body(), response.headers()) + parse_error_metadata(response.body(), response.headers()) .unwrap() .build(), Error::builder() @@ -200,7 +200,7 @@ mod test { )) .unwrap(); assert_eq!( - parse_generic_error(response.body(), response.headers()) + parse_error_metadata(response.body(), response.headers()) .unwrap() .build(), Error::builder() diff --git a/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs b/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs index d93ba1b262..def901cf7f 100644 --- a/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs +++ b/rust-runtime/inlineable/src/rest_xml_unwrapped_errors.rs @@ -6,7 +6,7 @@ //! Error abstractions for `noErrorWrapping`. Code generators should either inline this file //! or its companion `rest_xml_wrapped_errors.rs` for code generation -use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; +use aws_smithy_types::error::metadata::{Builder as ErrorMetadataBuilder, ErrorMetadata}; use aws_smithy_xml::decode::{try_data, Document, ScopedDecoder, XmlDecodeError}; use std::convert::TryFrom; @@ -27,10 +27,10 @@ pub fn error_scope<'a, 'b>( Ok(scoped) } -pub fn parse_generic_error(body: &[u8]) -> Result { +pub fn parse_error_metadata(body: &[u8]) -> Result { let mut doc = Document::try_from(body)?; let mut root = doc.root_element()?; - let mut builder = GenericError::builder(); + let mut builder = ErrorMetadata::builder(); while let Some(mut tag) = root.next_tag() { match tag.start_el().local() { "Code" => { @@ -47,7 +47,7 @@ pub fn parse_generic_error(body: &[u8]) -> Resultfoo-id "#; assert!(body_is_error(xml).unwrap()); - let parsed = parse_generic_error(xml).expect("valid xml").build(); + let parsed = parse_error_metadata(xml).expect("valid xml").build(); assert_eq!(parsed.message(), Some("Hi")); assert_eq!(parsed.code(), Some("InvalidGreeting")); } diff --git a/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs b/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs index f580b310d2..2511929a06 100644 --- a/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs +++ b/rust-runtime/inlineable/src/rest_xml_wrapped_errors.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_types::error::{Builder as GenericErrorBuilder, Error as GenericError}; +use aws_smithy_types::error::metadata::{Builder as ErrorMetadataBuilder, ErrorMetadata}; use aws_smithy_xml::decode::{try_data, Document, ScopedDecoder, XmlDecodeError}; use std::convert::TryFrom; @@ -14,10 +14,10 @@ pub fn body_is_error(body: &[u8]) -> Result { Ok(scoped.start_el().matches("ErrorResponse")) } -pub fn parse_generic_error(body: &[u8]) -> Result { +pub fn parse_error_metadata(body: &[u8]) -> Result { let mut doc = Document::try_from(body)?; let mut root = doc.root_element()?; - let mut err_builder = GenericError::builder(); + let mut err_builder = ErrorMetadata::builder(); while let Some(mut tag) = root.next_tag() { if tag.start_el().local() == "Error" { while let Some(mut error_field) = tag.next_tag() { @@ -60,7 +60,7 @@ pub fn error_scope<'a, 'b>( #[cfg(test)] mod test { - use super::{body_is_error, parse_generic_error}; + use super::{body_is_error, parse_error_metadata}; use crate::rest_xml_wrapped_errors::error_scope; use aws_smithy_xml::decode::Document; use std::convert::TryFrom; @@ -78,7 +78,7 @@ mod test { foo-id "#; assert!(body_is_error(xml).unwrap()); - let parsed = parse_generic_error(xml).expect("valid xml").build(); + let parsed = parse_error_metadata(xml).expect("valid xml").build(); assert_eq!(parsed.message(), Some("Hi")); assert_eq!(parsed.code(), Some("InvalidGreeting")); } From 13297be8cda7298bb2b5185e0541211f3456c0ee Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 3 Feb 2023 09:29:33 -0800 Subject: [PATCH 35/39] Fix doc link --- rust-runtime/aws-smithy-types/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index bf1e22ae08..1091b13428 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -27,7 +27,7 @@ pub mod timeout; pub use crate::date_time::DateTime; // TODO(deprecated): Remove deprecated re-export -/// Use [ErrorMetadata] instead. +/// Use [error::ErrorMetadata] instead. #[deprecated( note = "`aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`" )] From dd7c7ee44f20d73a401518dc73b31f284947eb8e Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 3 Feb 2023 10:17:15 -0800 Subject: [PATCH 36/39] Minor fixes --- .../rustsdk/customize/s3/S3Decorator.kt | 4 ++-- .../customize/RequiredCustomizations.kt | 2 +- .../customizations/SmithyTypesPubUseExtra.kt | 20 +++++++++++-------- ...rTest.kt => SmithyTypesPubUseExtraTest.kt} | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) rename codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/{SmithyTypesPubUseGeneratorTest.kt => SmithyTypesPubUseExtraTest.kt} (99%) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index 7b5f90037e..bd0c521008 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -92,7 +92,7 @@ class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContex private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( "Bytes" to RuntimeType.Bytes, - "Error" to RuntimeType.errorMetadata(runtimeConfig), + "ErrorMetadata" to RuntimeType.errorMetadata(runtimeConfig), "ErrorBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), "HeaderMap" to RuntimeType.HttpHeaderMap, "Response" to RuntimeType.HttpResponse, @@ -111,7 +111,7 @@ class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContex // S3 HEAD responses have no response body to for an error code. Therefore, // check the HTTP response status and populate an error code for 404s. if response.body().is_empty() { - let mut builder = #{Error}::builder(); + let mut builder = #{ErrorMetadata}::builder(); if response.status().as_u16() == 404 { builder = builder.code("NotFound"); } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index f65e042d44..f1c468a5cf 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -65,6 +65,6 @@ class RequiredCustomizations : ClientCodegenDecorator { // Re-export resiliency types ResiliencyReExportCustomization(codegenContext.runtimeConfig).extras(rustCrate) - pubUseSmithyTypes(codegenContext.runtimeConfig, codegenContext.model, rustCrate) + pubUseSmithyTypes(codegenContext, codegenContext.model, rustCrate) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt index 307e91b19a..69cf3774d7 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt @@ -10,11 +10,13 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.util.hasEventStreamMember import software.amazon.smithy.rust.codegen.core.util.hasStreamingMember +import software.amazon.smithy.rust.codegen.core.util.letIf private data class PubUseType( val type: RuntimeType, @@ -46,16 +48,18 @@ private fun hasBlobs(model: Model): Boolean = structUnionMembersMatchPredicate(m private fun hasDateTimes(model: Model): Boolean = structUnionMembersMatchPredicate(model, Shape::isTimestampShape) /** Returns a list of types that should be re-exported for the given model */ -internal fun pubUseTypes(runtimeConfig: RuntimeConfig, model: Model): List { +internal fun pubUseTypes(codegenContext: CodegenContext, model: Model): List { + val runtimeConfig = codegenContext.runtimeConfig return ( listOf( PubUseType(RuntimeType.blob(runtimeConfig), ::hasBlobs), PubUseType(RuntimeType.dateTime(runtimeConfig), ::hasDateTimes), ) + RuntimeType.smithyTypes(runtimeConfig).let { types -> - listOf( - PubUseType(types.resolve("error::display::DisplayErrorContext")) { true }, - PubUseType(types.resolve("error::metadata::ProvideErrorMetadata")) { true }, - ) + listOf(PubUseType(types.resolve("error::display::DisplayErrorContext")) { true }) + // Only re-export `ProvideErrorMetadata` for clients + .letIf(codegenContext.target == CodegenTarget.CLIENT) { list -> + list + listOf(PubUseType(types.resolve("error::metadata::ProvideErrorMetadata")) { true }) + } } + RuntimeType.smithyHttp(runtimeConfig).let { http -> listOf( PubUseType(http.resolve("result::SdkError")) { true }, @@ -67,9 +71,9 @@ internal fun pubUseTypes(runtimeConfig: RuntimeConfig, model: Model): List rust("pub use #T;", type) } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt similarity index 99% rename from codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseGeneratorTest.kt rename to codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt index c147567d71..dcc4891653 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt @@ -11,7 +11,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel -class SmithyTypesPubUseGeneratorTest { +class SmithyTypesPubUseExtraTest { private fun modelWithMember( inputMember: String = "", outputMember: String = "", From 4d92c09379184103929b989d7e844f0effb6abce Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 3 Feb 2023 10:25:40 -0800 Subject: [PATCH 37/39] Fix test compile --- .../core/smithy/customizations/SmithyTypesPubUseExtraTest.kt | 5 +++-- .../smithy/customizations/ServerRequiredCustomizations.kt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt index dcc4891653..d8629c3b1d 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt @@ -8,8 +8,9 @@ package software.amazon.smithy.rust.codegen.core.smithy.customizations import org.junit.jupiter.api.Test import software.amazon.smithy.model.Model import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGeneratorTest.Companion.model import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.testCodegenContext class SmithyTypesPubUseExtraTest { private fun modelWithMember( @@ -48,7 +49,7 @@ class SmithyTypesPubUseExtraTest { outputMember: String = "", unionMember: String = "", additionalShape: String = "", - ) = pubUseTypes(TestRuntimeConfig, modelWithMember(inputMember, outputMember, unionMember, additionalShape)) + ) = pubUseTypes(testCodegenContext(model), modelWithMember(inputMember, outputMember, unionMember, additionalShape)) private fun assertDoesntHaveTypes(types: List, expectedTypes: List) = expectedTypes.forEach { assertDoesntHaveType(types, it) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt index 90b3550b98..04861fae98 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/ServerRequiredCustomizations.kt @@ -35,6 +35,6 @@ class ServerRequiredCustomizations : ServerCodegenDecorator { // Add rt-tokio feature for `ByteStream::from_path` rustCrate.mergeFeature(Feature("rt-tokio", true, listOf("aws-smithy-http/rt-tokio"))) - pubUseSmithyTypes(codegenContext.runtimeConfig, codegenContext.model, rustCrate) + pubUseSmithyTypes(codegenContext, codegenContext.model, rustCrate) } } From 1805ceea77c12cddd876b97cb7e6da6c1b52958f Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 3 Feb 2023 10:56:25 -0800 Subject: [PATCH 38/39] Fix another doc link --- aws/rust-runtime/aws-http/src/request_id.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/rust-runtime/aws-http/src/request_id.rs b/aws/rust-runtime/aws-http/src/request_id.rs index 829077092a..c3f1927228 100644 --- a/aws/rust-runtime/aws-http/src/request_id.rs +++ b/aws/rust-runtime/aws-http/src/request_id.rs @@ -12,7 +12,7 @@ use aws_smithy_types::error::metadata::{ use aws_smithy_types::error::Unhandled; use http::{HeaderMap, HeaderValue}; -/// Constant for the [`aws_smithy_types::error::Error`] extra field that contains the request ID +/// Constant for the [`ErrorMetadata`] extra field that contains the request ID const AWS_REQUEST_ID: &str = "aws_request_id"; /// Implementers add a function to return an AWS request ID From ea72017bac56f6c30a3d9f272084b3be86cf9610 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 8 Feb 2023 17:43:26 -0800 Subject: [PATCH 39/39] Incorporate feedback --- .../amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt | 4 +--- .../codegen/core/smithy/customize/CoreCodegenDecorator.kt | 2 +- .../software/amazon/smithy/rust/codegen/core/util/Smithy.kt | 3 +++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt index caf42f7518..dfbd2ca597 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt @@ -8,10 +8,10 @@ package software.amazon.smithy.rustsdk.customize import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape -import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.AuthTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.core.util.shapeId // / STS (and possibly other services) need to have auth manually set to [] class DisabledAuthDecorator : ClientCodegenDecorator { @@ -44,5 +44,3 @@ class DisabledAuthDecorator : ClientCodegenDecorator { } } } - -private fun String.shapeId() = ShapeId.from(this) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt index 0f57667e31..756f0d132c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt @@ -73,7 +73,7 @@ interface CoreCodegenDecorator { baseCustomizations: List, ): List = baseCustomizations - // TODO(https://github.com/awslabs/smithy-rs/issues/1401): Move these customizations into `ClientCodegenDecorator` + // TODO(https://github.com/awslabs/smithy-rs/issues/1401): Move builder customizations into `ClientCodegenDecorator` /** * Hook to customize generated builders. */ diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt index 267327c35e..06c344337f 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt @@ -137,3 +137,6 @@ fun Shape.isPrimitive(): Boolean { else -> false } } + +/** Convert a string to a ShapeId */ +fun String.shapeId() = ShapeId.from(this)