Skip to content

Commit a01bcf8

Browse files
feat(codegen): correctly generate petstore
1 parent 4214d4a commit a01bcf8

7 files changed

Lines changed: 190 additions & 58 deletions

File tree

codegen/src/main/kotlin/tools/samt/codegen/PublicApi.kt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,70 @@ interface ConsumerUses {
179179
}
180180

181181
interface TypeReference {
182+
/**
183+
* The type this reference points to
184+
*/
182185
val type: Type
186+
187+
/**
188+
* Is true if this type reference is optional, meaning it can be null
189+
*/
183190
val isOptional: Boolean
191+
192+
/**
193+
* The range constraints placed on this type, if any
194+
*/
184195
val rangeConstraint: Constraint.Range?
196+
197+
/**
198+
* The size constraints placed on this type, if any
199+
*/
185200
val sizeConstraint: Constraint.Size?
201+
202+
/**
203+
* The pattern constraints placed on this type, if any
204+
*/
186205
val patternConstraint: Constraint.Pattern?
206+
207+
/**
208+
* The value constraints placed on this type, if any
209+
*/
187210
val valueConstraint: Constraint.Value?
211+
212+
/**
213+
* The runtime type this reference points to, could be different from [type] if this is an alias
214+
*/
215+
val runtimeType: Type
216+
217+
/**
218+
* Is true if this type reference or underlying type is optional, meaning it can be null at runtime
219+
* This is different from [isOptional] in that it will return true for an alias that points to an optional type
220+
*/
221+
val isRuntimeOptional: Boolean
222+
223+
/**
224+
* The runtime range constraints placed on this type, if any.
225+
* Will differ from [rangeConstraint] if this is an alias
226+
*/
227+
val runtimeRangeConstraint: Constraint.Range?
228+
229+
/**
230+
* The runtime size constraints placed on this type, if any.
231+
* Will differ from [sizeConstraint] if this is an alias
232+
*/
233+
val runtimeSizeConstraint: Constraint.Size?
234+
235+
/**
236+
* The runtime pattern constraints placed on this type, if any.
237+
* Will differ from [patternConstraint] if this is an alias
238+
*/
239+
val runtimePatternConstraint: Constraint.Pattern?
240+
241+
/**
242+
* The runtime value constraints placed on this type, if any.
243+
* Will differ from [valueConstraint] if this is an alias
244+
*/
245+
val runtimeValueConstraint: Constraint.Value?
188246
}
189247

190248
interface Constraint {

codegen/src/main/kotlin/tools/samt/codegen/PublicApiMapper.kt

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,16 @@ class PublicApiMapper(
5555
override val name = this@toPublicRequestResponseOperation.name
5656
override val parameters = this@toPublicRequestResponseOperation.parameters.map { it.toPublicParameter() }
5757
override val returnType = this@toPublicRequestResponseOperation.returnType?.toPublicTypeReference()
58-
override val raisesTypes = this@toPublicRequestResponseOperation.raisesTypes.map { it.toPublicTypeReference() }
58+
override val raisesTypes =
59+
this@toPublicRequestResponseOperation.raisesTypes.map { it.toPublicTypeReference() }
5960
override val isAsync = this@toPublicRequestResponseOperation.isAsync
6061
}
6162

62-
private fun tools.samt.semantic.ServiceType.Operation.Parameter.toPublicParameter() = object : ServiceOperationParameter {
63-
override val name = this@toPublicParameter.name
64-
override val type = this@toPublicParameter.type.toPublicTypeReference()
65-
}
63+
private fun tools.samt.semantic.ServiceType.Operation.Parameter.toPublicParameter() =
64+
object : ServiceOperationParameter {
65+
override val name = this@toPublicParameter.name
66+
override val type = this@toPublicParameter.type.toPublicTypeReference()
67+
}
6668

6769
private fun tools.samt.semantic.ProviderType.toPublicProvider() = object : ProviderType {
6870
override val name = this@toPublicProvider.name
@@ -108,6 +110,7 @@ class PublicApiMapper(
108110
return transportConfigurationParser.default()
109111
}
110112
}
113+
111114
else -> controller.reportGlobalError("Multiple transport configuration parsers found for transport '$name'")
112115
}
113116

