Unverified Commit 56e9d9d5 authored by Russell Cohen's avatar Russell Cohen Committed by GitHub
Browse files

Customization Abstractions & Config Generation (#96)

* Customization Abstractions & Config Generation

A previous PR supported idempotency tokens with a hardcoded config implementation. This commit replaces it with the "real thing,"
supported by the `NamedSectionGenerator` abstraction. The intention idea is that sections can be typesafe, well documented, and evolve as compile
errors instead of silent failures.

Sections can also bring along state / generation context to allow for situation-specific customization. This API is still very experimental
and is expected to evolve as we continue to generate a fully customized DynamoDB client.

HttpProtocolGeneratorTest & integration tests provide complete test coverage—a unit test was also added to allow quicker iterations when adding customizations.

* Use typealias

* Fix missed merge conflicts, rename to member
parent 02d0db5d
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -88,8 +88,7 @@ class InlineDependency(
        fun instantHttpDate() = forRustFile("instant_httpdate", "instant_httpdate", "instant_httpdate.rs", CargoDependency.Serde)
        fun instant8601() = forRustFile("instant_8601", "instant_8601", "instant_iso8601.rs", CargoDependency.Serde)

        // Stub config implementation as a placeholder before one can be generated dynamically
        fun config() = forRustFile("config", "config", "config.rs", CargoDependency.Rand)
        fun idempotencyToken() = forRustFile("idempotency_token", "idempotency_token", "idempotency_token.rs", CargoDependency.Rand)
        fun blobSerde(runtimeConfig: RuntimeConfig) = forRustFile("blob_serde", "blob_serde", "blob_serde.rs", CargoDependency.Serde, CargoDependency.SmithyHttp(runtimeConfig))
    }
}
+21 −19
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ package software.amazon.smithy.rust.codegen.lang
import software.amazon.smithy.rust.codegen.smithy.RuntimeType

interface Container {
    val value: RustType
    val member: RustType
}

/**
@@ -40,41 +40,46 @@ sealed class RustType {
        override val name: kotlin.String = "i$precision"
    }

    data class Vec(val member: RustType) : RustType() {
    data class Vec(override val member: RustType) : RustType(), Container {
        override val name: kotlin.String = "Vec"
        override val namespace = "::std::vec"
    }

    data class Slice(val member: RustType) : RustType() {
    data class Slice(override val member: RustType) : RustType(), Container {
        override val name: kotlin.String = ""
    }

    data class HashMap(val key: RustType, val value: RustType) : RustType() {
    data class HashMap(val key: RustType, override val member: RustType) : RustType(), Container {
        // TODO: assert that underneath, the member is a String
        override val name: kotlin.String = "HashMap"
        override val namespace = "::std::collections"
    }

    data class HashSet(val member: RustType) : RustType() {
    data class HashSet(override val member: RustType) : RustType(), Container {
        // TODO: assert that underneath, the member is a String
        override val name: kotlin.String = SetType
        override val namespace = SetNamespace
    }

    data class Reference(val lifetime: kotlin.String?, override val value: RustType) : RustType(), Container {
        override val name: kotlin.String = value.name
    data class Reference(val lifetime: kotlin.String?, override val member: RustType) : RustType(), Container {
        override val name: kotlin.String = member.name
    }

    data class Option(override val value: RustType) : RustType(), Container {
    data class Option(override val member: RustType) : RustType(), Container {
        override val name: kotlin.String = "Option"
        override val namespace = "::std::option"
    }

    data class Box(override val value: RustType) : RustType(), Container {
    data class Box(override val member: RustType) : RustType(), Container {
        override val name: kotlin.String = "Box"
        override val namespace = "::std::boxed"
    }

    data class Dyn(override val member: RustType) : RustType(), Container {
        override val name = "dyn"
        override val namespace: kotlin.String? = null
    }

    data class Opaque(override val name: kotlin.String, override val namespace: kotlin.String? = null) : RustType()

    companion object {
@@ -94,11 +99,12 @@ fun RustType.render(fullyQualified: Boolean): String {
        is RustType.String -> this.name
        is RustType.Vec -> "${this.name}<${this.member.render(fullyQualified)}>"
        is RustType.Slice -> "[${this.member.render(fullyQualified)}]"
        is RustType.HashMap -> "${this.name}<${this.key.render(fullyQualified)}, ${this.value.render(fullyQualified)}>"
        is RustType.HashMap -> "${this.name}<${this.key.render(fullyQualified)}, ${this.member.render(fullyQualified)}>"
        is RustType.HashSet -> "${this.name}<${this.member.render(fullyQualified)}>"
        is RustType.Reference -> "&${this.lifetime?.let { "'$it" } ?: ""} ${this.value.render(fullyQualified)}"
        is RustType.Option -> "${this.name}<${this.value.render(fullyQualified)}>"
        is RustType.Box -> "${this.name}<${this.value.render(fullyQualified)}>"
        is RustType.Reference -> "&${this.lifetime?.let { "'$it" } ?: ""} ${this.member.render(fullyQualified)}"
        is RustType.Option -> "${this.name}<${this.member.render(fullyQualified)}>"
        is RustType.Box -> "${this.name}<${this.member.render(fullyQualified)}>"
        is RustType.Dyn -> "${this.name} ${this.member.render(fullyQualified)}"
        is RustType.Opaque -> this.name
    }
    return "$namespace$base"
@@ -115,18 +121,14 @@ fun <T : RustType> RustType.contains(t: T): Boolean {
    }

    return when (this) {
        is RustType.Vec -> this.member.contains(t)
        is RustType.HashSet -> this.member.contains(t)
        is RustType.Reference -> this.value.contains(t)
        is RustType.Option -> this.value.contains(t)
        is RustType.Box -> this.value.contains(t)
        is Container -> this.member.contains(t)
        else -> false
    }
}

inline fun <reified T : Container> RustType.stripOuter(): RustType {
    return when (this) {
        is T -> this.value
        is T -> this.member
        else -> this
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -156,6 +156,11 @@ fun CodeWriter.raw(text: String) = writeInline(escape(text))

typealias Writable = RustWriter.() -> Unit

/** Helper to allow coercing the Writeable signature
 *  writable { rust("fn foo() { }")
 */
