diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index ed582f0e54..d5ed3b8be3 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -24,6 +24,8 @@ pub mod no_credentials; /// Support types required for adding presigning to an operation in a generated service. pub mod presigning; +// TODO(CrateReorganization): Delete the `old_presigning` module +pub mod old_presigning; /// Special logic for extracting request IDs from S3's responses. pub mod s3_request_id; diff --git a/aws/rust-runtime/aws-inlineable/src/old_presigning.rs b/aws/rust-runtime/aws-inlineable/src/old_presigning.rs new file mode 100644 index 0000000000..cf95c3901d --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/old_presigning.rs @@ -0,0 +1,282 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Presigned request types and configuration. + +/// Presigning config and builder +pub mod config { + use std::fmt; + use std::time::{Duration, SystemTime}; + + const ONE_WEEK: Duration = Duration::from_secs(604800); + + /// Presigning config values required for creating a presigned request. + #[non_exhaustive] + #[derive(Debug, Clone)] + pub struct PresigningConfig { + start_time: SystemTime, + expires_in: Duration, + } + + impl PresigningConfig { + /// Creates a `PresigningConfig` with the given `expires_in` duration. + /// + /// The `expires_in` duration is the total amount of time the presigned request should + /// be valid for. Other config values are defaulted. + /// + /// Credential expiration time takes priority over the `expires_in` value. + /// If the credentials used to sign the request expire before the presigned request is + /// set to expire, then the presigned request will become invalid. + pub fn expires_in(expires_in: Duration) -> Result { + Self::builder().expires_in(expires_in).build() + } + + /// Creates a new builder for creating a `PresigningConfig`. + pub fn builder() -> Builder { + Builder::default() + } + + /// Returns the amount of time the presigned request should be valid for. + pub fn expires(&self) -> Duration { + self.expires_in + } + + /// Returns the start time. The presigned request will be valid between this and the end + /// time produced by adding the `expires()` value to it. + pub fn start_time(&self) -> SystemTime { + self.start_time + } + } + + #[derive(Debug)] + enum ErrorKind { + /// Presigned requests cannot be valid for longer than one week. + ExpiresInDurationTooLong, + + /// The `PresigningConfig` builder requires a value for `expires_in`. + ExpiresInRequired, + } + + /// `PresigningConfig` build errors. + #[derive(Debug)] + pub struct Error { + kind: ErrorKind, + } + + impl std::error::Error for Error {} + + impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.kind { + ErrorKind::ExpiresInDurationTooLong => { + write!(f, "`expires_in` must be no longer than one week") + } + ErrorKind::ExpiresInRequired => write!(f, "`expires_in` is required"), + } + } + } + + impl From for Error { + fn from(kind: ErrorKind) -> Self { + Self { kind } + } + } + + /// Builder used to create `PresigningConfig`. + #[non_exhaustive] + #[derive(Default, Debug)] + pub struct Builder { + start_time: Option, + expires_in: Option, + } + + impl Builder { + /// Sets the start time for the presigned request. + /// + /// The request will start to be valid at this time, and will cease to be valid after + /// the end time, which can be determined by adding the `expires_in` duration to this + /// start time. If not specified, this will default to the current time. + /// + /// Optional. + pub fn start_time(mut self, start_time: SystemTime) -> Self { + self.set_start_time(Some(start_time)); + self + } + + /// Sets the start time for the presigned request. + /// + /// The request will start to be valid at this time, and will cease to be valid after + /// the end time, which can be determined by adding the `expires_in` duration to this + /// start time. If not specified, this will default to the current time. + /// + /// Optional. + pub fn set_start_time(&mut self, start_time: Option) { + self.start_time = start_time; + } + + /// Sets how long the request should be valid after the `start_time` (which defaults + /// to the current time). + /// + /// Credential expiration time takes priority over the `expires_in` value. + /// If the credentials used to sign the request expire before the presigned request is + /// set to expire, then the presigned request will become invalid. + /// + /// Required. + pub fn expires_in(mut self, expires_in: Duration) -> Self { + self.set_expires_in(Some(expires_in)); + self + } + + /// Sets how long the request should be valid after the `start_time` (which defaults + /// to the current time). + /// + /// Credential expiration time takes priority over the `expires_in` value. + /// If the credentials used to sign the request expire before the presigned request is + /// set to expire, then the presigned request will become invalid. + /// + /// Required. + pub fn set_expires_in(&mut self, expires_in: Option) { + self.expires_in = expires_in; + } + + /// Builds the `PresigningConfig`. This will error if `expires_in` is not + /// given, or if it's longer than one week. + pub fn build(self) -> Result { + let expires_in = self.expires_in.ok_or(ErrorKind::ExpiresInRequired)?; + if expires_in > ONE_WEEK { + return Err(ErrorKind::ExpiresInDurationTooLong.into()); + } + Ok(PresigningConfig { + start_time: self.start_time.unwrap_or_else(SystemTime::now), + expires_in, + }) + } + } +} + +/// Presigned request +pub mod request { + use std::fmt::{Debug, Formatter}; + + /// Represents a presigned request. This only includes the HTTP request method, URI, and headers. + /// + /// **This struct has conversion convenience functions:** + /// + /// - [`PresignedRequest::to_http_request`][Self::to_http_request] returns an [`http::Request`](https://docs.rs/http/0.2.6/http/request/struct.Request.html) + /// - [`PresignedRequest::into`](#impl-From) returns an [`http::request::Builder`](https://docs.rs/http/0.2.6/http/request/struct.Builder.html) + #[non_exhaustive] + pub struct PresignedRequest(http::Request<()>); + + impl PresignedRequest { + pub(crate) fn new(inner: http::Request<()>) -> Self { + Self(inner) + } + + /// Returns the HTTP request method. + pub fn method(&self) -> &http::Method { + self.0.method() + } + + /// Returns the HTTP request URI. + pub fn uri(&self) -> &http::Uri { + self.0.uri() + } + + /// Returns any HTTP headers that need to go along with the request, except for `Host`, + /// which should be sent based on the endpoint in the URI by the HTTP client rather than + /// added directly. + pub fn headers(&self) -> &http::HeaderMap { + self.0.headers() + } + + /// Given a body, convert this `PresignedRequest` into an `http::Request` + pub fn to_http_request(self, body: B) -> Result, http::Error> { + let builder: http::request::Builder = self.into(); + + builder.body(body) + } + } + + impl Debug for PresignedRequest { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PresignedRequest") + .field("method", self.method()) + .field("uri", self.uri()) + .field("headers", self.headers()) + .finish() + } + } + + impl From for http::request::Builder { + fn from(req: PresignedRequest) -> Self { + let mut builder = http::request::Builder::new() + .uri(req.uri()) + .method(req.method()); + + if let Some(headers) = builder.headers_mut() { + *headers = req.headers().clone(); + } + + builder + } + } +} + +/// Tower middleware service for creating presigned requests +#[allow(dead_code)] +pub(crate) mod service { + use super::request::PresignedRequest; + use aws_smithy_http::operation; + use http::header::USER_AGENT; + use std::future::{ready, Ready}; + use std::marker::PhantomData; + use std::task::{Context, Poll}; + + /// Tower [`Service`](tower::Service) for generated a [`PresignedRequest`] from the AWS middleware. + #[derive(Default, Debug)] + #[non_exhaustive] + pub(crate) struct PresignedRequestService { + _phantom: PhantomData, + } + + // Required because of the derive Clone on MapRequestService. + // Manually implemented to avoid requiring errors to implement Clone. + impl Clone for PresignedRequestService { + fn clone(&self) -> Self { + Self { + _phantom: Default::default(), + } + } + } + + impl PresignedRequestService { + /// Creates a new `PresignedRequestService` + pub(crate) fn new() -> Self { + Self { + _phantom: Default::default(), + } + } + } + + impl tower::Service for PresignedRequestService { + type Response = PresignedRequest; + type Error = E; + type Future = Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: operation::Request) -> Self::Future { + let (mut req, _) = req.into_parts(); + + // Remove user agent headers since the request will not be executed by the AWS Rust SDK. + req.headers_mut().remove(USER_AGENT); + req.headers_mut().remove("X-Amz-User-Agent"); + + ready(Ok(PresignedRequest::new(req.map(|_| ())))) + } + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/presigning.rs b/aws/rust-runtime/aws-inlineable/src/presigning.rs index 5a97a19902..da0997d591 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning.rs @@ -5,229 +5,221 @@ //! Presigned request types and configuration. -/// Presigning config and builder -pub mod config { - use std::fmt; - use std::time::{Duration, SystemTime}; +use std::fmt; +use std::time::{Duration, SystemTime}; - const ONE_WEEK: Duration = Duration::from_secs(604800); +const ONE_WEEK: Duration = Duration::from_secs(604800); - /// Presigning config values required for creating a presigned request. - #[non_exhaustive] - #[derive(Debug, Clone)] - pub struct PresigningConfig { - start_time: SystemTime, - expires_in: Duration, - } +/// Presigning config values required for creating a presigned request. +#[non_exhaustive] +#[derive(Debug, Clone)] +pub struct PresigningConfig { + start_time: SystemTime, + expires_in: Duration, +} - impl PresigningConfig { - /// Creates a `PresigningConfig` with the given `expires_in` duration. - /// - /// The `expires_in` duration is the total amount of time the presigned request should - /// be valid for. Other config values are defaulted. - /// - /// Credential expiration time takes priority over the `expires_in` value. - /// If the credentials used to sign the request expire before the presigned request is - /// set to expire, then the presigned request will become invalid. - pub fn expires_in(expires_in: Duration) -> Result { - Self::builder().expires_in(expires_in).build() - } +impl PresigningConfig { + /// Creates a `PresigningConfig` with the given `expires_in` duration. + /// + /// The `expires_in` duration is the total amount of time the presigned request should + /// be valid for. Other config values are defaulted. + /// + /// Credential expiration time takes priority over the `expires_in` value. + /// If the credentials used to sign the request expire before the presigned request is + /// set to expire, then the presigned request will become invalid. + pub fn expires_in(expires_in: Duration) -> Result { + Self::builder().expires_in(expires_in).build() + } - /// Creates a new builder for creating a `PresigningConfig`. - pub fn builder() -> Builder { - Builder::default() - } + /// Creates a new builder for creating a `PresigningConfig`. + pub fn builder() -> PresigningConfigBuilder { + PresigningConfigBuilder::default() + } - /// Returns the amount of time the presigned request should be valid for. - pub fn expires(&self) -> Duration { - self.expires_in - } + /// Returns the amount of time the presigned request should be valid for. + pub fn expires(&self) -> Duration { + self.expires_in + } - /// Returns the start time. The presigned request will be valid between this and the end - /// time produced by adding the `expires()` value to it. - pub fn start_time(&self) -> SystemTime { - self.start_time - } + /// Returns the start time. The presigned request will be valid between this and the end + /// time produced by adding the `expires()` value to it. + pub fn start_time(&self) -> SystemTime { + self.start_time } +} - #[derive(Debug)] - enum ErrorKind { - /// Presigned requests cannot be valid for longer than one week. - ExpiresInDurationTooLong, +#[derive(Debug)] +enum ErrorKind { + /// Presigned requests cannot be valid for longer than one week. + ExpiresInDurationTooLong, - /// The `PresigningConfig` builder requires a value for `expires_in`. - ExpiresInRequired, - } + /// The `PresigningConfig` builder requires a value for `expires_in`. + ExpiresInRequired, +} - /// `PresigningConfig` build errors. - #[derive(Debug)] - pub struct Error { - kind: ErrorKind, - } +/// `PresigningConfig` build errors. +#[derive(Debug)] +pub struct PresigningConfigError { + kind: ErrorKind, +} - impl std::error::Error for Error {} +impl std::error::Error for PresigningConfigError {} - impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.kind { - ErrorKind::ExpiresInDurationTooLong => { - write!(f, "`expires_in` must be no longer than one week") - } - ErrorKind::ExpiresInRequired => write!(f, "`expires_in` is required"), +impl fmt::Display for PresigningConfigError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.kind { + ErrorKind::ExpiresInDurationTooLong => { + write!(f, "`expires_in` must be no longer than one week") } + ErrorKind::ExpiresInRequired => write!(f, "`expires_in` is required"), } } +} - impl From for Error { - fn from(kind: ErrorKind) -> Self { - Self { kind } - } - } - - /// Builder used to create `PresigningConfig`. - #[non_exhaustive] - #[derive(Default, Debug)] - pub struct Builder { - start_time: Option, - expires_in: Option, +impl From for PresigningConfigError { + fn from(kind: ErrorKind) -> Self { + Self { kind } } +} - impl Builder { - /// Sets the start time for the presigned request. - /// - /// The request will start to be valid at this time, and will cease to be valid after - /// the end time, which can be determined by adding the `expires_in` duration to this - /// start time. If not specified, this will default to the current time. - /// - /// Optional. - pub fn start_time(mut self, start_time: SystemTime) -> Self { - self.set_start_time(Some(start_time)); - self - } - - /// Sets the start time for the presigned request. - /// - /// The request will start to be valid at this time, and will cease to be valid after - /// the end time, which can be determined by adding the `expires_in` duration to this - /// start time. If not specified, this will default to the current time. - /// - /// Optional. - pub fn set_start_time(&mut self, start_time: Option) { - self.start_time = start_time; - } - - /// Sets how long the request should be valid after the `start_time` (which defaults - /// to the current time). - /// - /// Credential expiration time takes priority over the `expires_in` value. - /// If the credentials used to sign the request expire before the presigned request is - /// set to expire, then the presigned request will become invalid. - /// - /// Required. - pub fn expires_in(mut self, expires_in: Duration) -> Self { - self.set_expires_in(Some(expires_in)); - self - } - - /// Sets how long the request should be valid after the `start_time` (which defaults - /// to the current time). - /// - /// Credential expiration time takes priority over the `expires_in` value. - /// If the credentials used to sign the request expire before the presigned request is - /// set to expire, then the presigned request will become invalid. - /// - /// Required. - pub fn set_expires_in(&mut self, expires_in: Option) { - self.expires_in = expires_in; - } +/// Builder used to create `PresigningConfig`. +#[non_exhaustive] +#[derive(Default, Debug)] +pub struct PresigningConfigBuilder { + start_time: Option, + expires_in: Option, +} - /// Builds the `PresigningConfig`. This will error if `expires_in` is not - /// given, or if it's longer than one week. - pub fn build(self) -> Result { - let expires_in = self.expires_in.ok_or(ErrorKind::ExpiresInRequired)?; - if expires_in > ONE_WEEK { - return Err(ErrorKind::ExpiresInDurationTooLong.into()); - } - Ok(PresigningConfig { - start_time: self.start_time.unwrap_or_else(SystemTime::now), - expires_in, - }) - } +impl PresigningConfigBuilder { + /// Sets the start time for the presigned request. + /// + /// The request will start to be valid at this time, and will cease to be valid after + /// the end time, which can be determined by adding the `expires_in` duration to this + /// start time. If not specified, this will default to the current time. + /// + /// Optional. + pub fn start_time(mut self, start_time: SystemTime) -> Self { + self.set_start_time(Some(start_time)); + self } -} -/// Presigned request -pub mod request { - use std::fmt::{Debug, Formatter}; + /// Sets the start time for the presigned request. + /// + /// The request will start to be valid at this time, and will cease to be valid after + /// the end time, which can be determined by adding the `expires_in` duration to this + /// start time. If not specified, this will default to the current time. + /// + /// Optional. + pub fn set_start_time(&mut self, start_time: Option) { + self.start_time = start_time; + } - /// Represents a presigned request. This only includes the HTTP request method, URI, and headers. + /// Sets how long the request should be valid after the `start_time` (which defaults + /// to the current time). /// - /// **This struct has conversion convenience functions:** + /// Credential expiration time takes priority over the `expires_in` value. + /// If the credentials used to sign the request expire before the presigned request is + /// set to expire, then the presigned request will become invalid. /// - /// - [`PresignedRequest::to_http_request`][Self::to_http_request] returns an [`http::Request`](https://docs.rs/http/0.2.6/http/request/struct.Request.html) - /// - [`PresignedRequest::into`](#impl-From) returns an [`http::request::Builder`](https://docs.rs/http/0.2.6/http/request/struct.Builder.html) - #[non_exhaustive] - pub struct PresignedRequest(http::Request<()>); + /// Required. + pub fn expires_in(mut self, expires_in: Duration) -> Self { + self.set_expires_in(Some(expires_in)); + self + } - impl PresignedRequest { - pub(crate) fn new(inner: http::Request<()>) -> Self { - Self(inner) - } + /// Sets how long the request should be valid after the `start_time` (which defaults + /// to the current time). + /// + /// Credential expiration time takes priority over the `expires_in` value. + /// If the credentials used to sign the request expire before the presigned request is + /// set to expire, then the presigned request will become invalid. + /// + /// Required. + pub fn set_expires_in(&mut self, expires_in: Option) { + self.expires_in = expires_in; + } - /// Returns the HTTP request method. - pub fn method(&self) -> &http::Method { - self.0.method() + /// Builds the `PresigningConfig`. This will error if `expires_in` is not + /// given, or if it's longer than one week. + pub fn build(self) -> Result { + let expires_in = self.expires_in.ok_or(ErrorKind::ExpiresInRequired)?; + if expires_in > ONE_WEEK { + return Err(ErrorKind::ExpiresInDurationTooLong.into()); } + Ok(PresigningConfig { + start_time: self.start_time.unwrap_or_else(SystemTime::now), + expires_in, + }) + } +} - /// Returns the HTTP request URI. - pub fn uri(&self) -> &http::Uri { - self.0.uri() - } +/// Represents a presigned request. This only includes the HTTP request method, URI, and headers. +/// +/// **This struct has conversion convenience functions:** +/// +/// - [`PresignedRequest::to_http_request`][Self::to_http_request] returns an [`http::Request`](https://docs.rs/http/0.2.6/http/request/struct.Request.html) +/// - [`PresignedRequest::into`](#impl-From) returns an [`http::request::Builder`](https://docs.rs/http/0.2.6/http/request/struct.Builder.html) +#[non_exhaustive] +pub struct PresignedRequest(http::Request<()>); + +impl PresignedRequest { + pub(crate) fn new(inner: http::Request<()>) -> Self { + Self(inner) + } - /// Returns any HTTP headers that need to go along with the request, except for `Host`, - /// which should be sent based on the endpoint in the URI by the HTTP client rather than - /// added directly. - pub fn headers(&self) -> &http::HeaderMap { - self.0.headers() - } + /// Returns the HTTP request method. + pub fn method(&self) -> &http::Method { + self.0.method() + } - /// Given a body, convert this `PresignedRequest` into an `http::Request` - pub fn to_http_request(self, body: B) -> Result, http::Error> { - let builder: http::request::Builder = self.into(); + /// Returns the HTTP request URI. + pub fn uri(&self) -> &http::Uri { + self.0.uri() + } - builder.body(body) - } + /// Returns any HTTP headers that need to go along with the request, except for `Host`, + /// which should be sent based on the endpoint in the URI by the HTTP client rather than + /// added directly. + pub fn headers(&self) -> &http::HeaderMap { + self.0.headers() } - impl Debug for PresignedRequest { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PresignedRequest") - .field("method", self.method()) - .field("uri", self.uri()) - .field("headers", self.headers()) - .finish() - } + /// Given a body, convert this `PresignedRequest` into an `http::Request` + pub fn to_http_request(self, body: B) -> Result, http::Error> { + let builder: http::request::Builder = self.into(); + + builder.body(body) } +} - impl From for http::request::Builder { - fn from(req: PresignedRequest) -> Self { - let mut builder = http::request::Builder::new() - .uri(req.uri()) - .method(req.method()); +impl fmt::Debug for PresignedRequest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PresignedRequest") + .field("method", self.method()) + .field("uri", self.uri()) + .field("headers", self.headers()) + .finish() + } +} - if let Some(headers) = builder.headers_mut() { - *headers = req.headers().clone(); - } +impl From for http::request::Builder { + fn from(req: PresignedRequest) -> Self { + let mut builder = http::request::Builder::new() + .uri(req.uri()) + .method(req.method()); - builder + if let Some(headers) = builder.headers_mut() { + *headers = req.headers().clone(); } + + builder } } /// Tower middleware service for creating presigned requests #[allow(dead_code)] pub(crate) mod service { - use crate::presigning::request::PresignedRequest; + use super::PresignedRequest; use aws_smithy_http::operation; use http::header::USER_AGENT; use std::future::{ready, Ready}; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index d5ba3b4349..e240d64270 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -96,9 +96,10 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { val generics = AwsClientGenerics(types) FluentClientGenerator( codegenContext, - generics, + reexportSmithyClientBuilder = false, + generics = generics, customizations = listOf( - AwsPresignedFluentBuilderMethod(runtimeConfig), + AwsPresignedFluentBuilderMethod(codegenContext, runtimeConfig), AwsFluentClientDocs(codegenContext), ), retryClassifier = AwsRuntimeType.awsHttp(runtimeConfig).resolve("retry::AwsResponseRetryClassifier"), 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 be12bf8e98..1ecca4651e 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 @@ -30,7 +30,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.rustlang.withBlock 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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization @@ -128,23 +127,23 @@ class AwsPresigningDecorator internal constructor( } class AwsInputPresignedMethod( - private val codegenContext: CodegenContext, + private val codegenContext: ClientCodegenContext, private val operationShape: OperationShape, ) : OperationCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val symbolProvider = codegenContext.symbolProvider - private val codegenScope = arrayOf( - "Error" to AwsRuntimeType.Presigning.resolve("config::Error"), - "PresignedRequest" to AwsRuntimeType.Presigning.resolve("request::PresignedRequest"), - "PresignedRequestService" to AwsRuntimeType.Presigning.resolve("service::PresignedRequestService"), - "PresigningConfig" to AwsRuntimeType.Presigning.resolve("config::PresigningConfig"), - "SdkError" to RuntimeType.sdkError(runtimeConfig), - "aws_sigv4" to AwsRuntimeType.awsSigv4(runtimeConfig), - "sig_auth" to AwsRuntimeType.awsSigAuth(runtimeConfig), - "tower" to RuntimeType.Tower, - "Middleware" to runtimeConfig.defaultMiddleware(), - ) + private val codegenScope = ( + presigningTypes(codegenContext) + listOf( + "PresignedRequestService" to AwsRuntimeType.presigning(codegenContext) + .resolve("service::PresignedRequestService"), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + "aws_sigv4" to AwsRuntimeType.awsSigv4(runtimeConfig), + "sig_auth" to AwsRuntimeType.awsSigAuth(runtimeConfig), + "tower" to RuntimeType.Tower, + "Middleware" to runtimeConfig.defaultMiddleware(), + ) + ).toTypedArray() override fun section(section: OperationSection): Writable = writable { @@ -241,14 +240,15 @@ class AwsInputPresignedMethod( } class AwsPresignedFluentBuilderMethod( + codegenContext: ClientCodegenContext, runtimeConfig: RuntimeConfig, ) : FluentClientCustomization() { - private val codegenScope = arrayOf( - "Error" to AwsRuntimeType.Presigning.resolve("config::Error"), - "PresignedRequest" to AwsRuntimeType.Presigning.resolve("request::PresignedRequest"), - "PresigningConfig" to AwsRuntimeType.Presigning.resolve("config::PresigningConfig"), - "SdkError" to RuntimeType.sdkError(runtimeConfig), - ) + private val codegenScope = ( + presigningTypes(codegenContext) + arrayOf( + "Error" to AwsRuntimeType.presigning(codegenContext).resolve("config::Error"), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + ) + ).toTypedArray() override fun section(section: FluentClientSection): Writable = writable { @@ -364,3 +364,15 @@ private fun RustWriter.documentPresignedMethod(hasConfigArg: Boolean) { """, ) } + +private fun presigningTypes(codegenContext: ClientCodegenContext): List> = + when (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> listOf( + "PresignedRequest" to AwsRuntimeType.presigning(codegenContext).resolve("PresignedRequest"), + "PresigningConfig" to AwsRuntimeType.presigning(codegenContext).resolve("PresigningConfig"), + ) + else -> listOf( + "PresignedRequest" to AwsRuntimeType.presigning(codegenContext).resolve("request::PresignedRequest"), + "PresigningConfig" to AwsRuntimeType.presigning(codegenContext).resolve("config::PresigningConfig"), + ) + } 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 c423dfd783..e07607a722 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 @@ -6,6 +6,7 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig @@ -41,9 +42,17 @@ fun RuntimeConfig.awsRoot(): RuntimeCrateLocation { } object AwsRuntimeType { - val Presigning by lazy { - RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning", visibility = Visibility.PUBLIC)) - } + fun presigning(codegenContext: ClientCodegenContext): RuntimeType = + when (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning", visibility = Visibility.PUBLIC)) + else -> RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFileAs( + file = "old_presigning", + moduleName = "presigning", + visibility = Visibility.PUBLIC, + ), + ) + } fun RuntimeConfig.defaultMiddleware() = RuntimeType.forInlineDependency( InlineAwsDependency.forRustFile( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt index 0a5a4cfd02..c1799c8bf5 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt @@ -26,7 +26,7 @@ import software.amazon.smithy.rust.codegen.core.util.orNull fun RuntimeConfig.awsInlineableBodyWithChecksum() = RuntimeType.forInlineDependency( InlineAwsDependency.forRustFile( - "http_body_checksum", visibility = Visibility.PUBLIC, + "http_body_checksum", visibility = Visibility.PUBCRATE, CargoDependency.Http, CargoDependency.HttpBody, CargoDependency.smithyHttp(this), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt index fa2554655a..b127795fc3 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InlineAwsDependency.kt @@ -12,5 +12,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Visibility object InlineAwsDependency { fun forRustFile(file: String, visibility: Visibility = Visibility.PRIVATE, vararg additionalDependency: RustDependency): InlineDependency = - InlineDependency.Companion.forRustFile(RustModule.new(file, visibility), "/aws-inlineable/src/$file.rs", *additionalDependency) + forRustFileAs(file, file, visibility, *additionalDependency) + + fun forRustFileAs(file: String, moduleName: String, visibility: Visibility = Visibility.PRIVATE, vararg additionalDependency: RustDependency): InlineDependency = + InlineDependency.Companion.forRustFile(RustModule.new(moduleName, visibility), "/aws-inlineable/src/$file.rs", *additionalDependency) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt index aa2ab120ee..eab6b9320c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.smithy.ModuleProvider import software.amazon.smithy.rust.codegen.core.smithy.ModuleProviderContext +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.contextName import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait @@ -137,3 +138,14 @@ fun ClientCodegenContext.featureGatedCustomizeModule() = when (settings.codegenC parent = ClientRustModule.Operation, ) } + +// TODO(CrateReorganization): Remove when cleaning up `enableNewCrateOrganizationScheme` +fun ClientCodegenContext.featureGatedPaginatorModule(symbolProvider: RustSymbolProvider, operation: OperationShape) = + when (settings.codegenConfig.enableNewCrateOrganizationScheme) { + true -> RustModule.public( + "paginator", + parent = symbolProvider.moduleForShape(operation), + documentation = "Paginator for this operation", + ) + else -> RustModule.public("paginator", "Paginators for the service") + } 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 77f5041a90..9847427f87 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 @@ -19,7 +19,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customizations.AllowLintsCustomization import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersionCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyTypes +import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyErrorTypes +import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyPrimitives import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization @@ -67,7 +68,15 @@ class RequiredCustomizations : ClientCodegenDecorator { ResiliencyReExportCustomization(codegenContext.runtimeConfig).extras(rustCrate) rustCrate.withModule(ClientRustModule.Types) { - pubUseSmithyTypes(codegenContext, codegenContext.model)(this) + pubUseSmithyPrimitives(codegenContext, codegenContext.model)(this) + if (!codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + pubUseSmithyErrorTypes(codegenContext)(this) + } + } + if (codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme) { + rustCrate.withModule(ClientRustModule.Error) { + pubUseSmithyErrorTypes(codegenContext)(this) + } } } } 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 4c668bc8ea..851553d317 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 @@ -8,11 +8,11 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.PaginatedIndex import software.amazon.smithy.model.shapes.OperationShape -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.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.featureGatedPaginatorModule import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerics -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 import software.amazon.smithy.rust.codegen.core.rustlang.render @@ -20,9 +20,7 @@ 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.stripOuter 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.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.generators.builderSymbol import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.PANIC @@ -39,25 +37,21 @@ fun OperationShape.isPaginated(model: Model) = .findMemberWithTrait(model) == null class PaginatorGenerator private constructor( - private val model: Model, - private val symbolProvider: RustSymbolProvider, - service: ServiceShape, + codegenContext: ClientCodegenContext, operation: OperationShape, private val generics: FluentClientGenerics, retryClassifier: RuntimeType, ) { companion object { fun paginatorType( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, generics: FluentClientGenerics, operationShape: OperationShape, retryClassifier: RuntimeType, ): RuntimeType? { return if (operationShape.isPaginated(codegenContext.model)) { PaginatorGenerator( - codegenContext.model, - codegenContext.symbolProvider, - codegenContext.serviceShape, + codegenContext, operationShape, generics, retryClassifier, @@ -68,12 +62,14 @@ class PaginatorGenerator private constructor( } } + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + private val runtimeConfig = codegenContext.runtimeConfig private val paginatorName = "${operation.id.name.toPascalCase()}Paginator" - private val runtimeConfig = symbolProvider.config.runtimeConfig private val idx = PaginatedIndex.of(model) - private val paginationInfo = - idx.getPaginationInfo(service, operation).orNull() ?: PANIC("failed to load pagination info") - private val module = RustModule.public("paginator", "Paginators for the service") + private val paginationInfo = idx.getPaginationInfo(codegenContext.serviceShape, operation).orNull() + ?: PANIC("failed to load pagination info") + private val module = codegenContext.featureGatedPaginatorModule(symbolProvider, operation) private val inputType = symbolProvider.toSymbol(operation.inputShape(model)) private val outputShape = operation.outputShape(model) 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 9b61ffe5ca..a1a000ffe1 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 @@ -55,6 +55,7 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase class FluentClientGenerator( private val codegenContext: ClientCodegenContext, + private val reexportSmithyClientBuilder: Boolean = true, private val generics: FluentClientGenerics = FlexibleClientGenerics( connectorDefault = null, middlewareDefault = null, @@ -86,6 +87,15 @@ class FluentClientGenerator( } private fun renderFluentClient(writer: RustWriter) { + if (!codegenContext.settings.codegenConfig.enableNewCrateOrganizationScheme || reexportSmithyClientBuilder) { + writer.rustTemplate( + """ + ##[doc(inline)] + pub use #{client}::Builder; + """, + "client" to RuntimeType.smithyClient(runtimeConfig), + ) + } writer.rustTemplate( """ ##[derive(Debug)] @@ -106,9 +116,6 @@ class FluentClientGenerator( } } - ##[doc(inline)] - pub use #{client}::Builder; - impl${generics.inst} From<#{client}::Client#{smithy_inst:W}> for Client${generics.inst} { fn from(client: #{client}::Client#{smithy_inst:W}) -> Self { Self::with_config(client, crate::Config::builder().build()) 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 0683182b51..48b68bfa6d 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,15 +54,8 @@ internal fun pubUseTypes(codegenContext: CodegenContext, model: Model): List - 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 -> + ) + RuntimeType.smithyHttp(runtimeConfig).let { http -> listOf( - PubUseType(http.resolve("result::SdkError")) { true }, PubUseType(http.resolve("byte_stream::ByteStream"), ::hasStreamingOperations), PubUseType(http.resolve("byte_stream::AggregatedBytes"), ::hasStreamingOperations), ) @@ -70,10 +63,32 @@ internal fun pubUseTypes(codegenContext: CodegenContext, model: Model): List pubUseType.shouldExport(model) }.map { it.type } } -/** Adds re-export statements in a separate file for the types module */ -fun pubUseSmithyTypes(codegenContext: CodegenContext, model: Model): Writable = writable { +/** Adds re-export statements for Smithy primitives */ +fun pubUseSmithyPrimitives(codegenContext: CodegenContext, model: Model): Writable = writable { val types = pubUseTypes(codegenContext, model) if (types.isNotEmpty()) { types.forEach { type -> rust("pub use #T;", type) } } } + +/** Adds re-export statements for error types */ +fun pubUseSmithyErrorTypes(codegenContext: CodegenContext): Writable = writable { + val runtimeConfig = codegenContext.runtimeConfig + val reexports = listOf( + listOf( + RuntimeType.smithyHttp(runtimeConfig).let { http -> + PubUseType(http.resolve("result::SdkError")) { true } + }, + ), + RuntimeType.smithyTypes(runtimeConfig).let { types -> + 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 }) + } + }, + ).flatten() + reexports.forEach { reexport -> + rust("pub use #T;", reexport.type) + } +} 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 d8629c3b1d..120fc5cb20 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 @@ -72,11 +72,6 @@ class SmithyTypesPubUseExtraTest { } } - @Test - fun `it always re-exports SdkError`() { - assertHasType(typesWithEmptyModel(), "aws_smithy_http::result::SdkError") - } - @Test fun `it re-exports Blob when a model uses blobs`() { assertDoesntHaveType(typesWithEmptyModel(), "aws_smithy_types::Blob") 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 7104cb9451..d6eb1625d0 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 @@ -9,7 +9,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customizations.AllowLintsCustomization import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersionCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyTypes +import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyErrorTypes +import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyPrimitives import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule @@ -37,7 +38,8 @@ class ServerRequiredCustomizations : ServerCodegenDecorator { rustCrate.mergeFeature(Feature("rt-tokio", true, listOf("aws-smithy-http/rt-tokio"))) rustCrate.withModule(ServerRustModule.Types) { - pubUseSmithyTypes(codegenContext, codegenContext.model)(this) + pubUseSmithyPrimitives(codegenContext, codegenContext.model)(this) + pubUseSmithyErrorTypes(codegenContext)(this) } } }