-
Notifications
You must be signed in to change notification settings - Fork 198
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial implementation of a Serde Decorator (#3753)
## Motivation and Context Customers want to be able to use `serde` with Smithy models. For details, see the [RFC](https://github.com/smithy-lang/smithy-rs/blob/configurable-serde/design/src/rfcs/rfc0045_configurable_serde.md) ## Description <!--- Why is this change required? What problem does it solve? --> <!--- If it fixes an open issue, please link to the issue here --> Implementation of `serde::Serialize` for smithy-rs code generators. This takes the approach of a trait, `SerializeConfigured` which _returns_ a type that implements `Serialize`. This allows customers to control serde behavior for their use case. ```rust /// Trait that allows configuring serialization /// **This trait should not be implemented directly!** Instead, `impl Serialize for ConfigurableSerdeRef<T>`** pub trait SerializeConfigured { /// Return a `Serialize` implementation for this object that owns the object. This is what you want /// If you need to pass something that `impl`s serialize elsewhere. fn serialize_owned(self, settings: SerializationSettings) -> impl Serialize; /// Return a `Serialize` implementation for this object that borrows from the given object fn serialize_ref<'a>(&'a self, settings: &'a SerializationSettings) -> impl Serialize + 'a; } ``` This can be used as follows: ```rust serde_json::to_string(&my_struct.serialize_ref(&SerializationSettings::redact_sensitive_fields()); ``` Currently, this codegen plugin is not used by anything. It can be enabled by bringing it in scope during code generation & adding the `@serde` trait to the model for the fields where serialization is desired. This will allow the SDK (if they choose to) roll it out only for specific types or services that have customer demand. There are a number of follow on items required: - [x] Ensure the generated impls work for `Server` codegen - [ ] Generate `Deserialize` implementations - [x] Test the implementation all Smithy protocol test models (ensure it compiles) ## Testing Unit test testing serialization with a kitchen-sink model ## Checklist <!--- If a checkbox below is not applicable, then please DELETE it rather than leaving it unchecked --> - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the AWS SDK, generated SDK code, or SDK runtime crates ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: david-perez <[email protected]>
- Loading branch information
1 parent
40d6fb9
commit a107c01
Showing
23 changed files
with
1,845 additions
and
56 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import org.gradle.api.tasks.testing.logging.TestExceptionFormat | ||
|
||
plugins { | ||
kotlin("jvm") | ||
`maven-publish` | ||
} | ||
|
||
description = "Plugin to generate `serde` implementations. NOTE: This is separate from generalized serialization." | ||
extra["displayName"] = "Smithy :: Rust :: Codegen Serde" | ||
extra["moduleName"] = "software.amazon.smithy.rust.codegen.client" | ||
|
||
group = "software.amazon.smithy.rust.codegen.serde" | ||
version = "0.1.0" | ||
|
||
val smithyVersion: String by project | ||
|
||
dependencies { | ||
implementation(project(":codegen-core")) | ||
implementation(project(":codegen-client")) | ||
implementation(project(":codegen-server")) | ||
} | ||
|
||
java { | ||
sourceCompatibility = JavaVersion.VERSION_11 | ||
targetCompatibility = JavaVersion.VERSION_11 | ||
} | ||
|
||
tasks.compileKotlin { | ||
kotlinOptions.jvmTarget = "11" | ||
} | ||
|
||
// Reusable license copySpec | ||
val licenseSpec = copySpec { | ||
from("${project.rootDir}/LICENSE") | ||
from("${project.rootDir}/NOTICE") | ||
} | ||
|
||
// Configure jars to include license related info | ||
tasks.jar { | ||
metaInf.with(licenseSpec) | ||
inputs.property("moduleName", project.name) | ||
manifest { | ||
attributes["Automatic-Module-Name"] = project.name | ||
} | ||
} | ||
|
||
val sourcesJar by tasks.creating(Jar::class) { | ||
group = "publishing" | ||
description = "Assembles Kotlin sources jar" | ||
archiveClassifier.set("sources") | ||
from(sourceSets.getByName("main").allSource) | ||
} | ||
|
||
val isTestingEnabled: String by project | ||
if (isTestingEnabled.toBoolean()) { | ||
val kotestVersion: String by project | ||
|
||
dependencies { | ||
runtimeOnly(project(":rust-runtime")) | ||
testImplementation("org.junit.jupiter:junit-jupiter:5.6.1") | ||
testImplementation("software.amazon.smithy:smithy-validation-model:$smithyVersion") | ||
testImplementation("software.amazon.smithy:smithy-aws-protocol-tests:$smithyVersion") | ||
testImplementation("io.kotest:kotest-assertions-core-jvm:$kotestVersion") | ||
} | ||
|
||
tasks.compileTestKotlin { | ||
kotlinOptions.jvmTarget = "11" | ||
} | ||
|
||
tasks.test { | ||
useJUnitPlatform() | ||
testLogging { | ||
events("failed") | ||
exceptionFormat = TestExceptionFormat.FULL | ||
showCauses = true | ||
showExceptions = true | ||
showStackTraces = true | ||
} | ||
} | ||
} | ||
|
||
publishing { | ||
publications { | ||
create<MavenPublication>("default") { | ||
from(components["java"]) | ||
artifact(sourcesJar) | ||
} | ||
} | ||
repositories { maven { url = uri(layout.buildDirectory.dir("repository")) } } | ||
} |
80 changes: 80 additions & 0 deletions
80
codegen-serde/src/main/kotlin/software/amazon/smithy/rust/codegen/serde/SerdeDecorator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package software.amazon.smithy.rust.codegen.serde | ||
|
||
import software.amazon.smithy.model.neighbor.Walker | ||
import software.amazon.smithy.model.shapes.Shape | ||
import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext | ||
import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator | ||
import software.amazon.smithy.rust.codegen.core.rustlang.Attribute | ||
import software.amazon.smithy.rust.codegen.core.rustlang.Feature | ||
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule | ||
import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext | ||
import software.amazon.smithy.rust.codegen.core.smithy.RustCrate | ||
import software.amazon.smithy.rust.codegen.core.util.hasTrait | ||
import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext | ||
import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator | ||
|
||
val SerdeFeature = Feature("serde", false, listOf("dep:serde")) | ||
val SerdeModule = | ||
RustModule.public( | ||
"serde", | ||
additionalAttributes = listOf(Attribute.featureGate(SerdeFeature.name)), | ||
documentationOverride = "Implementations of `serde` for model types. NOTE: These implementations are NOT used for wire serialization as part of a Smithy protocol and WILL NOT match the wire format. They are provided for convenience only.", | ||
) | ||
|
||
class ClientSerdeDecorator : ClientCodegenDecorator { | ||
override val name: String = "ClientSerdeDecorator" | ||
override val order: Byte = 0 | ||
|
||
override fun extras( | ||
codegenContext: ClientCodegenContext, | ||
rustCrate: RustCrate, | ||
) { | ||
rustCrate.mergeFeature(SerdeFeature) | ||
val generator = SerializeImplGenerator(codegenContext) | ||
rustCrate.withModule(SerdeModule) { | ||
serializationRoots(codegenContext).forEach { | ||
generator.generateRootSerializerForShape( | ||
it, | ||
)(this) | ||
} | ||
addDependency(SupportStructures.serializeRedacted().toSymbol()) | ||
addDependency(SupportStructures.serializeUnredacted().toSymbol()) | ||
} | ||
} | ||
} | ||
|
||
class ServerSerdeDecorator : ServerCodegenDecorator { | ||
override val name: String = "ServerSerdeDecorator" | ||
override val order: Byte = 0 | ||
|
||
override fun extras( | ||
codegenContext: ServerCodegenContext, | ||
rustCrate: RustCrate, | ||
) { | ||
rustCrate.mergeFeature(SerdeFeature) | ||
val generator = SerializeImplGenerator(codegenContext) | ||
rustCrate.withModule(SerdeModule) { | ||
serializationRoots(codegenContext).forEach { | ||
generator.generateRootSerializerForShape( | ||
it, | ||
)(this) | ||
} | ||
addDependency(SupportStructures.serializeRedacted().toSymbol()) | ||
addDependency(SupportStructures.serializeUnredacted().toSymbol()) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* All entry points for serialization in the service closure. | ||
*/ | ||
fun serializationRoots(ctx: CodegenContext): List<Shape> { | ||
val serviceShape = ctx.serviceShape | ||
val walker = Walker(ctx.model) | ||
return walker.walkShapes(serviceShape).filter { it.hasTrait<SerdeTrait>() } | ||
} |
Oops, something went wrong.