Unverified Commit 65035d5b authored by Zelda Hessler's avatar Zelda Hessler Committed by GitHub
Browse files

Add codegen helper for defining operation-specific `CustomizableOperation` methods. (#3918)

This PR lays the groundwork for defining operation-specific
`CustomizableOperation` methods.

## Codegen Example

If we were to define pre-signable ops this way, code like the following
would be emitted for each op that supported presigning.

```rust
impl<E, B> CustomizableOperation<crate::operation::put_object::PutObject, E, B> {
    /// Sends the request and returns the response.
    #[allow(unused_mut)]
    pub async fn presigned(
        mut self,
        presigning_config: crate::presigning::PresigningConfig,
    ) -> ::std::result::Result<crate::presigning::PresignedRequest, crate::error::SdkError<E>>
    where
        E: std::error::Error + ::std::marker::Send + ::std::marker::Sync + 'static,
        B: crate::client::customize::internal::CustomizablePresigned<E>,
    {
        self.execute(move |sender, conf| sender.presign(conf, presigning_config)).await
    }
}
```

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
parent 104797d1
Loading
Loading
Loading
Loading
+20 −11
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen
import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientEnumGenerator
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator
import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceGenerator
import software.amazon.smithy.rust.codegen.client.smithy.generators.client.CustomizableOperationImplGenerator
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.ClientProtocolTestGenerator
@@ -311,10 +312,10 @@ class ClientCodegenVisitor(
     * Generate operations
     */
    override fun operationShape(operationShape: OperationShape) {
        rustCrate.useShapeWriter(operationShape) operationWriter@{
        rustCrate.useShapeWriter(operationShape) {
            // Render the operation shape
            operationGenerator.renderOperation(
                this@operationWriter,
                this,
                operationShape,
                codegenDecorator,
            )
@@ -327,7 +328,8 @@ class ClientCodegenVisitor(
                    protocolGeneratorFactory.support(),
                    operationShape,
                ),
            ).render(this@operationWriter)
            ).render(this)
        }

        rustCrate.withModule(symbolProvider.moduleForOperationError(operationShape)) {
            OperationErrorGenerator(
@@ -337,6 +339,13 @@ class ClientCodegenVisitor(
                codegenDecorator.errorCustomizations(codegenContext, emptyList()),
            ).render(this)
        }

        rustCrate.withModule(ClientRustModule.Client.customize) {
            CustomizableOperationImplGenerator(
                codegenContext,
                operationShape,
                codegenDecorator.operationCustomizations(codegenContext, operationShape, emptyList()),
            ).render(this)
        }
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -125,6 +125,11 @@ sealed class OperationSection(name: String) : Section(name) {
            writer.rustTemplate(".with_retry_classifier(#{classifier})", "classifier" to classifier)
        }
    }

    data class CustomizableOperationImpl(
        override val customizations: List<OperationCustomization>,
        val operationShape: OperationShape,
    ) : OperationSection("CustomizableOperationImpl")
}

abstract class OperationCustomization : NamedCustomization<OperationSection>()
+33 −0
Original line number Diff line number Diff line
/*
 * 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.client

import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization
import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection
import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter
import software.amazon.smithy.rust.codegen.core.rustlang.rust
import software.amazon.smithy.rust.codegen.core.smithy.customize.allCustomizationsAreEmpty
import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations

class CustomizableOperationImplGenerator(
    private val codegenContext: ClientCodegenContext,
    private val operation: OperationShape,
    private val customizations: List<OperationCustomization>,
) {
    fun render(writer: RustWriter) {
        val section = OperationSection.CustomizableOperationImpl(customizations, operation)
        // When no customizations are set or there is nothing to write, return early.
        if (customizations.isEmpty() || allCustomizationsAreEmpty(customizations, section)) {
            return
        }

        writer.rust("impl<E, B> CustomizableOperation<#T, E, B> {", codegenContext.symbolProvider.toSymbol(operation))
        writer.writeCustomizations(customizations, section)
        writer.rust("}")
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -80,3 +80,13 @@ fun <T : Section> RustWriter.writeCustomizationsOrElse(
        orElse(this)
    }
}

fun <T : Section> allCustomizationsAreEmpty(
    customizations: List<NamedCustomization<T>>,
    section: T,
): Boolean {
    val test = RustWriter.root()
    test.writeCustomizations(customizations, section)
    // If they're not dirty, then they're empty.
    return !test.dirty()
}