Loading codegen-test/build.gradle.kts +8 −1 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ dependencies { implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") } data class CodegenTest(val service: String, val module: String) data class CodegenTest(val service: String, val module: String, val extraConfig: String? = null) val CodgenTests = listOf( CodegenTest("com.amazonaws.dynamodb#DynamoDB_20120810", "dynamo"), Loading @@ -35,6 +35,12 @@ val CodgenTests = listOf( CodegenTest( "aws.protocoltests.restjson#RestJson", "rest_json" ), CodegenTest( "crate#Config", "naming_test", """ , "codegen": { "renameErrors": false } """.trimIndent() ) ) Loading @@ -53,6 +59,7 @@ fun generateSmithyBuild(tests: List<CodegenTest>): String { "build": { "rootProject": true } ${it.extraConfig ?: ""} } } } Loading codegen-test/model/naming-obstacle-course.smithy 0 → 100644 +97 −0 Original line number Diff line number Diff line $version: "1.0" namespace crate use smithy.test#httpRequestTests use smithy.test#httpResponseTests use aws.protocols#awsJson1_1 /// Confounds model generation machinery with lots of problematic names @awsJson1_1 service Config { version: "2006-03-01", operations: [ ReservedWordsAsMembers, StructureNamePunning, ErrCollisions ] } @httpRequestTests([ { id: "reserved_words", protocol: awsJson1_1, params: { "as": 5, "async": true, }, method: "POST", uri: "/", body: "{\"as\": 5, \"async\": true}", bodyMediaType: "application/json" } ]) operation ReservedWordsAsMembers { input: ReservedWords } structure ReservedWords { as: Integer, async: Boolean } @httpRequestTests([ { id: "structure_punning", protocol: awsJson1_1, params: { "regular_string": "hello!", "punned_string": { "ps_member": true }, }, method: "POST", uri: "/", body: "{\"regular_string\": \"hello!\", \"punned_string\": { \"ps_member\": true }}", bodyMediaType: "application/json" } ]) operation StructureNamePunning { input: StructureNamePunningInput } structure StructureNamePunningInput { regular_string: smithy.api#String, punned_string: crate#String, punned_vec: Vec } structure Vec { pv_member: Boolean } structure String { ps_member: Boolean } operation ErrCollisions { errors: [ CollidingError, CollidingException // , ErrCollisionsException ] } @error("client") structure CollidingError { } /// This will be renamed to CollidingError @error("client") structure CollidingException { } // This will conflict with the name of the top-level error declaration // Fixing this is more refactoring than I want to get into right now // @error("client") // structure ErrCollisionsException { } codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/lang/RustReservedWords.kt 0 → 100644 +82 −0 Original line number Diff line number Diff line package software.amazon.smithy.rust.codegen.lang import software.amazon.smithy.codegen.core.ReservedWordSymbolProvider import software.amazon.smithy.codegen.core.ReservedWords import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider class RustReservedWordSymbolProvider(base: RustSymbolProvider) : WrappingSymbolProvider(base) { private val internal = ReservedWordSymbolProvider.builder().symbolProvider(base).memberReservedWords(RustReservedWords).build() override fun toMemberName(shape: MemberShape): String { return internal.toMemberName(shape) } override fun toSymbol(shape: Shape): Symbol { return internal.toSymbol(shape) } } object RustReservedWords : ReservedWords { private val RustKeywords = setOf( "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe", "use", "where", "while", "async", "await", "dyn", "abstract", "become", "box", "do", "final", "macro", "override", "priv", "typeof", "unsized", "virtual", "yield", "try" ) override fun escape(word: String): String = "r##$word" override fun isReserved(word: String): Boolean = RustKeywords.contains(word) } codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/lang/RustTypes.kt +31 −17 Original line number Diff line number Diff line Loading @@ -21,12 +21,15 @@ sealed class RustType { */ abstract val name: kotlin.String open val namespace: kotlin.String? = null object Bool : RustType() { override val name: kotlin.String = "bool" } object String : RustType() { override val name: kotlin.String = "String" override val namespace = "::std::string" } data class Float(val precision: Int) : RustType() { Loading @@ -39,21 +42,23 @@ sealed class RustType { data class Vec(val member: RustType) : RustType() { override val name: kotlin.String = "Vec" override val namespace = "::std::vec" } data class Slice(val member: RustType) : RustType() { override val name: kotlin.String get() = "" override val name: kotlin.String = "" } data class HashMap(val key: RustType, val value: RustType) : RustType() { // TODO: assert that underneath, the member is a String override val name: kotlin.String = "HashMap" override val namespace = "::std::collections" } data class HashSet(val member: RustType) : RustType() { // TODO: assert that underneath, the member is a String override val name: kotlin.String = SetType override val namespace = SetNamespace } data class Reference(val lifetime: kotlin.String?, override val value: RustType) : RustType(), Container { Loading @@ -62,33 +67,42 @@ sealed class RustType { data class Option(override val value: RustType) : RustType(), Container { override val name: kotlin.String = "Option" override val namespace = "::std::option" } data class Box(override val value: RustType) : RustType(), Container { override val name: kotlin.String = "Box" override val namespace = "::std::boxed" } data class Opaque(override val name: kotlin.String) : RustType() data class Opaque(override val name: kotlin.String, override val namespace: kotlin.String? = null) : RustType() companion object { val SetType = "BTreeSet" const val SetType = "BTreeSet" const val SetNamespace = "::std::collections" } } fun RustType.render(): String = when (this) { fun RustType.render(fullyQualified: Boolean): String { val namespace = if (fullyQualified) { this.namespace?.let { "$it::" } ?: "" } else "" val base = when (this) { is RustType.Bool -> this.name is RustType.Float -> this.name is RustType.Integer -> this.name is RustType.String -> this.name is RustType.Vec -> "${this.name}<${this.member.render()}>" is RustType.Slice -> "[${this.member.render()}]" is RustType.HashMap -> "${this.name}<${this.key.render()}, ${this.value.render()}>" is RustType.HashSet -> "${this.name}<${this.member.render()}>" is RustType.Reference -> "&${this.lifetime?.let { "'$it" } ?: ""} ${this.value.render()}" is RustType.Option -> "${this.name}<${this.value.render()}>" is RustType.Box -> "${this.name}<${this.value.render()}>" is RustType.Vec -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Slice -> "[${this.member.render(fullyQualified)}]" is RustType.HashMap -> "${this.name}<${this.key.render(fullyQualified)}, ${this.value.render(fullyQualified)}>" is RustType.HashSet -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Reference -> "&${this.lifetime?.let { "'$it" } ?: ""} ${this.value.render(fullyQualified)}" is RustType.Option -> "${this.name}<${this.value.render(fullyQualified)}>" is RustType.Box -> "${this.name}<${this.value.render(fullyQualified)}>" is RustType.Opaque -> this.name } return "$namespace$base" } /** * Returns true if [this] contains [t] anywhere within it's tree. For example, Loading codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/lang/RustWriter.kt +2 −4 Original line number Diff line number Diff line Loading @@ -269,10 +269,8 @@ class RustWriter private constructor( t.fullyQualifiedName() } is Symbol -> { if (t.namespace != namespace) { addImport(t, null) } t.rustType().render() t.rustType().render(fullyQualified = true) } else -> throw CodegenException("Invalid type provided to RustSymbolFormatter") } Loading Loading
codegen-test/build.gradle.kts +8 −1 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ dependencies { implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") } data class CodegenTest(val service: String, val module: String) data class CodegenTest(val service: String, val module: String, val extraConfig: String? = null) val CodgenTests = listOf( CodegenTest("com.amazonaws.dynamodb#DynamoDB_20120810", "dynamo"), Loading @@ -35,6 +35,12 @@ val CodgenTests = listOf( CodegenTest( "aws.protocoltests.restjson#RestJson", "rest_json" ), CodegenTest( "crate#Config", "naming_test", """ , "codegen": { "renameErrors": false } """.trimIndent() ) ) Loading @@ -53,6 +59,7 @@ fun generateSmithyBuild(tests: List<CodegenTest>): String { "build": { "rootProject": true } ${it.extraConfig ?: ""} } } } Loading
codegen-test/model/naming-obstacle-course.smithy 0 → 100644 +97 −0 Original line number Diff line number Diff line $version: "1.0" namespace crate use smithy.test#httpRequestTests use smithy.test#httpResponseTests use aws.protocols#awsJson1_1 /// Confounds model generation machinery with lots of problematic names @awsJson1_1 service Config { version: "2006-03-01", operations: [ ReservedWordsAsMembers, StructureNamePunning, ErrCollisions ] } @httpRequestTests([ { id: "reserved_words", protocol: awsJson1_1, params: { "as": 5, "async": true, }, method: "POST", uri: "/", body: "{\"as\": 5, \"async\": true}", bodyMediaType: "application/json" } ]) operation ReservedWordsAsMembers { input: ReservedWords } structure ReservedWords { as: Integer, async: Boolean } @httpRequestTests([ { id: "structure_punning", protocol: awsJson1_1, params: { "regular_string": "hello!", "punned_string": { "ps_member": true }, }, method: "POST", uri: "/", body: "{\"regular_string\": \"hello!\", \"punned_string\": { \"ps_member\": true }}", bodyMediaType: "application/json" } ]) operation StructureNamePunning { input: StructureNamePunningInput } structure StructureNamePunningInput { regular_string: smithy.api#String, punned_string: crate#String, punned_vec: Vec } structure Vec { pv_member: Boolean } structure String { ps_member: Boolean } operation ErrCollisions { errors: [ CollidingError, CollidingException // , ErrCollisionsException ] } @error("client") structure CollidingError { } /// This will be renamed to CollidingError @error("client") structure CollidingException { } // This will conflict with the name of the top-level error declaration // Fixing this is more refactoring than I want to get into right now // @error("client") // structure ErrCollisionsException { }
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/lang/RustReservedWords.kt 0 → 100644 +82 −0 Original line number Diff line number Diff line package software.amazon.smithy.rust.codegen.lang import software.amazon.smithy.codegen.core.ReservedWordSymbolProvider import software.amazon.smithy.codegen.core.ReservedWords import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.rust.codegen.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.smithy.WrappingSymbolProvider class RustReservedWordSymbolProvider(base: RustSymbolProvider) : WrappingSymbolProvider(base) { private val internal = ReservedWordSymbolProvider.builder().symbolProvider(base).memberReservedWords(RustReservedWords).build() override fun toMemberName(shape: MemberShape): String { return internal.toMemberName(shape) } override fun toSymbol(shape: Shape): Symbol { return internal.toSymbol(shape) } } object RustReservedWords : ReservedWords { private val RustKeywords = setOf( "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super", "trait", "true", "type", "unsafe", "use", "where", "while", "async", "await", "dyn", "abstract", "become", "box", "do", "final", "macro", "override", "priv", "typeof", "unsized", "virtual", "yield", "try" ) override fun escape(word: String): String = "r##$word" override fun isReserved(word: String): Boolean = RustKeywords.contains(word) }
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/lang/RustTypes.kt +31 −17 Original line number Diff line number Diff line Loading @@ -21,12 +21,15 @@ sealed class RustType { */ abstract val name: kotlin.String open val namespace: kotlin.String? = null object Bool : RustType() { override val name: kotlin.String = "bool" } object String : RustType() { override val name: kotlin.String = "String" override val namespace = "::std::string" } data class Float(val precision: Int) : RustType() { Loading @@ -39,21 +42,23 @@ sealed class RustType { data class Vec(val member: RustType) : RustType() { override val name: kotlin.String = "Vec" override val namespace = "::std::vec" } data class Slice(val member: RustType) : RustType() { override val name: kotlin.String get() = "" override val name: kotlin.String = "" } data class HashMap(val key: RustType, val value: RustType) : RustType() { // TODO: assert that underneath, the member is a String override val name: kotlin.String = "HashMap" override val namespace = "::std::collections" } data class HashSet(val member: RustType) : RustType() { // TODO: assert that underneath, the member is a String override val name: kotlin.String = SetType override val namespace = SetNamespace } data class Reference(val lifetime: kotlin.String?, override val value: RustType) : RustType(), Container { Loading @@ -62,33 +67,42 @@ sealed class RustType { data class Option(override val value: RustType) : RustType(), Container { override val name: kotlin.String = "Option" override val namespace = "::std::option" } data class Box(override val value: RustType) : RustType(), Container { override val name: kotlin.String = "Box" override val namespace = "::std::boxed" } data class Opaque(override val name: kotlin.String) : RustType() data class Opaque(override val name: kotlin.String, override val namespace: kotlin.String? = null) : RustType() companion object { val SetType = "BTreeSet" const val SetType = "BTreeSet" const val SetNamespace = "::std::collections" } } fun RustType.render(): String = when (this) { fun RustType.render(fullyQualified: Boolean): String { val namespace = if (fullyQualified) { this.namespace?.let { "$it::" } ?: "" } else "" val base = when (this) { is RustType.Bool -> this.name is RustType.Float -> this.name is RustType.Integer -> this.name is RustType.String -> this.name is RustType.Vec -> "${this.name}<${this.member.render()}>" is RustType.Slice -> "[${this.member.render()}]" is RustType.HashMap -> "${this.name}<${this.key.render()}, ${this.value.render()}>" is RustType.HashSet -> "${this.name}<${this.member.render()}>" is RustType.Reference -> "&${this.lifetime?.let { "'$it" } ?: ""} ${this.value.render()}" is RustType.Option -> "${this.name}<${this.value.render()}>" is RustType.Box -> "${this.name}<${this.value.render()}>" is RustType.Vec -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Slice -> "[${this.member.render(fullyQualified)}]" is RustType.HashMap -> "${this.name}<${this.key.render(fullyQualified)}, ${this.value.render(fullyQualified)}>" is RustType.HashSet -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Reference -> "&${this.lifetime?.let { "'$it" } ?: ""} ${this.value.render(fullyQualified)}" is RustType.Option -> "${this.name}<${this.value.render(fullyQualified)}>" is RustType.Box -> "${this.name}<${this.value.render(fullyQualified)}>" is RustType.Opaque -> this.name } return "$namespace$base" } /** * Returns true if [this] contains [t] anywhere within it's tree. For example, Loading
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/lang/RustWriter.kt +2 −4 Original line number Diff line number Diff line Loading @@ -269,10 +269,8 @@ class RustWriter private constructor( t.fullyQualifiedName() } is Symbol -> { if (t.namespace != namespace) { addImport(t, null) } t.rustType().render() t.rustType().render(fullyQualified = true) } else -> throw CodegenException("Invalid type provided to RustSymbolFormatter") } Loading