diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt index 0189a4cb65..1d3ffabbb7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.NoOpEventStreamSigningDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.RequiredCustomizations +import software.amazon.smithy.rust.codegen.client.smithy.customize.SerdeDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointsDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientDecorator import software.amazon.smithy.rust.codegen.client.testutil.DecoratableBuildPlugin @@ -53,6 +54,7 @@ class RustClientCodegenPlugin : DecoratableBuildPlugin() { val codegenDecorator = CombinedClientCodegenDecorator.fromClasspath( context, + SerdeDecorator(), ClientCustomizations(), RequiredCustomizations(), FluentClientDecorator(), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/SerdeDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/SerdeDecorator.kt new file mode 100644 index 0000000000..56a777b470 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/SerdeDecorator.kt @@ -0,0 +1,28 @@ +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* SPDX-License-Identifier: Apache-2.0 +*/ + +package software.amazon.smithy.rust.codegen.client.smithy.customize + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.core.rustlang.Feature +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate + +/** + * This class, + * - Adds serde as a dependency + * + */ +class SerdeDecorator : ClientCodegenDecorator { + override val name: String = "SerdeDecorator" + override val order: Byte = -1 + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + fun _feature(feature_name: String, crate_name: String): Feature { + return Feature(feature_name, false, listOf(crate_name + "/" + feature_name)) + } + rustCrate.mergeFeature(_feature("serde-serialize", "aws-smithy-types")) + rustCrate.mergeFeature(_feature("serde-deserialize", "aws-smithy-types")) + } +} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index 6d355da68a..6083b73d72 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -16,6 +16,7 @@ import java.nio.file.Path sealed class DependencyScope { object Dev : DependencyScope() object Compile : DependencyScope() + object CfgUnstable : DependencyScope() } sealed class DependencyLocation @@ -245,5 +246,8 @@ data class CargoDependency( fun smithyQuery(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-query") fun smithyTypes(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-types") fun smithyXml(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-xml") + + // behind feature-gate + val Serde = CargoDependency("serde", CratesIo("1.0"), features = setOf("derive"), scope = DependencyScope.CfgUnstable) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index f4fbfd5b70..51fd2683b8 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -446,6 +446,16 @@ class Attribute(val inner: Writable) { } } + // These were supposed to be a part of companion object but we decided to move it out to here to avoid NPE + // You can find the discussion here. + // https://github.com/awslabs/smithy-rs/discussions/2248 + public fun SerdeSerialize(): Attribute { + return Attribute(cfgAttr(all(writable("aws_sdk_unstable"), feature("serde-serialize")), derive(RuntimeType.SerdeSerialize))) + } + public fun SerdeDeserialize(): Attribute { + return Attribute(cfgAttr(all(writable("aws_sdk_unstable"), feature("serde-deserialize")), derive(RuntimeType.SerdeDeserialize))) + } + companion object { val AllowClippyBoxedLocal = Attribute(allow("clippy::boxed_local")) val AllowClippyLetAndReturn = Attribute(allow("clippy::let_and_return")) @@ -498,6 +508,7 @@ class Attribute(val inner: Writable) { } fun all(vararg attrMacros: Writable): Writable = macroWithArgs("all", *attrMacros) + fun cfgAttr(vararg attrMacros: Writable): Writable = macroWithArgs("cfg_attr", *attrMacros) fun allow(lints: Collection): Writable = macroWithArgs("allow", *lints.toTypedArray()) fun allow(vararg lints: String): Writable = macroWithArgs("allow", *lints) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 1c489f188f..d49ae411b7 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -240,6 +240,11 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) val ConstrainedTrait = RuntimeType("crate::constrained::Constrained", InlineDependency.constrained()) val MaybeConstrained = RuntimeType("crate::constrained::MaybeConstrained", InlineDependency.constrained()) + // serde types. Gated behind `CfgUnstable`. + val Serde = CargoDependency.Serde.toType() + val SerdeSerialize = Serde.resolve("Serialize") + val SerdeDeserialize = Serde.resolve("Deserialize") + // smithy runtime types fun smithyAsync(runtimeConfig: RuntimeConfig) = CargoDependency.smithyAsync(runtimeConfig).toType() fun smithyChecksums(runtimeConfig: RuntimeConfig) = CargoDependency.smithyChecksums(runtimeConfig).toType() diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index 69aca45630..ad7fe27b60 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -4,7 +4,6 @@ */ package software.amazon.smithy.rust.codegen.core.smithy.generators - import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model @@ -27,6 +26,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.documentShape import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustInline import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.withBlock @@ -43,6 +43,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.locatedIn import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.smithy.shape import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -206,13 +207,19 @@ class BuilderGenerator( } private fun renderBuilder(writer: RustWriter) { + writer.docs("This is the datatype returned when calling `Builder::build()`.") + writer.rustInline("pub type OutputShape = #T;", structureSymbol) writer.docs("A builder for #D.", structureSymbol) Attribute(derive(builderDerives)).render(writer) + RenderSerdeAttribute.forStructureShape(writer, shape, model) + SensitiveWarning.addDoc(writer, shape) writer.rustBlock("pub struct $builderName") { + // add serde for (member in members) { val memberName = symbolProvider.toMemberName(member) // All fields in the builder are optional. val memberSymbol = symbolProvider.toSymbol(member).makeOptional() + SensitiveWarning.addDoc(writer, member) renderBuilderMember(this, memberName, memberSymbol) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt index cceced878b..fee9e4e0e1 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt @@ -74,6 +74,8 @@ class CargoTomlGenerator( .associate { it.name to it.toMap() }, "dev-dependencies" to dependencies.filter { it.scope == DependencyScope.Dev } .associate { it.name to it.toMap() }, + "target.'cfg(aws_sdk_unstable)'.dependencies" to dependencies.filter { it.scope == DependencyScope.CfgUnstable } + .associate { it.name to it.toMap() }, "features" to cargoFeatures.toMap(), ).deepMergeWith(manifestCustomizations) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt index ec8f3505e9..d831ca00b2 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt @@ -4,7 +4,6 @@ */ package software.amazon.smithy.rust.codegen.core.smithy.generators - import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.StringShape @@ -139,6 +138,8 @@ open class EnumGenerator( private fun renderUnnamedEnum() { writer.documentShape(shape, model) writer.deprecatedShape(shape) + RenderSerdeAttribute.writeAttributes(writer) + SensitiveWarning.addDoc(writer, shape) meta.render(writer) writer.write("struct $enumName(String);") writer.rustBlock("impl $enumName") { @@ -178,7 +179,8 @@ open class EnumGenerator( renamedWarning.ifBlank { null }, ) writer.deprecatedShape(shape) - + RenderSerdeAttribute.writeAttributes(writer) + SensitiveWarning.addDoc(writer, shape) meta.render(writer) writer.rustBlock("enum $enumName") { sortedMembers.forEach { member -> member.render(writer) } @@ -225,6 +227,7 @@ open class EnumGenerator( part of the enums that are public interface. """.trimIndent(), ) + // adding serde features here adds attribute to the end of the file for some reason meta.render(this) rust("struct $UnknownVariantValue(pub(crate) String);") rustBlock("impl $UnknownVariantValue") { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/RenderSerdeAttribute.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/RenderSerdeAttribute.kt new file mode 100644 index 0000000000..6d33cf24e7 --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/RenderSerdeAttribute.kt @@ -0,0 +1,25 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.util.isEventStream + +public object RenderSerdeAttribute { + public fun forStructureShape(writer: RustWriter, shape: StructureShape, model: Model) { + if (shape.members().none { it.isEventStream(model) }) { + writeAttributes(writer) + } + } + + public fun writeAttributes(writer: RustWriter) { + Attribute("").SerdeSerialize().render(writer) + Attribute("").SerdeDeserialize().render(writer) + } +} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/SensitiveWarning.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/SensitiveWarning.kt new file mode 100644 index 0000000000..59483329b8 --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/SensitiveWarning.kt @@ -0,0 +1,20 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.smithy.generators + +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.traits.SensitiveTrait +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.util.hasTrait + +object SensitiveWarning { + private const val warningMessage = "/// This data may contain sensitive information; It will not be obscured when serialized.\n" + fun addDoc(writer: RustWriter, shape: T) { + if (shape.hasTrait()) { + writer.writeInline(warningMessage) + } + } +} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt index 7e88d3d1fa..b0c72571e3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGenerator.kt @@ -4,7 +4,6 @@ */ package software.amazon.smithy.rust.codegen.core.smithy.generators - import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model @@ -134,6 +133,7 @@ open class StructureGenerator( open fun renderStructureMember(writer: RustWriter, member: MemberShape, memberName: String, memberSymbol: Symbol) { writer.renderMemberDoc(member, memberSymbol) + SensitiveWarning.addDoc(writer, shape) writer.deprecatedShape(member) memberSymbol.expectRustMetadata().render(writer) writer.write("$memberName: #T,", memberSymbol) @@ -144,10 +144,13 @@ open class StructureGenerator( val containerMeta = symbol.expectRustMetadata() writer.documentShape(shape, model) writer.deprecatedShape(shape) + RenderSerdeAttribute.forStructureShape(writer, shape, model) + SensitiveWarning.addDoc(writer, shape) containerMeta.render(writer) writer.rustBlock("struct $name ${lifetimeDeclaration()}") { writer.forEachMember(members) { member, memberName, memberSymbol -> + SensitiveWarning.addDoc(writer, shape) renderStructureMember(writer, member, memberName, memberSymbol) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt index 69cd25f2ea..b0ba3b09ff 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt @@ -4,7 +4,6 @@ */ package software.amazon.smithy.rust.codegen.core.smithy.generators - import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.model.Model @@ -61,7 +60,7 @@ class UnionGenerator( fun render() { writer.documentShape(shape, model) writer.deprecatedShape(shape) - + RenderSerdeAttribute.writeAttributes(writer) val containerMeta = unionSymbol.expectRustMetadata() containerMeta.render(writer) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGeneratorTest.kt index c57ebdc727..f9b1e210ca 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGeneratorTest.kt @@ -42,4 +42,32 @@ class CargoTomlGeneratorTest { } project.compileAndTest() } + + @Test + fun `check serde features`() { + val project = TestWorkspace.testProject() + /* + ["target.'cfg(aws_sdk_unstable)'.dependencies".serde] + version = "1.0" + features = ["derive"] + serde-serialize = ["aws-smithy-types/serde-serialize"] + serde-deserialize = ["aws-smithy-types/serde-deserialize"] + */ + project.lib { + addDependency(CargoMetadata) + unitTest( + "smithy_codegen_serde_features", + """ + let metadata = cargo_metadata::MetadataCommand::new() + .exec() + .expect("could not run `cargo metadata`"); + + let features = &metadata.root_package().expect("missing root package").features; + + assert_eq!(features.get("aws-aws-smithy-types"), Some(vec!["serde-serialize", "serde-deserialize"])); + """, + ) + } + project.compileAndTest() + } } diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index 079664ada1..7b17b55722 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -40,5 +40,5 @@ name = "base64" harness = false [features] -"serialize" = [] -"deserialize" = [] +serde-serialize = [] +serde-deserialize = []