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

feat: add directed codegen #681

Merged
merged 2 commits into from
Mar 27, 2024
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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

package software.amazon.smithy.swift.codegen

import software.amazon.smithy.build.PluginContext
import software.amazon.smithy.codegen.core.SymbolProvider
import software.amazon.smithy.codegen.core.directed.CreateContextDirective
import software.amazon.smithy.codegen.core.directed.CreateSymbolProviderDirective
import software.amazon.smithy.codegen.core.directed.DirectedCodegen
import software.amazon.smithy.codegen.core.directed.GenerateEnumDirective
import software.amazon.smithy.codegen.core.directed.GenerateErrorDirective
import software.amazon.smithy.codegen.core.directed.GenerateIntEnumDirective
import software.amazon.smithy.codegen.core.directed.GenerateServiceDirective
import software.amazon.smithy.codegen.core.directed.GenerateStructureDirective
import software.amazon.smithy.codegen.core.directed.GenerateUnionDirective
import software.amazon.smithy.model.Model
import software.amazon.smithy.model.knowledge.ServiceIndex
import software.amazon.smithy.model.shapes.ServiceShape
import software.amazon.smithy.model.traits.SensitiveTrait
import software.amazon.smithy.swift.codegen.core.GenerationContext
import software.amazon.smithy.swift.codegen.integration.CustomDebugStringConvertibleGenerator
import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator
import software.amazon.smithy.swift.codegen.integration.SwiftIntegration
import software.amazon.smithy.swift.codegen.model.hasTrait
import java.util.logging.Logger