@@ -142,20 +145,40 @@ class PublicApiMapper(
142145

143146
private fun tools.samt.semantic.TypeReference?.toPublicTypeReference(): TypeReference {
144147
check(this is tools.samt.semantic.ResolvedTypeReference)
148+
val typeReference = this@toPublicTypeReference
149+
val runtimeTypeReference = when (val type = typeReference.type) {
150+
is tools.samt.semantic.AliasType -> checkNotNull(type.fullyResolvedType) { "Found unresolved alias when generating code" }
151+
else -> typeReference
152+
}
145153
return object : TypeReference {
146-
override val type = this@toPublicTypeReference.type.toPublicType()
147-
override val isOptional = this@toPublicTypeReference.isOptional
154+
override val type = typeReference.type.toPublicType()
155+
override val isOptional = typeReference.isOptional
148156
override val rangeConstraint =
149-
this@toPublicTypeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Range>()
157+
typeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Range>()
150158
?.toPublicRangeConstraint()
151159
override val sizeConstraint =
152-
this@toPublicTypeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Size>()
160+
typeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Size>()
153161
?.toPublicSizeConstraint()
154162
override val patternConstraint =
155-
this@toPublicTypeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Pattern>()
163+
typeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Pattern>()
156164
?.toPublicPatternConstraint()
157165
override val valueConstraint =
158-
this@toPublicTypeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Value>()
166+
typeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Value>()
167+
?.toPublicValueConstraint()
168+
169+
override val runtimeType = runtimeTypeReference.type.toPublicType()
170+
override val isRuntimeOptional = isOptional || runtimeTypeReference.isOptional
171+
override val runtimeRangeConstraint = rangeConstraint
172+
?: runtimeTypeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Range>()
173+
?.toPublicRangeConstraint()
174+
override val runtimeSizeConstraint = sizeConstraint
175+
?: runtimeTypeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Size>()
176+
?.toPublicSizeConstraint()
177+
override val runtimePatternConstraint = patternConstraint
178+
?: runtimeTypeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Pattern>()
179+
?.toPublicPatternConstraint()
180+
override val runtimeValueConstraint = valueConstraint
181+
?: runtimeTypeReference.constraints.findConstraint<tools.samt.semantic.ResolvedTypeReference.Constraint.Value>()
159182
?.toPublicValueConstraint()
160183
}
161184
}
@@ -191,24 +214,27 @@ class PublicApiMapper(
191214
tools.samt.semantic.UnknownType -> error("Unknown type cannot be converted to public API")
192215
}
193216

194-
private fun tools.samt.semantic.ResolvedTypeReference.Constraint.Range.toPublicRangeConstraint() = object : Constraint.Range {
195-
override val lowerBound = this@toPublicRangeConstraint.lowerBound
196-
override val upperBound = this@toPublicRangeConstraint.upperBound
197-
}
217+
private fun tools.samt.semantic.ResolvedTypeReference.Constraint.Range.toPublicRangeConstraint() =
218+
object : Constraint.Range {
219+
override val lowerBound = this@toPublicRangeConstraint.lowerBound
220+
override val upperBound = this@toPublicRangeConstraint.upperBound
221+
}
198222

199-
private fun tools.samt.semantic.ResolvedTypeReference.Constraint.Size.toPublicSizeConstraint() = object : Constraint.Size {
200-
override val lowerBound = this@toPublicSizeConstraint.lowerBound
201-
override val upperBound = this@toPublicSizeConstraint.upperBound
202-
}
223+
private fun tools.samt.semantic.ResolvedTypeReference.Constraint.Size.toPublicSizeConstraint() =
224+
object : Constraint.Size {
225+
override val lowerBound = this@toPublicSizeConstraint.lowerBound
226+
override val upperBound = this@toPublicSizeConstraint.upperBound
227+
}
203228

204229
private fun tools.samt.semantic.ResolvedTypeReference.Constraint.Pattern.toPublicPatternConstraint() =
205230
object : Constraint.Pattern {
206231
override val pattern = this@toPublicPatternConstraint.pattern
207232
}
208233

209-
private fun tools.samt.semantic.ResolvedTypeReference.Constraint.Value.toPublicValueConstraint() = object : Constraint.Value {
210-
override val value = this@toPublicValueConstraint.value
211-
}
234+
private fun tools.samt.semantic.ResolvedTypeReference.Constraint.Value.toPublicValueConstraint() =
235+
object : Constraint.Value {
236+
override val value = this@toPublicValueConstraint.value
237+
}
212238

213239
private fun tools.samt.semantic.UserDeclaredNamedType.getQualifiedName(): String {
214240
val components = parentPackage.nameComponents + name

codegen/src/main/kotlin/tools/samt/codegen/kotlin/KotlinGeneratorUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ object KotlinGeneratorConfig {
88
}
99

1010
val GeneratedFilePreamble = """
11-
@file:Suppress("RemoveRedundantQualifierName", "unused", "UnusedImport", "LocalVariableName", "FunctionName", "ConvertTwoComparisonsToRangeCheck", "ReplaceSizeCheckWithIsNotEmpty", "NAME_SHADOWING", "UNUSED_VARIABLE")
11+
@file:Suppress("RemoveRedundantQualifierName", "unused", "UnusedImport", "LocalVariableName", "FunctionName", "ConvertTwoComparisonsToRangeCheck", "ReplaceSizeCheckWithIsNotEmpty", "NAME_SHADOWING", "UNUSED_VARIABLE", "NestedLambdaShadowedImplicitParameter", "KotlinRedundantDiagnosticSuppress")
1212
1313
/*
1414
* This file is generated by SAMT, manual changes will be overwritten.

codegen/src/main/kotlin/tools/samt/codegen/kotlin/KotlinTypesGenerator.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,17 @@ object KotlinTypesGenerator : Generator {
4747
}
4848

4949
private fun StringBuilder.appendRecord(record: RecordType, options: Map<String, String>) {
50+
if (record.fields.isEmpty()) {
51+
appendLine("class ${record.name}")
52+
appendLine()
53+
return
54+
}
55+
5056
appendLine("data class ${record.name}(")
5157
record.fields.forEach { field ->
5258
val fullyQualifiedName = field.type.getQualifiedName(options)
53-
val isOptional = field.type.isOptional
5459

55-
if (isOptional) {
60+
if (field.type.isRuntimeOptional) {
5661
appendLine(" val ${field.name}: $fullyQualifiedName = null,")
5762
} else {
5863
appendLine(" val ${field.name}: $fullyQualifiedName,")
@@ -116,9 +121,8 @@ object KotlinTypesGenerator : Generator {
116121
private fun StringBuilder.appendServiceOperationParameterList(parameters: List<ServiceOperationParameter>, options: Map<String, String>) {
117122
parameters.forEach { parameter ->
118123
val fullyQualifiedName = parameter.type.getQualifiedName(options)
119-
val isOptional = parameter.type.isOptional
120124

121-
if (isOptional) {
125+
if (parameter.type.isRuntimeOptional) {
122126
appendLine(" ${parameter.name}: $fullyQualifiedName = null,")
123127
} else {
124128
appendLine(" ${parameter.name}: $fullyQualifiedName,")

codegen/src/main/kotlin/tools/samt/codegen/kotlin/ktor/KotlinKtorConsumerGenerator.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ object KotlinKtorConsumerGenerator : Generator {
121121
appendLine(" }")
122122
}
123123
}
124+
appendLine()
124125
}
125126

126127
info.notImplementedOperations.forEach { operation ->
@@ -195,7 +196,7 @@ object KotlinKtorConsumerGenerator : Generator {
195196

196197
appendLine(" // Encode query parameters")
197198
queryParameters.forEach { (name, queryParameter) ->
198-
if (queryParameter.type.isOptional) {
199+
if (queryParameter.type.isRuntimeOptional) {
199200
appendLine(" if ($name != null) {")
200201
appendLine(" this.parameters.append(\"$name\", ${encodeJsonElement(queryParameter.type, options, valueName = name)}.toString())")
201202
appendLine(" }")
@@ -216,7 +217,7 @@ object KotlinKtorConsumerGenerator : Generator {
216217

217218
// header parameters
218219
headerParameters.forEach { (name, headerParameter) ->
219-
if (headerParameter.type.isOptional) {
220+
if (headerParameter.type.isRuntimeOptional) {
220221
appendLine(" if ($name != null) {")
221222
appendLine(" header(\"${name}\", ${encodeJsonElement(headerParameter.type, options, valueName = name)})")
222223
appendLine(" }")
@@ -227,7 +228,7 @@ object KotlinKtorConsumerGenerator : Generator {
227228

228229
// cookie parameters
229230
cookieParameters.forEach { (name, cookieParameter) ->
230-
if (cookieParameter.type.isOptional) {
231+
if (cookieParameter.type.isRuntimeOptional) {
231232
appendLine(" if ($name != null) {")
232233
appendLine(" cookie(\"${name}\", ${encodeJsonElement(cookieParameter.type, options, valueName = name)}.toString())")
233234
appendLine(" }")

0 commit comments

Comments
 (0)