diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointConfigCustomizationTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointConfigCustomizationTest.kt index b2064fddfb..22c9a3f369 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointConfigCustomizationTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointConfigCustomizationTest.kt @@ -48,7 +48,7 @@ internal class EndpointConfigCustomizationTest { @Test fun `write an endpoint into the config`() { val project = stubConfigProject(EndpointConfigCustomization(TestRuntimeConfig, model.lookup("test#TestService"))) - project.useFileWriter("src/lib.rs", "crate") { + project.lib { it.addDependency(awsTypes(TestRuntimeConfig)) it.addDependency(CargoDependency.Http) it.unitTest( diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningCustomizationTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningCustomizationTest.kt index 8f2583a37e..74be357eed 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningCustomizationTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningCustomizationTest.kt @@ -15,7 +15,7 @@ internal class SigV4SigningCustomizationTest { @Test fun `generates a valid config`() { val project = stubConfigProject(SigV4SigningConfig(SigV4Trait.builder().name("test-service").build())) - project.useFileWriter("src/lib.rs", "crate") { + project.lib { it.unitTest( """ let conf = crate::config::Config::builder().build(); diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt index 0e7207613f..a660edf1f6 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt @@ -106,6 +106,8 @@ class InlineDependency( fun CargoDependency.asType(): RuntimeType = RuntimeType(null, dependency = this, namespace = this.name.replace("-", "_")) +data class Feature(val name: String, val default: Boolean, val deps: List) + /** * A dependency on an internal or external Cargo Crate */ @@ -113,6 +115,7 @@ data class CargoDependency( override val name: String, private val location: DependencyLocation, val scope: DependencyScope = DependencyScope.Compile, + val optional: Boolean = false, private val features: List = listOf() ) : RustDependency(name) { @@ -137,6 +140,9 @@ data class CargoDependency( attribs["features"] = this } } + if (optional) { + attribs["optional"] = true + } return attribs } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustModule.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustModule.kt index 0cf752f6f1..184b70cc5d 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustModule.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustModule.kt @@ -21,5 +21,8 @@ data class RustModule(val name: String, val rustMetadata: RustMetadata) { }*/ return RustModule(name, RustMetadata(public = public)) } + + val Config = default("config", public = true) + val Error = default("error", public = true) } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt index 4c836bba4c..ae102f28e7 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.rustlang import software.amazon.smithy.rust.codegen.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.util.dq /** * A hierarchy of types handled by Smithy codegen @@ -149,14 +150,15 @@ inline fun RustType.stripOuter(): RustType { * Meta information about a Rust construction (field, struct, or enum) */ data class RustMetadata( - val derives: Derives = Derives.Empty, + val derives: Attribute.Derives = Attribute.Derives.Empty, val additionalAttributes: List = listOf(), val public: Boolean ) { fun withDerives(vararg newDerive: RuntimeType): RustMetadata = this.copy(derives = derives.copy(derives = derives.derives + newDerive)) - fun attributes(): List = additionalAttributes + derives + private fun attributes(): List = additionalAttributes + derives + fun renderAttributes(writer: RustWriter): RustMetadata { attributes().forEach { it.render(writer) @@ -201,33 +203,40 @@ sealed class Attribute { val NonExhaustive = Custom("non_exhaustive") val AllowUnused = Custom("allow(dead_code)") } -} -data class Derives(val derives: Set) : Attribute() { - override fun render(writer: RustWriter) { - if (derives.isEmpty()) { - return + data class Derives(val derives: Set) : Attribute() { + override fun render(writer: RustWriter) { + if (derives.isEmpty()) { + return + } + writer.raw("#[derive(") + derives.sortedBy { it.name }.forEach { derive -> + writer.writeInline("#T, ", derive) + } + writer.write(")]") } - writer.raw("#[derive(") - derives.sortedBy { it.name }.forEach { derive -> - writer.writeInline("#T, ", derive) + + companion object { + val Empty = Derives(setOf()) } - writer.write(")]") } - companion object { - val Empty = Derives(setOf()) + data class Custom(val annotation: String, val symbols: List = listOf()) : Attribute() { + override fun render(writer: RustWriter) { + writer.raw("#[$annotation]") + symbols.forEach { + writer.addDependency(it.dependency) + } + } } -} -data class Custom(val annot: String, val symbols: List = listOf()) : Attribute() { - override fun render(writer: RustWriter) { - writer.raw("#[") - writer.writeInline(annot) - writer.write("]") + data class Cfg(val cond: String) : Attribute() { + override fun render(writer: RustWriter) { + writer.raw("#[cfg($cond)]") + } - symbols.forEach { - writer.addDependency(it.dependency) + companion object { + fun feature(feature: String) = Cfg("feature = ${feature.dq()}") } } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt index ee1873a003..cf76ad1f14 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt @@ -5,8 +5,12 @@ package software.amazon.smithy.rust.codegen.smithy +import software.amazon.smithy.build.FileManifest +import software.amazon.smithy.codegen.core.SymbolProvider import software.amazon.smithy.codegen.core.writer.CodegenWriterDelegator +import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.rust.codegen.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.rustlang.Feature import software.amazon.smithy.rust.codegen.rustlang.InlineDependency import software.amazon.smithy.rust.codegen.rustlang.RustDependency import software.amazon.smithy.rust.codegen.rustlang.RustModule @@ -15,58 +19,90 @@ import software.amazon.smithy.rust.codegen.smithy.generators.CargoTomlGenerator import software.amazon.smithy.rust.codegen.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.smithy.generators.LibRsGenerator -private fun CodegenWriterDelegator.includedModules(): List = this.writers.values.mapNotNull { it.module() } +open class RustCrate( + fileManifest: FileManifest, + symbolProvider: SymbolProvider, + baseModules: Map +) { + private val inner = CodegenWriterDelegator(fileManifest, symbolProvider, RustWriter.Factory) + private val modules: MutableMap = baseModules.toMutableMap() + private val features: MutableSet = mutableSetOf() + fun useShapeWriter(shape: Shape, f: (RustWriter) -> Unit) { + inner.useShapeWriter(shape, f) + } + + fun lib(moduleWriter: (RustWriter) -> Unit) { + inner.useFileWriter("src/lib.rs", "crate", moduleWriter) + } + + fun addFeature(feature: Feature) = this.features.add(feature) + + fun finalize(settings: RustSettings, libRsCustomizations: List) { + injectInlineDependencies() + val modules = inner.writers.values.mapNotNull { it.module() }.filter { it != "lib" } + .map { modules[it] ?: RustModule.default(it, false) } + inner.finalize(settings, libRsCustomizations, modules, this.features.toList()) + } + + private fun injectInlineDependencies() { + val unloadedDepdencies = { + this + .inner.dependencies + .map { dep -> RustDependency.fromSymbolDependency(dep) } + .filterIsInstance().distinctBy { it.key() } + .filter { !modules.contains(it.module) } + } + while (unloadedDepdencies().isNotEmpty()) { + unloadedDepdencies().forEach { dep -> + this.withModule(RustModule.default(dep.module, false)) { + dep.renderer(it) + } + } + } + } + + fun withModule( + module: RustModule, + moduleWriter: (RustWriter) -> Unit + ): RustCrate { + val moduleName = module.name + modules[moduleName] = module + inner.useFileWriter("src/$moduleName.rs", "crate::$moduleName", moduleWriter) + return this + } +} // TODO: this should _probably_ be configurable via RustSettings; 2h /** * Allowlist of modules that will be exposed publicly in generated crates */ -private val PublicModules = setOf("error", "operation", "model", "input", "output", "config") +val DefaultPublicModules = + setOf("error", "operation", "model", "input", "output", "config").map { it to RustModule.default(it, true) }.toMap() /** * Finalize all the writers by: * - inlining inline dependencies that have been used * - generating (and writing) a Cargo.toml based on the settings & the required dependencies */ -fun CodegenWriterDelegator.finalize(settings: RustSettings, libRsCustomizations: List) { - val loadDependencies = { this.dependencies.map { dep -> RustDependency.fromSymbolDependency(dep) } } - val inlineDependencies = loadDependencies().filterIsInstance().distinctBy { it.key() } - inlineDependencies.forEach { dep -> - this.useFileWriter("src/${dep.module}.rs", "crate::${dep.module}") { - dep.renderer(it) - } - } - val newDeps = loadDependencies().filterIsInstance().distinctBy { it.key() } - newDeps.forEach { dep -> - if (!this.writers.containsKey("src/${dep.module}.rs")) { - this.useFileWriter("src/${dep.module}.rs", "crate::${dep.module}") { - dep.renderer(it) - } - } - } +fun CodegenWriterDelegator.finalize( + settings: RustSettings, + libRsCustomizations: List, + modules: List, + features: List +) { this.useFileWriter("src/lib.rs", "crate::lib") { writer -> - val includedModules = this.includedModules().toSet().filter { it != "lib" } - val modules = includedModules.map { moduleName -> - RustModule.default(moduleName, PublicModules.contains(moduleName)) - } LibRsGenerator(settings.moduleDescription, modules, libRsCustomizations).render(writer) } - val cargoDependencies = loadDependencies().filterIsInstance().distinct() + val cargoDependencies = + this.dependencies.map { RustDependency.fromSymbolDependency(it) }.filterIsInstance().distinct() this.useFileWriter("Cargo.toml") { val cargoToml = CargoTomlGenerator( settings, it, cargoDependencies, + features ) cargoToml.render() } flushWriters() } - -fun CodegenWriterDelegator.withModule( - moduleName: String, - moduleWriter: RustWriter.() -> Unit -): CodegenWriterDelegator { - this.useFileWriter("src/$moduleName.rs", "crate::$moduleName", moduleWriter) - return this -} diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt index fda9c65020..f40f580f10 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt @@ -6,7 +6,6 @@ package software.amazon.smithy.rust.codegen.smithy import software.amazon.smithy.build.PluginContext -import software.amazon.smithy.codegen.core.writer.CodegenWriterDelegator import software.amazon.smithy.model.Model import software.amazon.smithy.model.neighbor.Walker import software.amazon.smithy.model.shapes.ServiceShape @@ -16,7 +15,6 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait -import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.smithy.customize.RustCodegenDecorator import software.amazon.smithy.rust.codegen.smithy.generators.CrateVersionGenerator import software.amazon.smithy.rust.codegen.smithy.generators.EnumGenerator @@ -43,7 +41,7 @@ class CodegenVisitor(context: PluginContext, private val codegenDecorator: RustC private val settings = RustSettings.from(context.model, context.settings) private val symbolProvider: RustSymbolProvider - private val writers: CodegenWriterDelegator + private val rustCrate: RustCrate private val fileManifest = context.fileManifest private val model: Model private val protocolConfig: ProtocolConfig @@ -68,10 +66,10 @@ class CodegenVisitor(context: PluginContext, private val codegenDecorator: RustC protocolConfig = ProtocolConfig(model, symbolProvider, settings.runtimeConfig, service, protocol, settings.moduleName) - writers = CodegenWriterDelegator( + rustCrate = RustCrate( context.fileManifest, symbolProvider, - RustWriter.Factory + DefaultPublicModules ) httpGenerator = protocolGenerator.buildProtocolGenerator(protocolConfig) } @@ -84,7 +82,7 @@ class CodegenVisitor(context: PluginContext, private val codegenDecorator: RustC val serviceShapes = Walker(model).walkShapes(service) serviceShapes.forEach { it.accept(this) } // TODO: if we end up with a lot of these on-by-default customizations, we may want to refactor them somewhere - writers.finalize( + rustCrate.finalize( settings, codegenDecorator.libRsCustomizations( protocolConfig, @@ -104,7 +102,7 @@ class CodegenVisitor(context: PluginContext, private val codegenDecorator: RustC override fun structureShape(shape: StructureShape) { logger.fine("generating a structure...") - writers.useShapeWriter(shape) { writer -> + rustCrate.useShapeWriter(shape) { writer -> StructureGenerator(model, symbolProvider, writer, shape).render() if (!shape.hasTrait(SyntheticInputTrait::class.java)) { val builderGenerator = ModelBuilderGenerator(protocolConfig.model, protocolConfig.symbolProvider, shape) @@ -118,19 +116,19 @@ class CodegenVisitor(context: PluginContext, private val codegenDecorator: RustC override fun stringShape(shape: StringShape) { shape.getTrait(EnumTrait::class.java).map { enum -> - writers.useShapeWriter(shape) { writer -> + rustCrate.useShapeWriter(shape) { writer -> EnumGenerator(symbolProvider, writer, shape, enum).render() } } } override fun unionShape(shape: UnionShape) { - writers.useShapeWriter(shape) { + rustCrate.useShapeWriter(shape) { UnionGenerator(model, symbolProvider, it, shape).render() } } override fun serviceShape(shape: ServiceShape) { - ServiceGenerator(writers, httpGenerator, protocolGenerator.support(), protocolConfig, codegenDecorator).render() + ServiceGenerator(rustCrate, httpGenerator, protocolGenerator.support(), protocolConfig, codegenDecorator).render() } } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolMetadataProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolMetadataProvider.kt index c9903cf44f..f0657e1893 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolMetadataProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolMetadataProvider.kt @@ -13,8 +13,8 @@ import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.Attribute.Companion.NonExhaustive -import software.amazon.smithy.rust.codegen.rustlang.Derives import software.amazon.smithy.rust.codegen.rustlang.RustMetadata /** @@ -62,7 +62,7 @@ abstract class SymbolMetadataProvider(private val base: RustSymbolProvider) : Wr class BaseSymbolMetadataProvider(base: RustSymbolProvider) : SymbolMetadataProvider(base) { private val containerDefault = RustMetadata( - Derives(defaultDerives.toSet()), + Attribute.Derives(defaultDerives.toSet()), additionalAttributes = listOf(NonExhaustive), public = true ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt index c2c2ab92a2..94b16bda01 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CargoTomlGenerator.kt @@ -8,15 +8,21 @@ package software.amazon.smithy.rust.codegen.smithy.generators import com.moandjiezana.toml.TomlWriter import software.amazon.smithy.rust.codegen.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.rustlang.DependencyScope +import software.amazon.smithy.rust.codegen.rustlang.Feature import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.utils.CodeWriter class CargoTomlGenerator( private val settings: RustSettings, private val writer: CodeWriter, - private val dependencies: List + private val dependencies: List, + private val features: List ) { fun render() { + val cargoFeatures = features.map { it.name to it.deps }.toMutableList() + if (features.isNotEmpty()) { + cargoFeatures.add("default" to features.filter { it.default }.map { it.name }) + } val cargoToml = mapOf( "package" to mapOf( "name" to settings.moduleName, @@ -28,7 +34,8 @@ class CargoTomlGenerator( "dependencies" to dependencies.filter { it.scope == DependencyScope.Compile }.map { it.name to it.toMap() } .toMap(), "dev-dependencies" to dependencies.filter { it.scope == DependencyScope.Dev }.map { it.name to it.toMap() } - .toMap() + .toMap(), + "features" to cargoFeatures.toMap() ) writer.writeWithNoFormatting(TomlWriter().write(cargoToml)) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CombinedErrorGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CombinedErrorGenerator.kt index 3eea5dc251..82f74c8c72 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CombinedErrorGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/CombinedErrorGenerator.kt @@ -13,7 +13,6 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.traits.RetryableTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute -import software.amazon.smithy.rust.codegen.rustlang.Derives import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.Writable @@ -57,7 +56,7 @@ class CombinedErrorGenerator( val errors = operationIndex.getErrors(operation) val symbol = operation.errorSymbol(symbolProvider) val meta = RustMetadata( - derives = Derives(setOf(RuntimeType.Debug)), + derives = Attribute.Derives(setOf(RuntimeType.Debug)), additionalAttributes = listOf(Attribute.NonExhaustive), public = true ) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/HttpProtocolGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/HttpProtocolGenerator.kt index ebd566d9d9..f7cda121e6 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/HttpProtocolGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/HttpProtocolGenerator.kt @@ -10,7 +10,7 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.rustlang.Derives +import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.documentShape import software.amazon.smithy.rust.codegen.rustlang.rustBlock @@ -80,7 +80,7 @@ abstract class HttpProtocolGenerator( } val operationName = symbolProvider.toSymbol(operationShape).name operationWriter.documentShape(operationShape, model) - Derives(setOf(RuntimeType.Clone)).render(operationWriter) + Attribute.Derives(setOf(RuntimeType.Clone)).render(operationWriter) operationWriter.rustBlock("pub struct $operationName") { write("input: #T", inputSymbol) } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/HttpProtocolTestGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/HttpProtocolTestGenerator.kt index df0a6d69dc..39617efd1e 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/HttpProtocolTestGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/HttpProtocolTestGenerator.kt @@ -16,7 +16,7 @@ import software.amazon.smithy.protocoltests.traits.HttpRequestTestCase import software.amazon.smithy.protocoltests.traits.HttpRequestTestsTrait import software.amazon.smithy.protocoltests.traits.HttpResponseTestCase import software.amazon.smithy.protocoltests.traits.HttpResponseTestsTrait -import software.amazon.smithy.rust.codegen.rustlang.Custom +import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.escape @@ -82,8 +82,8 @@ class HttpProtocolTestGenerator( val moduleMeta = RustMetadata( public = false, additionalAttributes = listOf( - Custom("cfg(test)"), - Custom("allow(unreachable_code, unused_variables)") + Attribute.Cfg("test"), + Attribute.Custom("allow(unreachable_code, unused_variables)") ) ) writer.withModule(testModuleName, moduleMeta) { diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/ServiceGenerator.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/ServiceGenerator.kt index 5c26275f23..98cc455845 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/ServiceGenerator.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/ServiceGenerator.kt @@ -5,11 +5,11 @@ package software.amazon.smithy.rust.codegen.smithy.generators -import software.amazon.smithy.codegen.core.writer.CodegenWriterDelegator 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.RustWriter +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.traits.SyntheticInputTrait @@ -17,7 +17,7 @@ import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticOutputTrait import software.amazon.smithy.rust.codegen.util.inputShape class ServiceGenerator( - private val writers: CodegenWriterDelegator, + private val rustCrate: RustCrate, private val protocolGenerator: HttpProtocolGenerator, private val protocolSupport: ProtocolSupport, private val config: ProtocolConfig, @@ -28,8 +28,8 @@ class ServiceGenerator( fun render() { val operations = index.getContainedOperations(config.serviceShape).sortedBy { it.id } operations.map { operation -> - writers.useShapeWriter(operation) { operationWriter -> - writers.useShapeWriter(operation.inputShape(config.model)) { inputWriter -> + rustCrate.useShapeWriter(operation) { operationWriter -> + rustCrate.useShapeWriter(operation.inputShape(config.model)) { inputWriter -> protocolGenerator.renderOperation( operationWriter, inputWriter, @@ -39,20 +39,20 @@ class ServiceGenerator( HttpProtocolTestGenerator(config, protocolSupport, operation, operationWriter).render() } } - val sym = operation.errorSymbol(config.symbolProvider) - writers.useFileWriter("src/error.rs", sym.namespace) { writer -> + rustCrate.withModule(RustModule.Error) { writer -> CombinedErrorGenerator(config.model, config.symbolProvider, operation).render(writer) } } renderBodies(operations) - writers.useFileWriter("src/config.rs", "crate::config") { writer -> + rustCrate.withModule(RustModule.Config) { writer -> ServiceConfigGenerator.withBaseBehavior( config, extraCustomizations = decorator.configCustomizations(config, listOf()) ).render(writer) } - writers.useFileWriter("src/lib.rs", "crate::lib") { + + rustCrate.lib { it.write("pub use config::Config;") } } @@ -74,7 +74,7 @@ class ServiceGenerator( } (inputBodies + outputBodies).map { body -> // The body symbol controls its location, usually in the serializer module - writers.useShapeWriter(body) { writer -> + rustCrate.useShapeWriter(body) { writer -> with(config) { // Generate a body via the structure generator StructureGenerator(model, symbolProvider, writer, body).render() diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/JsonSerializerSymbolProvider.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/JsonSerializerSymbolProvider.kt index d945ac3d06..8ce0513176 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/JsonSerializerSymbolProvider.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/JsonSerializerSymbolProvider.kt @@ -14,7 +14,6 @@ import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.JsonNameTrait import software.amazon.smithy.model.traits.TimestampFormatTrait import software.amazon.smithy.rust.codegen.rustlang.Attribute -import software.amazon.smithy.rust.codegen.rustlang.Custom import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustType import software.amazon.smithy.rust.codegen.rustlang.stripOuter @@ -52,24 +51,24 @@ class JsonSerializerSymbolProvider( val serdeConfig = serdeRequired(model.expectShape(memberShape.container)) val attribs = mutableListOf() if (serdeConfig.serialize || serdeConfig.deserialize) { - attribs.add(Custom("serde(rename = ${memberShape.serializedName().dq()})")) + attribs.add(Attribute.Custom("serde(rename = ${memberShape.serializedName().dq()})")) } if (serdeConfig.serialize) { if (base.toSymbol(memberShape).rustType().stripOuter() is RustType.Option) { - attribs.add(Custom("serde(skip_serializing_if = \"Option::is_none\")")) + attribs.add(Attribute.Custom("serde(skip_serializing_if = \"Option::is_none\")")) } serializerBuilder.serializerFor(memberShape)?.also { - attribs.add(Custom("serde(serialize_with = ${it.fullyQualifiedName().dq()})", listOf(it))) + attribs.add(Attribute.Custom("serde(serialize_with = ${it.fullyQualifiedName().dq()})", listOf(it))) } } if (serdeConfig.deserialize) { serializerBuilder.deserializerFor(memberShape)?.also { - attribs.add(Custom("serde(deserialize_with = ${it.fullyQualifiedName().dq()})", listOf(it))) + attribs.add(Attribute.Custom("serde(deserialize_with = ${it.fullyQualifiedName().dq()})", listOf(it))) } if (model.expectShape(memberShape.container) is StructureShape && base.toSymbol(memberShape) .isOptional() ) { - attribs.add(Custom("serde(default)")) + attribs.add(Attribute.Custom("serde(default)")) } } return currentMeta.copy(additionalAttributes = currentMeta.additionalAttributes + attribs) diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/Rust.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/Rust.kt index 4dc3f93ac7..46c18d1d38 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/Rust.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/Rust.kt @@ -10,7 +10,6 @@ import org.intellij.lang.annotations.Language import software.amazon.smithy.build.FileManifest import software.amazon.smithy.build.PluginContext import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.codegen.core.writer.CodegenWriterDelegator import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.Shape @@ -22,6 +21,8 @@ import software.amazon.smithy.rust.codegen.rustlang.raw import software.amazon.smithy.rust.codegen.rustlang.rustBlock import software.amazon.smithy.rust.codegen.smithy.BuildSettings import software.amazon.smithy.rust.codegen.smithy.CodegenConfig +import software.amazon.smithy.rust.codegen.smithy.DefaultPublicModules +import software.amazon.smithy.rust.codegen.smithy.RustCrate import software.amazon.smithy.rust.codegen.smithy.RustSettings import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.SymbolVisitorConfig @@ -143,7 +144,7 @@ fun RustWriter.unitTest( } class TestWriterDelegator(fileManifest: FileManifest, symbolProvider: RustSymbolProvider) : - CodegenWriterDelegator(fileManifest, symbolProvider, RustWriter.Factory) { + RustCrate(fileManifest, symbolProvider, DefaultPublicModules) { val baseDir: Path = fileManifest.baseDir } diff --git a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestConfigCustomization.kt b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestConfigCustomization.kt index 737191e611..f6f0d0c258 100644 --- a/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestConfigCustomization.kt +++ b/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/testutil/TestConfigCustomization.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.testutil +import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.writable @@ -52,7 +53,7 @@ fun stubConfigProject(vararg customization: ConfigCustomization): TestWriterDele val customizations = listOf(stubCustomization("a")) + customization.toList() + stubCustomization("b") val generator = ServiceConfigGenerator(customizations = customizations.toList()) val project = TestWorkspace.testProject() - project.useFileWriter("src/config.rs", "crate::config") { + project.withModule(RustModule.Config) { generator.render(it) it.unitTest( """ diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt index 294599f359..fe28239fb1 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/generators/StructureGeneratorTest.kt @@ -8,7 +8,7 @@ package software.amazon.smithy.rust.codegen.generators import io.kotest.matchers.string.shouldContainInOrder import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.rustlang.Custom +import software.amazon.smithy.rust.codegen.rustlang.Attribute import software.amazon.smithy.rust.codegen.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.rustlang.RustWriter import software.amazon.smithy.rust.codegen.rustlang.docs @@ -170,7 +170,7 @@ class StructureGeneratorTest { .withModule( "model", // By attaching this lint, any missing documentation becomes a compiler erorr - RustMetadata(additionalAttributes = listOf(Custom("deny(missing_docs)")), public = true) + RustMetadata(additionalAttributes = listOf(Attribute.Custom("deny(missing_docs)")), public = true) ) { StructureGenerator(model, provider, this, model.lookup("com.test#Inner")).render() StructureGenerator(model, provider, this, model.lookup("com.test#MyStruct")).render() diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGeneratorTest.kt index cc6d9e4d4e..73067f38c7 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/generators/config/ServiceConfigGeneratorTest.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.smithy.generators.config import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.rustlang.RustModule import software.amazon.smithy.rust.codegen.rustlang.Writable import software.amazon.smithy.rust.codegen.rustlang.rust import software.amazon.smithy.rust.codegen.rustlang.writable @@ -67,7 +68,7 @@ internal class ServiceConfigGeneratorTest { val sut = ServiceConfigGenerator(listOf(ServiceCustomizer())) val symbolProvider = testSymbolProvider("namespace empty".asSmithyModel()) val project = TestWorkspace.testProject(symbolProvider) - project.useFileWriter("src/config.rs", "crate::config") { + project.withModule(RustModule.Config) { sut.render(it) it.unitTest( """ diff --git a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/CustomSerializerGeneratorTest.kt b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/CustomSerializerGeneratorTest.kt index e47e6cf950..acba640e72 100644 --- a/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/CustomSerializerGeneratorTest.kt +++ b/codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/CustomSerializerGeneratorTest.kt @@ -74,7 +74,7 @@ internal class CustomSerializerGeneratorTest { private fun checkSymbol(symbol: RuntimeType) { val writer = TestWorkspace.testProject(provider) - writer.useFileWriter("src/lib.rs", "crate::lib") { + writer.lib { it.rust( """ fn foo() {