class DirectedSwiftCodegen(val context: PluginContext) :
DirectedCodegen<GenerationContext, SwiftSettings, SwiftIntegration> {
private val LOGGER = Logger.getLogger(javaClass.name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: is there a Kotlin logging library we should be using instead of the Java one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there is an "official" kotlin logging library so I am using the Java one for now. Can add it later if needed to.


override fun createSymbolProvider(directive: CreateSymbolProviderDirective<SwiftSettings>): SymbolProvider {
return SwiftSymbolProvider(directive.model(), directive.settings())
}

override fun createContext(directive: CreateContextDirective<SwiftSettings, SwiftIntegration>): GenerationContext {
val model = directive.model()
val service = directive.service()
val settings = directive.settings()
val integrations = directive.integrations()

val protocolGenerator = resolveProtocolGenerator(integrations, model, service, settings)

for (integration in integrations) {
integration.serviceErrorProtocolSymbol()?.let {
protocolGenerator?.serviceErrorProtocolSymbol = it
}
}

return GenerationContext(
directive.model(),
directive.symbolProvider(),
directive.settings(),
directive.fileManifest(),
protocolGenerator,
directive.integrations()
)
}

override fun generateService(directive: GenerateServiceDirective<GenerationContext, SwiftSettings>) {
val service = directive.service()
val settings = directive.settings()
val symbolProvider = directive.symbolProvider()
val context = directive.context()
val model = directive.model()
val integrations = context.integrations
val fileManifest = context.fileManifest
val writers = context.writerDelegator()

LOGGER.info("Generating Swift client for service ${directive.settings().service}")

var shouldGenerateTestTarget = false
context.protocolGenerator?.apply {
val ctx = ProtocolGenerator.GenerationContext(settings, model, service, symbolProvider, integrations, this.protocol, writers)
LOGGER.info("[${service.id}] Generating serde for protocol ${this.protocol}")
generateSerializers(ctx)
generateDeserializers(ctx)
generateMessageMarshallable(ctx)
generateMessageUnmarshallable(ctx)
generateCodableConformanceForNestedTypes(ctx)

initializeMiddleware(ctx)

LOGGER.info("[${service.id}] Generating unit tests for protocol ${this.protocol}")
val numProtocolUnitTestsGenerated = generateProtocolUnitTests(ctx)
shouldGenerateTestTarget = (numProtocolUnitTestsGenerated > 0)

LOGGER.info("[${service.id}] Generating service client for protocol ${this.protocol}")

generateProtocolClient(ctx)

integrations.forEach { it.writeAdditionalFiles(context, ctx, writers) }
}

println("Flushing swift writers")
val dependencies = writers.dependencies
writers.flushWriters()

println("Generating package manifest file")
writePackageManifest(settings, fileManifest, dependencies, shouldGenerateTestTarget)
}

override fun generateStructure(directive: GenerateStructureDirective<GenerationContext, SwiftSettings>) {
val shape = directive.shape()
val context = directive.context()
val model = directive.model()
val settings = directive.settings()
val symbolProvider = directive.symbolProvider()
val protocolGenerator = context.protocolGenerator
val writers = context.writerDelegator()

writers.useShapeWriter(shape) { writer: SwiftWriter ->
StructureGenerator(model, symbolProvider, writer, shape, settings, protocolGenerator?.serviceErrorProtocolSymbol).render()
}

if (shape.hasTrait<SensitiveTrait>() || shape.members().any { it.hasTrait<SensitiveTrait>() || model.expectShape(it.target).hasTrait<SensitiveTrait>() }) {
writers.useShapeExtensionWriter(shape, "CustomDebugStringConvertible") { writer: SwiftWriter ->
CustomDebugStringConvertibleGenerator(symbolProvider, writer, shape, model).render()
}
}
}

override fun generateError(directive: GenerateErrorDirective<GenerationContext, SwiftSettings>) {
val shape = directive.shape()
val context = directive.context()
val model = directive.model()
val settings = directive.settings()
val symbolProvider = directive.symbolProvider()
val protocolGenerator = context.protocolGenerator
val writers = context.writerDelegator()

writers.useShapeWriter(shape) { writer: SwiftWriter ->
StructureGenerator(model, symbolProvider, writer, shape, settings, protocolGenerator?.serviceErrorProtocolSymbol).renderErrors()
}
syall marked this conversation as resolved.
Show resolved Hide resolved

if (shape.hasTrait<SensitiveTrait>() || shape.members().any { it.hasTrait<SensitiveTrait>() || model.expectShape(it.target).hasTrait<SensitiveTrait>() }) {
writers.useShapeExtensionWriter(shape, "CustomDebugStringConvertible") { writer: SwiftWriter ->
CustomDebugStringConvertibleGenerator(symbolProvider, writer, shape, model).render()
}
}
}

override fun generateUnion(directive: GenerateUnionDirective<GenerationContext, SwiftSettings>) {
val shape = directive.shape()
val context = directive.context()
val model = directive.model()
val settings = directive.settings()
val symbolProvider = directive.symbolProvider()
val writers = context.writerDelegator()
writers.useShapeWriter(shape) { writer: SwiftWriter -> UnionGenerator(model, symbolProvider, writer, shape, settings).render() }
}

override fun generateEnumShape(directive: GenerateEnumDirective<GenerationContext, SwiftSettings>) {
val shape = directive.shape()
val context = directive.context()
val model = directive.model()
val settings = directive.settings()
val symbolProvider = directive.symbolProvider()
val writers = context.writerDelegator()
writers.useShapeWriter(shape) { writer: SwiftWriter -> EnumGenerator(model, symbolProvider, writer, shape, settings).render() }
}

override fun generateIntEnumShape(directive: GenerateIntEnumDirective<GenerationContext, SwiftSettings>) {
val shape = directive.shape()
val context = directive.context()
val model = directive.model()
val settings = directive.settings()
val symbolProvider = directive.symbolProvider()
val writers = context.writerDelegator()
writers.useShapeWriter(shape) { writer: SwiftWriter -> IntEnumGenerator(model, symbolProvider, writer, shape.asIntEnumShape().get(), settings).render() }
}

private fun resolveProtocolGenerator(
integrations: List<SwiftIntegration>,
model: Model,
service: ServiceShape,
settings: SwiftSettings
): ProtocolGenerator? {
val generators = integrations.flatMap { it.protocolGenerators }.associateBy { it.protocol }
val serviceIndex = ServiceIndex.of(model)

try {
val protocolTrait = settings.resolveServiceProtocol(serviceIndex, service, generators.keys)
return generators[protocolTrait]
} catch (ex: UnresolvableProtocolException) {
LOGGER.warning("Unable to find protocol generator for ${service.id}: ${ex.message}")
}
return null
}
}
Loading
Loading