fun writeable(w: Writable): Writable = w

class RustWriter private constructor(
    private val filename: String,
    val namespace: String,
+3 −1
Original line number Diff line number Diff line
@@ -137,9 +137,11 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n
        val InstantEpoch = RuntimeType("instant_epoch", InlineDependency.instantEpoch(), "crate")
        val InstantHttpDate = RuntimeType("instant_httpdate", InlineDependency.instantHttpDate(), "crate")
        val Instant8601 = RuntimeType("instant_8601", InlineDependency.instant8601(), "crate")
        val IdempotencyToken = RuntimeType("idempotency_token", InlineDependency.idempotencyToken(), "crate")

        val Config = RuntimeType("config", null, "crate")

        fun BlobSerde(runtimeConfig: RuntimeConfig) = RuntimeType("blob_serde", InlineDependency.blobSerde(runtimeConfig), "crate")
        val Config = RuntimeType("config", InlineDependency.config(), "crate")

        fun forInlineFun(name: String, module: String, func: (RustWriter) -> Unit) = RuntimeType(
            name = name,
+7 −2
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import software.amazon.smithy.model.shapes.StructureShape
import software.amazon.smithy.model.shapes.TimestampShape
import software.amazon.smithy.model.shapes.UnionShape
import software.amazon.smithy.model.traits.EnumTrait
import software.amazon.smithy.model.traits.IdempotencyTokenTrait
import software.amazon.smithy.rust.codegen.lang.RustType
import software.amazon.smithy.rust.codegen.lang.RustWriter
import software.amazon.smithy.rust.codegen.lang.conditionalBlock
@@ -242,12 +243,16 @@ class Instantiator(
        writer.rustBlock("") {
            val isSyntheticInput = shape.hasTrait(SyntheticInputTrait::class.java)
            if (isSyntheticInput) {
                writer.rust(
                rust(
                    """
                let config = #T::Config::builder().token_provider("00000000-0000-4000-8000-000000000000").build();
                let config = #T::Config::builder()
            """,
                    RuntimeType.Config
                )
                if (shape.allMembers.values.any { it.hasTrait(IdempotencyTokenTrait::class.java) }) {
                    rust(".token_provider(\"00000000-0000-4000-8000-000000000000\")")
                }
                rust(".build();")
            } else {
                write("let _ = 5;")
            }
Loading