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

Smithy async docs (#1278)

* Add lints to aws-smithy-async

* Write some event stream docs, and enable lints

* Codegen docs

* Fix one missing doc

* Compile fixes

* remove accidentally added code

* Update codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt

* Update response headers docs
parent e78d40f0
Loading
Loading
Loading
Loading
+39 −0
Original line number Diff line number Diff line
@@ -44,6 +44,45 @@
//! let client = aws_sdk_dynamodb::Client::new(&config);
//! # }
//! ```
//!
//! Override configuration after construction of `SdkConfig`:
//!
//! ```no_run
//! # use aws_types::SdkConfig;
//! # mod aws_sdk_dynamodb {
//! #   pub mod config {
//! #     pub struct Builder;
//! #     impl Builder {
//! #       pub fn credentials_provider(
//! #         self,
//! #         credentials_provider: impl aws_types::credentials::ProvideCredentials + 'static) -> Self { self }
//! #       pub fn build(self) -> Builder { self }
//! #     }
//! #     impl From<&aws_types::SdkConfig> for Builder {
//! #       fn from(_: &aws_types::SdkConfig) -> Self {
//! #           todo!()
//! #       }
//! #     }
//! #   }
//! #   pub struct Client;
//! #   impl Client {
//! #     pub fn from_conf(conf: config::Builder) -> Self { Client }
//! #     pub fn new(config: &aws_types::SdkConfig) -> Self { Client }
//! #   }
//! # }
//! # async fn docs() {
//! # use aws_config::meta::region::RegionProviderChain;
//! # fn custom_provider(base: &SdkConfig) -> impl aws_types::credentials::ProvideCredentials {
//! #   base.credentials_provider().unwrap().clone()
//! # }
//! let sdk_config = aws_config::load_from_env().await;
//! let custom_credentials_provider = custom_provider(&sdk_config);
//! let dynamo_config = aws_sdk_dynamodb::config::Builder::from(&sdk_config)
//!   .credentials_provider(custom_credentials_provider)
//!   .build();
//! let client = aws_sdk_dynamodb::Client::from_conf(dynamo_config);
//! # }
//! ```

#[allow(dead_code)]
const PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
+3 −0
Original line number Diff line number Diff line
@@ -9,6 +9,9 @@ import software.amazon.smithy.model.Model
import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.model.shapes.ShapeId

/**
 * Code generation mode: In some situations, codegen has different behavior for client vs. server (eg. required fields)
 */
sealed class CodegenMode {
    object Server : CodegenMode()
    object Client : CodegenMode()
+7 −0
Original line number Diff line number Diff line
@@ -9,6 +9,13 @@ import software.amazon.smithy.model.node.Node
import software.amazon.smithy.model.shapes.ShapeId
import software.amazon.smithy.model.traits.Trait

/**
 * Trait indicating that this shape should be represented with `Box<T>` when converted into Rust
 *
 * This is used to handle recursive shapes. See RecursiveShapeBoxer.
 *
 * This trait is synthetic, applied during code generation, and never used in actual models.
 */
class RustBoxTrait : Trait {
    val ID = ShapeId.from("software.amazon.smithy.rust.codegen.smithy.rust.synthetic#box")
    override fun toNode(): Node = Node.objectNode()
+8 −0
Original line number Diff line number Diff line
@@ -48,6 +48,14 @@ class StreamingShapeSymbolProvider(private val base: RustSymbolProvider, private
    }
}

/**
 * SymbolProvider to drop the clone and PartialEq bounds in streaming shapes
 *
 * Streaming shapes cannot be cloned and equality cannot be checked without reading the body. Because of this, these shapes
 * do not implement `Clone` or `PartialEq`.
 *
 * Note that since streaming members can only be used on the root shape, this can only impact input and output shapes.
 */
class StreamingShapeMetadataProvider(private val base: RustSymbolProvider, private val model: Model) : SymbolMetadataProvider(base) {
    override fun memberMeta(memberShape: MemberShape): RustMetadata {
        return base.toSymbol(memberShape).expectRustMetadata()
+52 −12
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import software.amazon.smithy.rust.codegen.util.toPascalCase
import software.amazon.smithy.rust.codegen.util.toSnakeCase
import kotlin.reflect.KClass

/** Map from Smithy Shapes to Rust Types */
val SimpleShapes: Map<KClass<out Shape>, RustType> = mapOf(
    BooleanShape::class to RustType.Bool,
    FloatShape::class to RustType.Float(32),
@@ -74,6 +75,11 @@ val DefaultConfig =
        codegenConfig = CodegenConfig()
    )

/**
 * Container type for the file a symbol should be written to
 *
 * Downstream code uses symbol location to determine which file to use acquiring a writer
 */
data class SymbolLocation(val namespace: String) {
    val filename = "$namespace.rs"
}
@@ -85,6 +91,11 @@ val Serializers = SymbolLocation("serializer")
val Inputs = SymbolLocation("input")
val Outputs = SymbolLocation("output")

/**
 * Make the Rust type of a symbol optional (hold `Option<T>`)
 *
 * This is idempotent and will have no change if the type is already optional.
 */
fun Symbol.makeOptional(): Symbol {
    return if (isOptional()) {
        this
@@ -98,6 +109,7 @@ fun Symbol.makeOptional(): Symbol {
    }
}

/** Map the RustType of a symbol with [f] */
fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol {
    val newType = f(this.rustType())
    return Symbol.builder().rustType(newType)
@@ -106,17 +118,7 @@ fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol {
        .build()
}

fun Symbol.makeRustBoxed(): Symbol {
    val symbol = this
    val rustType = RustType.Box(symbol.rustType())
    return with(Symbol.builder()) {
        rustType(rustType)
        addReference(symbol)
        name(rustType.name)
        build()
    }
}

/** Set the symbolLocation for this symbol builder */
fun Symbol.Builder.locatedIn(symbolLocation: SymbolLocation): Symbol.Builder {
    val currentRustType = this.build().rustType()
    check(currentRustType is RustType.Opaque) { "Only Opaque can have their namespace updated" }
@@ -126,8 +128,20 @@ fun Symbol.Builder.locatedIn(symbolLocation: SymbolLocation): Symbol.Builder {
        .rustType(newRustType)
}

/**
 * Track both the past and current name of a symbol
 *
 * When a symbol name conflicts with another name, we need to rename it. This tracks both names enabling us to generate helpful
 * docs that cover both cases.
 *
 * Note that this is only used for enum shapes an enum variant does not have it's own symbol. For structures, the [Symbol.renamedFrom]
 * field will be set.
 */
data class MaybeRenamed(val name: String, val renamedFrom: String?)

/**
 * SymbolProvider interface that carries both the inner configuration and a function to produce an enum variant name.
 */
interface RustSymbolProvider : SymbolProvider {
    fun config(): SymbolVisitorConfig
    fun toEnumVariantName(definition: EnumDefinition): MaybeRenamed?
@@ -142,6 +156,13 @@ fun SymbolProvider.wrapOptional(member: MemberShape, value: String): String = va
 */
fun SymbolProvider.toOptional(member: MemberShape, value: String): String = value.letIf(!toSymbol(member).isOptional()) { "Some($value)" }

/**
 * Base converter from `Shape` to `Symbol`. Shapes are the direct contents of the `Smithy` model. `Symbols` carry information
 * about Rust types, namespaces, dependencies, metadata as well as other information required to render a symbol.
 *
 * This is composed with other symbol visitors to handle behavior like Streaming shapes and determining the correct
 * derives for a given shape.
 */
class SymbolVisitor(
    private val model: Model,
    private val serviceShape: ServiceShape?,
@@ -155,6 +176,10 @@ class SymbolVisitor(
        return shape.accept(this)
    }

    /**
     * Services can rename their contained shapes. See https://awslabs.github.io/smithy/1.0/spec/core/model.html#service
     * specifically, `rename`
     */
    private fun Shape.contextName(): String {
        return if (serviceShape != null) {
            id.getName(serviceShape)
@@ -163,6 +188,12 @@ class SymbolVisitor(
        }
    }

    /**
     * Return the name of a given `enum` variant. Note that this refers to `enum` in the Smithy context
     * where enum is a trait that can be applied to [StringShape] and not in the Rust context of an algebraic data type.
     *
     * Because enum variants are not member shape, a separate handler is required.
     */
    override fun toEnumVariantName(definition: EnumDefinition): MaybeRenamed? {
        val baseName = definition.name.orNull()?.toPascalCase() ?: return null
        return MaybeRenamed(baseName, null)
@@ -187,6 +218,9 @@ class SymbolVisitor(
            symbol
        }

    /**
     * Produce `Box<T>` when the shape has the `RustBoxTrait`
     */
    private fun handleRustBoxing(symbol: Symbol, shape: Shape): Symbol {
        return if (shape.hasTrait<RustBoxTrait>()) {
            val rustType = RustType.Box(symbol.rustType())
@@ -336,6 +370,9 @@ fun Symbol.Builder.setDefault(default: Default): Symbol.Builder {
    return this.putProperty(SYMBOL_DEFAULT, default)
}

/**
 * Type representing the default value for a given type. (eg. for Strings, this is `""`)
 */
sealed class Default {
    /**
     * This symbol has no default value. If the symbol is not optional, this will be an error during builder construction
@@ -361,12 +398,15 @@ fun Symbol.isOptional(): Boolean = when (this.rustType()) {
    else -> false
}

fun Symbol.isBoxed(): Boolean = rustType().stripOuter<RustType.Option>() is RustType.Box
fun Symbol.isRustBoxed(): Boolean = rustType().stripOuter<RustType.Option>() is RustType.Box

// Symbols should _always_ be created with a Rust type & shape attached
fun Symbol.rustType(): RustType = this.getProperty(RUST_TYPE_KEY, RustType::class.java).get()
fun Symbol.shape(): Shape = this.expectProperty(SHAPE_KEY, Shape::class.java)

/**
 * Utility function similar to `let` that conditionally applies [f] only if [cond] is true.
 */
fun <T> T.letIf(cond: Boolean, f: (T) -> T): T {
    return if (cond) {
        f(this)
Loading