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

Add more docs to codegen (#776)

parent 661ecab5
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -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(
+39 −0
Original line number Diff line number Diff line
@@ -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<String, RustModule>
) {
    private val inner = CodegenWriterDelegator(fileManifest, symbolProvider, RustWriter.Factory)
    private val modules: MutableMap<String, RustModule> = baseModules.toMutableMap()
    private val features: MutableSet<Feature> = 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/<modulename>.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)
+56 −0
Original line number Diff line number Diff line
@@ -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<ObjectNode>): 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",
+1 −1

File changed and moved.

Contains only whitespace changes.

gradle/jvm.gradle

deleted100644 → 0
+0 −39
Original line number Diff line number Diff line
/*
 * 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
    }
}