From 4f351d91c292b9b39a91878169912ce51585b886 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Fri, 15 Oct 2021 18:53:08 -0400 Subject: [PATCH] Add more docs to codegen --- .../rust/codegen/smithy/CodegenContext.kt | 24 ++++++++ .../rust/codegen/smithy/CodegenDelegator.kt | 39 +++++++++++++ .../rust/codegen/smithy/RuntimeTypes.kt | 56 +++++++++++++++++++ .../RetryConfigDecorator.kt | 2 +- gradle/jvm.gradle | 39 ------------- 5 files changed, 120 insertions(+), 40 deletions(-) rename codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/{ => customizations}/RetryConfigDecorator.kt (99%) delete mode 100644 gradle/jvm.gradle diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt index bb2a23bc5e..57d4c24007 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenContext.kt @@ -13,12 +13,36 @@ import software.amazon.smithy.model.shapes.ShapeId * Configuration needed to generate the client for a given Service<->Protocol pair */ data class CodegenContext( + /** + * The smithy model. + * + * Note: This model may or not be pruned to the given service closure, so ensure that `serviceShape` is used as + * an entry point. + */ val model: Model, val symbolProvider: RustSymbolProvider, + /** + * Configuration of the runtime package: + * - Where are the runtime crates (smithy-*) located on the file system? Or are they versioned? + * - What are they called? + */ val runtimeConfig: RuntimeConfig, + /** + * Entrypoint service shape for code generation + */ val serviceShape: ServiceShape, + /** + * Smithy Protocol to generate, eg. RestJson1 + */ val protocol: ShapeId, + /** + * The name of the cargo crate to generate eg. `aws-sdk-s3` + * This is loaded from the smithy-build.json during codegen. + */ val moduleName: String, + /** + * Settings loaded from smithy-build.json + */ val settings: RustSettings, ) { constructor( diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt index 96dd81ee5e..e946736aec 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt @@ -21,18 +21,46 @@ import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.smithy.generators.LibRsGenerator import software.amazon.smithy.rust.codegen.smithy.generators.ManifestCustomizations +/** + * RustCrate abstraction. + * + * **Note**: This is the only implementation, `open` only for test purposes. + * + * All code-generation at some point goes through this class. `RustCrate` maintains a `CodegenWriterDelegator` internally + * which tracks a set of file-writer pairs and allows them to be loaded and cached (see: [useShapeWriter]) + * + * On top of this, it adds Rust specific features: + * - Generation of a `lib.rs` which adds `mod` statements automatically for every module that was used + * - Tracking dependencies and crate features used during code generation, enabling generation of `Cargo.toml` + * + * Users will generally want to use two main entry points: + * 1. [useShapeWriter]: Find or create a writer that will contain a given shape. See [locatedIn] for context about how + * shape locations are determined. + * 2. [finalize]: Write the crate out to the file system, generating a lib.rs and Cargo.toml + */ open class RustCrate( fileManifest: FileManifest, symbolProvider: SymbolProvider, + /** + * For core modules like `input`, `output`, and `error`, we need to specify whether these modules should be public or + * private as well as any other metadata. [baseModules] enables configuring this. See [DefaultPublicModules]. + */ baseModules: Map ) { private val inner = CodegenWriterDelegator(fileManifest, symbolProvider, RustWriter.Factory) private val modules: MutableMap = baseModules.toMutableMap() private val features: MutableSet = mutableSetOf() + + /** + * Write into the module that this shape is [locatedIn] + */ fun useShapeWriter(shape: Shape, f: (RustWriter) -> Unit) { inner.useShapeWriter(shape, f) } + /** + * Write directly into lib.rs + */ fun lib(moduleWriter: (RustWriter) -> Unit) { inner.useFileWriter("src/lib.rs", "crate", moduleWriter) } @@ -51,6 +79,11 @@ open class RustCrate( } } + /** + * Finalize Cargo.toml and lib.rs and flush the writers to the file system. + * + * This is also where inline dependencies are actually reified and written, potentially recursively. + */ fun finalize( settings: RustSettings, model: Model, @@ -82,6 +115,9 @@ open class RustCrate( } } + /** + * Create a new module directly. The resulting module will be placed in `src/.rs` + */ fun withModule( module: RustModule, moduleWriter: (RustWriter) -> Unit @@ -92,6 +128,9 @@ open class RustCrate( return this } + /** + * Create a new file directly + */ fun withFile(filename: String, fileWriter: (RustWriter) -> Unit) { inner.useFileWriter(filename) { fileWriter(it) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt index 8e901551be..344a819634 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt @@ -20,8 +20,20 @@ import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.asType import java.util.Optional +/** + * Location of the runtime crates (smithy-http, smithy-types etc.) + * + * This can be configured via the `runtimeConfig.version` field in smithy-build.json + */ sealed class RuntimeCrateLocation { + /** + * Relative path to find the runtime crates, eg. `../` + */ data class Path(val path: String) : RuntimeCrateLocation() + + /** + * Version for the runtime crates, eg. `v0.0.1-alpha` + */ data class Versioned(val version: String) : RuntimeCrateLocation() } @@ -30,12 +42,18 @@ fun RuntimeCrateLocation.crateLocation(): DependencyLocation = when (this) { is RuntimeCrateLocation.Versioned -> CratesIo(this.version) } +/** + * Prefix & crate location for the runtime crates. + */ data class RuntimeConfig( val cratePrefix: String = "smithy", val runtimeCrateLocation: RuntimeCrateLocation = RuntimeCrateLocation.Path("../") ) { companion object { + /** + * Load a `RuntimeConfig` from an [ObjectNode] (JSON) + */ fun fromNode(node: Optional): RuntimeConfig { return if (node.isPresent) { val runtimeCrateLocation = if (node.get().containsMember("version")) { @@ -57,7 +75,31 @@ data class RuntimeConfig( CargoDependency("$cratePrefix-$runtimeCrateName", runtimeCrateLocation.crateLocation(), optional = optional) } +/** + * `RuntimeType` captures all necessary information to render a type into a Rust file: + * - [name]: What type is this? + * - [dependency]: What other crates, if any, are required to use this type? + * - [namespace]: Where can we find this type. + * + * For example: + * + * `http::header::HeaderName` + * ------------ ---------- + * | | + * [namespace] [name] + * + * This type would have a [CargoDependency] pointing to the `http` crate. + * + * By grouping all of this information, when we render a type into a [RustWriter], we can not only render a fully qualified + * name, but also ensure that we automatically add any dependencies **as they are used**. + */ data class RuntimeType(val name: String?, val dependency: RustDependency?, val namespace: String) { + /** + * Convert this [RuntimeType] into a [Symbol]. + * + * This is not commonly required, but is occasionally useful when you want to force an import without referencing a type + * (eg. when bringing a trait into scope). See [CodegenWriter.addUseImports]. + */ fun toSymbol(): Symbol { val builder = Symbol.builder().name(name).namespace(namespace, "::") .rustType(RustType.Opaque(name ?: "", namespace = namespace)) @@ -66,17 +108,31 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n return builder.build() } + /** + * Create a new [RuntimeType] with a nested name. + * + * # Example + * ```kotlin + * val http = CargoDependency.http.member("Request") + * ``` + */ fun member(member: String): RuntimeType { val newName = name?.let { "$name::$member" } ?: member return copy(name = newName) } + /** + * Returns the fully qualified name for this type + */ fun fullyQualifiedName(): String { val postFix = name?.let { "::$name" } ?: "" return "$namespace$postFix" } // TODO: refactor to be RuntimeTypeProvider a la Symbol provider that packages the `RuntimeConfig` state. + /** + * The companion object contains commonly used RuntimeTypes + */ companion object { fun errorKind(runtimeConfig: RuntimeConfig) = RuntimeType( "ErrorKind", diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RetryConfigDecorator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/RetryConfigDecorator.kt similarity index 99% rename from codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RetryConfigDecorator.kt rename to codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/RetryConfigDecorator.kt index 472185f2c1..caa09c07c7 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RetryConfigDecorator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/customizations/RetryConfigDecorator.kt @@ -114,7 +114,7 @@ class RetryConfigProviderConfig(codegenContext: CodegenContext) : ConfigCustomiz self.set_retry_config(Some(retry_config)); self } - + /// Set the retry_config for the builder /// /// ## Examples diff --git a/gradle/jvm.gradle b/gradle/jvm.gradle deleted file mode 100644 index 7e0ea83924..0000000000 --- a/gradle/jvm.gradle +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ - -kotlin { - targets { - fromPreset(presets.jvm, 'jvm') - } - - sourceSets { - jvmMain.dependencies { - api group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlinVersion - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" - } - - jvmTest.dependencies { - api 'org.jetbrains.kotlin:kotlin-test' - api 'org.jetbrains.kotlin:kotlin-test-junit' - api 'junit:junit:$junitVersion' -// api "org.junit.jupiter:junit-jupiter-api" - - api group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit', version: kotlinVersion -// api group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit5', version: kotlinVersion - api group: 'junit', name: 'junit', version: junitVersion -// api group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: junitVersion - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-debug:$coroutinesVersion" - implementation "io.kotest:kotest-assertions-core-jvm:$kotestVersion" - } - } - -} - -jvmTest { - testLogging { - events("passed", "skipped", "failed") - showStandardStreams = true - } -}