Skip to content

Commit

Permalink
Add more docs to codegen (#776)
Browse files Browse the repository at this point in the history
  • Loading branch information
rcoh authored Oct 15, 2021
1 parent 661ecab commit dbc5401
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand All @@ -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")) {
Expand All @@ -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))
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 0 additions & 39 deletions gradle/jvm.gradle

This file was deleted.

0 comments on commit dbc5401

Please sign in to comment.