Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish the smithy-json deserializer codegen #530

Merged
merged 4 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class IntegrationTestDependencies(
LibRsSection.Body -> writable {
if (hasTests) {
addDependency(runtimeConfig.awsHyper().copy(scope = DependencyScope.Dev))
addDependency(SerdeJson)
addDependency(Tokio)
}
if (hasBenches) {
Expand All @@ -61,5 +62,6 @@ class IntegrationTestDependencies(
}

val Criterion = CargoDependency("criterion", CratesIo("0.3"), scope = DependencyScope.Dev)
val SerdeJson = CargoDependency("serde_json", CratesIo("1"), features = emptyList())
val Tokio = CargoDependency("tokio", CratesIo("1"), features = listOf("macros", "test-util"), scope = DependencyScope.Dev)
fun RuntimeConfig.awsHyper() = awsRuntimeDependency("aws-hyper", features = listOf("test-util"))
Original file line number Diff line number Diff line change
Expand Up @@ -88,25 +88,12 @@ class InlineDependency(
private fun forRustFile(name: String, vararg additionalDependencies: RustDependency) =
forRustFile(name, "inlineable", *additionalDependencies)

fun awsJsonErrors(runtimeConfig: RuntimeConfig) =
forRustFile("aws_json_errors", CargoDependency.Http, CargoDependency.SmithyTypes(runtimeConfig))

fun docJson() = forRustFile("doc_json", CargoDependency.Serde)
fun instantEpoch() = forRustFile("instant_epoch", CargoDependency.Serde)
fun instantHttpDate() =
forRustFile("instant_httpdate", CargoDependency.Serde)

fun instant8601() = forRustFile("instant_iso8601", CargoDependency.Serde)
fun jsonErrors(runtimeConfig: RuntimeConfig) =
forRustFile("json_errors", CargoDependency.Http, CargoDependency.SmithyTypes(runtimeConfig))

fun idempotencyToken() =
forRustFile("idempotency_token", CargoDependency.FastRand)

fun blobSerde(runtimeConfig: RuntimeConfig) = forRustFile(
"blob_serde",
CargoDependency.Serde,
CargoDependency.SmithyHttp(runtimeConfig)
)

fun ec2QueryErrors(runtimeConfig: RuntimeConfig): InlineDependency =
forRustFile("ec2_query_errors", CargoDependency.smithyXml(runtimeConfig))

Expand Down Expand Up @@ -201,9 +188,6 @@ data class CargoDependency(
fun smithyQuery(runtimeConfig: RuntimeConfig): CargoDependency = runtimeConfig.runtimeCrate("query")
fun smithyXml(runtimeConfig: RuntimeConfig): CargoDependency = runtimeConfig.runtimeCrate("xml")

val SerdeJson: CargoDependency =
CargoDependency("serde_json", CratesIo("1"), features = listOf("float_roundtrip"))
val Serde = CargoDependency("serde", CratesIo("1"), features = listOf("derive"))
val Bytes: RustDependency = CargoDependency("bytes", CratesIo("1"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n
val Debug = stdfmt.member("Debug")
val Default: RuntimeType = RuntimeType("Default", dependency = null, namespace = "std::default")
val From = RuntimeType("From", dependency = null, namespace = "std::convert")
val Infallible = RuntimeType("Infallible", dependency = null, namespace = "std::convert")
val PartialEq = std.member("cmp::PartialEq")
val StdError = RuntimeType("Error", dependency = null, namespace = "std::error")
val String = RuntimeType("String", dependency = null, namespace = "std::string")
Expand Down Expand Up @@ -160,25 +161,9 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n
val HttpRequestBuilder = Http("request::Builder")
val HttpResponseBuilder = Http("response::Builder")

fun Serde(path: String) = RuntimeType(
path, dependency = CargoDependency.Serde, namespace = "serde"
)

val Deserialize: RuntimeType = RuntimeType("Deserialize", CargoDependency.Serde, namespace = "serde")
val Deserializer = RuntimeType("Deserializer", CargoDependency.Serde, namespace = "serde")
fun SerdeJson(path: String) =
RuntimeType(path, dependency = CargoDependency.SerdeJson, namespace = "serde_json")

val serdeJson = RuntimeType(null, dependency = CargoDependency.SerdeJson, namespace = "serde_json")

fun awsJsonErrors(runtimeConfig: RuntimeConfig) =
forInlineDependency(InlineDependency.awsJsonErrors(runtimeConfig))

val DocJson by lazy { forInlineDependency(InlineDependency.docJson()) }
fun jsonErrors(runtimeConfig: RuntimeConfig) =
forInlineDependency(InlineDependency.jsonErrors(runtimeConfig))

val InstantEpoch by lazy { forInlineDependency(InlineDependency.instantEpoch()) }
val InstantHttpDate by lazy { forInlineDependency(InlineDependency.instantHttpDate()) }
val Instant8601 by lazy { forInlineDependency(InlineDependency.instant8601()) }
val IdempotencyToken by lazy { forInlineDependency(InlineDependency.idempotencyToken()) }

val Config = RuntimeType("config", null, "crate")
Expand All @@ -205,7 +190,6 @@ data class RuntimeType(val name: String?, val dependency: RustDependency?, val n
)

val Bytes = RuntimeType("Bytes", dependency = CargoDependency.Bytes, namespace = "bytes")
fun BlobSerde(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.blobSerde(runtimeConfig))

fun forInlineDependency(inlineDependency: InlineDependency) =
RuntimeType(inlineDependency.name, inlineDependency, namespace = "crate")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ import software.amazon.smithy.model.traits.ErrorTrait
import software.amazon.smithy.model.traits.HttpLabelTrait
import software.amazon.smithy.rust.codegen.rustlang.RustType
import software.amazon.smithy.rust.codegen.rustlang.stripOuter
import software.amazon.smithy.rust.codegen.smithy.traits.InputBodyTrait
import software.amazon.smithy.rust.codegen.smithy.traits.OutputBodyTrait
import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait
import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticOutputTrait
import software.amazon.smithy.rust.codegen.util.hasTrait
Expand Down Expand Up @@ -257,7 +255,6 @@ class SymbolVisitor(
val isError = shape.hasTrait<ErrorTrait>()
val isInput = shape.hasTrait<SyntheticInputTrait>()
val isOutput = shape.hasTrait<SyntheticOutputTrait>()
val isBody = shape.hasTrait<InputBodyTrait>() || shape.hasTrait<OutputBodyTrait>()
val name = StringUtils.capitalize(shape.contextName()).letIf(isError && config.codegenConfig.renameExceptions) {
// TODO: Do we want to do this?
// https://github.com/awslabs/smithy-rs/issues/77
Expand All @@ -268,7 +265,6 @@ class SymbolVisitor(
isError -> builder.locatedIn(Errors)
isInput -> builder.locatedIn(Inputs)
isOutput -> builder.locatedIn(Outputs)
isBody -> builder.locatedIn(Serializers)
else -> builder.locatedIn(Models)
}.build()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import software.amazon.smithy.rust.codegen.rustlang.docs
import software.amazon.smithy.rust.codegen.rustlang.documentShape
import software.amazon.smithy.rust.codegen.rustlang.rust
import software.amazon.smithy.rust.codegen.rustlang.rustBlock
import software.amazon.smithy.rust.codegen.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.rustlang.withBlock
import software.amazon.smithy.rust.codegen.smithy.MaybeRenamed
import software.amazon.smithy.rust.codegen.smithy.RuntimeType
Expand Down Expand Up @@ -104,7 +103,6 @@ class EnumGenerator(
} else {
renderUnamedEnum()
}
renderSerde()
}

private fun renderUnamedEnum() {
Expand Down Expand Up @@ -163,21 +161,6 @@ class EnumGenerator(
}
}

private fun renderSerde() {
writer.rustTemplate(
"""
impl<'de> #{deserialize}<'de> for $enumName {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: #{deserializer}<'de> {
let data = <&str>::deserialize(deserializer)?;
Ok(Self::from(data))
}
}
""",
"deserializer" to RuntimeType.Deserializer,
"deserialize" to RuntimeType.Deserialize
)
}

private fun renderFromStr() {
writer.rustBlock("impl #T<&str> for $enumName", RuntimeType.From) {
writer.rustBlock("fn from(s: &str) -> Self") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ import software.amazon.smithy.model.shapes.TimestampShape
import software.amazon.smithy.model.shapes.UnionShape
import software.amazon.smithy.model.traits.EnumTrait
import software.amazon.smithy.model.traits.HttpPrefixHeadersTrait
import software.amazon.smithy.rust.codegen.rustlang.CargoDependency
import software.amazon.smithy.rust.codegen.rustlang.RustType
import software.amazon.smithy.rust.codegen.rustlang.RustWriter
import software.amazon.smithy.rust.codegen.rustlang.asType
import software.amazon.smithy.rust.codegen.rustlang.conditionalBlock
import software.amazon.smithy.rust.codegen.rustlang.escape
import software.amazon.smithy.rust.codegen.rustlang.rust
import software.amazon.smithy.rust.codegen.rustlang.rustBlock
import software.amazon.smithy.rust.codegen.rustlang.rustTemplate
import software.amazon.smithy.rust.codegen.rustlang.stripOuter
import software.amazon.smithy.rust.codegen.rustlang.withBlock
import software.amazon.smithy.rust.codegen.smithy.RuntimeConfig
Expand Down Expand Up @@ -113,13 +116,16 @@ class Instantiator(
is StringShape -> renderString(writer, shape, arg as StringNode)
is NumberShape -> writer.write(arg.asNumberNode().get())
is BooleanShape -> writer.write(arg.asBooleanNode().get().toString())
is DocumentShape -> {
writer.rust(
"""{
let as_json = #T! { ${Node.prettyPrintJson(arg)} };
#T::json_to_doc(as_json)
}""",
RuntimeType.SerdeJson("json"), RuntimeType.DocJson
is DocumentShape -> writer.rustBlock("") {
val smithyJson = CargoDependency.smithyJson(runtimeConfig).asType()
rustTemplate(
"""
let json_bytes = br##"${Node.prettyPrintJson(arg)}"##;
let mut tokens = #{json_token_iter}(json_bytes).peekable();
#{expect_document}(&mut tokens).expect("well formed json")
""",
"expect_document" to smithyJson.member("deserialize::token::expect_document"),
"json_token_iter" to smithyJson.member("deserialize::json_token_iter"),
)
}
else -> writer.writeWithNoFormatting("todo!() /* $shape $arg */")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,12 @@
package software.amazon.smithy.rust.codegen.smithy.generators

import software.amazon.smithy.model.knowledge.TopDownIndex
import software.amazon.smithy.model.shapes.OperationShape
import software.amazon.smithy.model.shapes.StructureShape
import software.amazon.smithy.rust.codegen.rustlang.RustModule
import software.amazon.smithy.rust.codegen.smithy.RustCrate
import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator
import software.amazon.smithy.rust.codegen.smithy.generators.config.ServiceConfigGenerator
import software.amazon.smithy.rust.codegen.smithy.generators.error.CombinedErrorGenerator
import software.amazon.smithy.rust.codegen.smithy.generators.error.TopLevelErrorGenerator
import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait
import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticOutputTrait
import software.amazon.smithy.rust.codegen.util.expectTrait
import software.amazon.smithy.rust.codegen.util.inputShape

class ServiceGenerator(
Expand Down Expand Up @@ -48,7 +43,6 @@ class ServiceGenerator(
}

TopLevelErrorGenerator(config, operations).render(rustCrate)
renderBodies(operations)

rustCrate.withModule(RustModule.Config) { writer ->
ServiceConfigGenerator.withBaseBehavior(
Expand All @@ -61,30 +55,4 @@ class ServiceGenerator(
it.write("pub use config::Config;")
}
}

private fun renderBodies(operations: List<OperationShape>) {
val inputBodies = operations.map { config.model.expectShape(it.input.get()) }.map {
it.expectTrait<SyntheticInputTrait>()
}.mapNotNull { // mapNotNull is flatMap but for null `map { it }.filter { it != null }`
it.body
}.map { // Lookup the Body structure by its id
config.model.expectShape(it, StructureShape::class.java)
}
val outputBodies = operations.map { config.model.expectShape(it.output.get()) }.map {
it.expectTrait<SyntheticOutputTrait>()
}.mapNotNull { // mapNotNull is flatMap but for null `map { it }.filter { it != null }`
it.body
}.map { // Lookup the Body structure by its id
config.model.expectShape(it, StructureShape::class.java)
}
(inputBodies + outputBodies).map { body ->
// The body symbol controls its location, usually in the serializer module
rustCrate.useShapeWriter(body) { writer ->
with(config) {
// Generate a body via the structure generator
StructureGenerator(model, symbolProvider, writer, body).render()
}
}
}
}
}
Loading