Skip to content

Commit 7da7822

Browse files
authored
feat: add support for smithy document type (#671)
1 parent f4b2cf5 commit 7da7822

File tree

17 files changed

+245
-74
lines changed

17 files changed

+245
-74
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": "fc4060e3-a826-46dd-a1f7-8fb041c77fd2",
3+
"type": "feature",
4+
"description": "Add support for smithy Document type.",
5+
"issues": [
6+
"awslabs/smithy-kotlin#123"
7+
]
8+
}

runtime/serde/common/src/aws/smithy/kotlin/runtime/serde/Serializer.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,11 @@ interface PrimitiveSerializer {
414414
* Serializes the given value.
415415
*/
416416
fun serializeNull()
417+
418+
/**
419+
* Serializes the given value.
420+
*/
421+
fun serializeDocument(value: Document?)
417422
}
418423

419424
/**

runtime/serde/serde-form-url/common/src/aws/smithy/kotlin/runtime/serde/formurl/FormUrlSerializer.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ private class FormUrlSerializer(
6161
override fun serializeNull() {
6262
throw SerializationException("null values not supported by form-url serializer")
6363
}
64+
65+
override fun serializeDocument(value: Document?) {
66+
throw SerializationException("document values not supported by form-url serializer")
67+
}
6468
}
6569

6670
private class FormUrlStructSerializer(
@@ -212,6 +216,9 @@ private class FormUrlListSerializer(
212216
}
213217

214218
override fun serializeNull() {}
219+
override fun serializeDocument(value: Document?) {
220+
throw SerializationException("document values not supported by form-url serializer")
221+
}
215222
}
216223

217224
private class FormUrlMapSerializer(

runtime/serde/serde-json/common/src/aws/smithy/kotlin/runtime/serde/json/JsonSerializer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ class JsonSerializer : Serializer, ListSerializer, MapSerializer, StructSerializ
259259
}
260260
}
261261

262-
fun serializeDocument(value: Document?) {
262+
override fun serializeDocument(value: Document?) {
263263
when (value) {
264264
is Document.Number -> jsonWriter.writeValue(value.value)
265265
is Document.String -> jsonWriter.writeValue(value.value)

runtime/serde/serde-xml/common/src/aws/smithy/kotlin/runtime/serde/xml/XmlSerializer.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ class XmlSerializer(private val xmlWriter: XmlStreamWriter = xmlStreamWriter())
149149
// NOP
150150
}
151151

152+
override fun serializeDocument(value: Document?) {
153+
throw SerializationException("document values not supported by xml serializer")
154+
}
155+
152156
override fun serializeBoolean(value: Boolean) { xmlWriter.text(value.toString()) }
153157

154158
override fun serializeByte(value: Byte) = serializeNumber(value)
@@ -286,6 +290,10 @@ private class XmlMapSerializer(
286290
xmlWriter.writeTag(tagName, ns)
287291
}
288292

293+
override fun serializeDocument(value: Document?) {
294+
throw SerializationException("document values not supported by xml serializer")
295+
}
296+
289297
private fun serializePrimitive(value: Any) {
290298
val tagName = descriptor.findTrait<XmlMapName>()?.value ?: XmlMapName.Default.value
291299
val ns = descriptor.findTrait<XmlCollectionValueNamespace>()
@@ -344,6 +352,10 @@ private class XmlListSerializer(
344352
xmlWriter.writeTag(memberTagName, ns)
345353
}
346354

355+
override fun serializeDocument(value: Document?) {
356+
throw SerializationException("document values not supported by xml serializer")
357+
}
358+
347359
override fun serializeInstant(value: Instant, format: TimestampFormat) = serializeString(value.format(format))
348360

349361
private fun serializePrimitive(value: Any) {

smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/CodeWriterExt.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ fun <T : CodeWriter> T.withBlock(
3333
block: T.() -> Unit
3434
): T = wrapBlockIf(true, textBeforeNewLine, textAfterNewLine, *args) { block(this) }
3535

36+
/**
37+
* Like [withBlock], except the closing write of [textAfterNewLine] does NOT include a line break.
38+
*/
39+
fun <T : CodeWriter> T.withInlineBlock(
40+
textBeforeNewLine: String,
41+
textAfterNewLine: String,
42+
vararg args: Any,
43+
block: T.() -> Unit
44+
): T {
45+
openBlock(textBeforeNewLine, *args)
46+
block(this)
47+
closeInlineBlock(textAfterNewLine)
48+
return this
49+
}
50+
3651
/**
3752
* Extension function that is more idiomatic Kotlin that is roughly the same purpose as an if block wrapped around
3853
* the provided function `openBlock(String textBeforeNewline, String textAfterNewline, Runnable r)`
@@ -64,6 +79,16 @@ fun <T : CodeWriter> T.wrapBlockIf(
6479
return this
6580
}
6681

82+
/**
83+
* Like [CodeWriter.closeBlock], except the closing write of [textAfterNewLine] does NOT include a line break.
84+
*/
85+
fun <T : CodeWriter> T.closeInlineBlock(textAfterNewLine: String): T {
86+
writeInline("\n")
87+
dedent()
88+
writeInline(textAfterNewLine)
89+
return this
90+
}
91+
6792
/**
6893
* Extension function that closes the previous block, dedents, opens a new block with [textBeforeNewLine], and indents.
6994
*

smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/core/RuntimeTypes.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ object RuntimeTypes {
122122
val SuccessAcceptor = runtimeSymbol("SuccessAcceptor", KotlinDependency.CORE, "retries.policy")
123123
}
124124
}
125+
126+
object Smithy {
127+
val Document = runtimeSymbol("Document", KotlinDependency.CORE, "smithy")
128+
val buildDocument = runtimeSymbol("buildDocument", KotlinDependency.CORE, "smithy")
129+
}
125130
}
126131

127132
object Hashing {

smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/ShapeValueGenerator.kt

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ class ShapeValueGenerator(
138138
) : NodeVisitor<Unit> {
139139

140140
override fun objectNode(node: ObjectNode) {
141+
if (currShape.type == ShapeType.DOCUMENT) {
142+
writer
143+
.writeInline("#T {\n", RuntimeTypes.Core.Smithy.buildDocument)
144+
.indent()
145+
}
146+
141147
var i = 0
142148
node.members.forEach { (keyNode, valueNode) ->
143149
val memberShape: Shape
@@ -168,7 +174,9 @@ class ShapeValueGenerator(
168174
}
169175
}
170176
is DocumentShape -> {
171-
// TODO - deal with document shapes
177+
writer.writeInline("#S to ", keyNode.value)
178+
generator.writeShapeValueInline(writer, currShape, valueNode)
179+
writer.writeInline("\n")
172180
}
173181
is UnionShape -> {
174182
val member = currShape.getMember(keyNode.value).orElseThrow {
@@ -186,6 +194,12 @@ class ShapeValueGenerator(
186194
}
187195
i++
188196
}
197+
198+
if (currShape.type === ShapeType.DOCUMENT) {
199+
writer
200+
.dedent()
201+
.writeInline("}")
202+
}
189203
}
190204

191205
override fun stringNode(node: StringNode) {
@@ -202,6 +216,8 @@ class ShapeValueGenerator(
202216
writer.writeInline("#L", "$symbolName.$symbolMember")
203217
}
204218

219+
ShapeType.DOCUMENT -> writer.writeInline("#T(#S)", RuntimeTypes.Core.Smithy.Document, node.value)
220+
205221
else -> writer.writeInline("#S", node.value)
206222
}
207223
}
@@ -211,14 +227,29 @@ class ShapeValueGenerator(
211227
}
212228

213229
override fun arrayNode(node: ArrayNode) {
214-
val memberShape = generator.model.expectShape((currShape as CollectionShape).member.target)
215-
var i = 0
216-
node.elements.forEach { element ->
217-
generator.writeShapeValueInline(writer, memberShape, element)
218-
if (i < node.elements.size - 1) {
219-
writer.writeInline(",\n")
230+
when (currShape.type) {
231+
ShapeType.DOCUMENT -> {
232+
writer.withInlineBlock("#T(", ")", RuntimeTypes.Core.Smithy.Document) {
233+
writer.withInlineBlock("listOf(", ")") {
234+
node.elements.forEach {
235+
generator.writeShapeValueInline(writer, currShape, it)
236+
writer.writeInline(",\n")
237+
}
238+
}
239+
}
240+
}
241+
242+
else -> {
243+
val memberShape = generator.model.expectShape((currShape as CollectionShape).member.target)
244+
var i = 0
245+
node.elements.forEach { element ->
246+
generator.writeShapeValueInline(writer, memberShape, element)
247+
if (i < node.elements.size - 1) {
248+
writer.writeInline(",\n")
249+
}
250+
i++
251+
}
220252
}
221-
i++
222253
}
223254
}
224255

@@ -242,19 +273,20 @@ class ShapeValueGenerator(
242273
// TODO - We need to decide non-JVM only symbols to generate for these before we know how to assign values to them
243274
}
244275

245-
ShapeType.DOCUMENT -> {
246-
// TODO - deal with document shapes
247-
}
276+
ShapeType.DOCUMENT -> writer.writeInline(
277+
"#T(#L#L)",
278+
RuntimeTypes.Core.Smithy.Document,
279+
node.value,
280+
if (node.isFloatingPointNumber) "F" else "L"
281+
)
248282

249283
else -> throw CodegenException("unexpected shape type $currShape for numberNode")
250284
}
251285
}
252286

253287
override fun booleanNode(node: BooleanNode) {
254288
when (currShape.type) {
255-
ShapeType.DOCUMENT -> {
256-
// TODO - deal with document shapes
257-
}
289+
ShapeType.DOCUMENT -> writer.writeInline("#T(#L)", RuntimeTypes.Core.Smithy.Document, node.value)
258290
ShapeType.BOOLEAN -> writer.writeInline("#L", if (node.value) "true" else "false")
259291
else -> throw CodegenException("unexpected shape type $currShape for boolean value")
260292
}

smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/protocol/HttpBindingProtocolGenerator.kt

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -447,15 +447,12 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator {
447447
}
448448
writer.write("builder.body = #T(input.#L.#T())", RuntimeTypes.Http.ByteArrayContent, contents, KotlinTypes.Text.encodeToByteArray)
449449
}
450-
ShapeType.STRUCTURE, ShapeType.UNION -> {
450+
ShapeType.STRUCTURE, ShapeType.UNION, ShapeType.DOCUMENT -> {
451451
val sdg = structuredDataSerializer(ctx)
452452
val payloadSerializerFn = sdg.payloadSerializer(ctx, binding.member)
453453
writer.write("val payload = #T(input.#L)", payloadSerializerFn, memberName)
454454
writer.write("builder.body = #T(payload)", RuntimeTypes.Http.ByteArrayContent)
455455
}
456-
ShapeType.DOCUMENT -> {
457-
// TODO - deal with document members
458-
}
459456
else -> throw CodegenException("member shape ${binding.member} serializer not implemented yet")
460457
}
461458
writer.closeBlock("}")
@@ -897,7 +894,7 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator {
897894
}
898895
writer.write("builder.$memberName = response.body.$conversion")
899896
}
900-
ShapeType.STRUCTURE, ShapeType.UNION -> {
897+
ShapeType.STRUCTURE, ShapeType.UNION, ShapeType.DOCUMENT -> {
901898
// delegate to the payload deserializer
902899
val sdg = structuredDataParser(ctx)
903900
val payloadDeserializerFn = sdg.payloadDeserializer(ctx, binding.member)
@@ -907,9 +904,6 @@ abstract class HttpBindingProtocolGenerator : ProtocolGenerator {
907904
write("builder.#L = #T(payload)", memberName, payloadDeserializerFn)
908905
}
909906
}
910-
ShapeType.DOCUMENT -> {
911-
// TODO - implement document support
912-
}
913907
else -> throw CodegenException("member shape ${binding.member} deserializer not implemented")
914908
}
915909

smithy-kotlin-codegen/src/main/kotlin/software/amazon/smithy/kotlin/codegen/rendering/serde/DeserializeStructGenerator.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ open class DeserializeStructGenerator(
9090
ShapeType.MAP -> renderMapMemberDeserializer(memberShape, targetShape as MapShape)
9191
ShapeType.STRUCTURE,
9292
ShapeType.UNION -> renderShapeDeserializer(memberShape)
93-
ShapeType.DOCUMENT -> renderDocumentShapeDeserializer(memberShape)
9493
ShapeType.BLOB,
9594
ShapeType.BOOLEAN,
9695
ShapeType.STRING,
@@ -101,17 +100,13 @@ open class DeserializeStructGenerator(
101100
ShapeType.LONG,
102101
ShapeType.FLOAT,
103102
ShapeType.DOUBLE,
103+
ShapeType.DOCUMENT,
104104
ShapeType.BIG_DECIMAL,
105105
ShapeType.BIG_INTEGER -> renderShapeDeserializer(memberShape)
106106
else -> error("Unexpected shape type: ${targetShape.type}")
107107
}
108108
}
109109

110-
// TODO ~ Not yet implemented
111-
@Suppress("UNUSED_PARAMETER") // Until method is implemented
112-
protected fun renderDocumentShapeDeserializer(memberShape: MemberShape) {
113-
}
114-
115110
/**
116111
* Codegen the deserialization of a primitive value into a response type. Example:
117112
* ```
@@ -179,6 +174,7 @@ open class DeserializeStructGenerator(
179174
ShapeType.BIG_DECIMAL,
180175
ShapeType.BIG_INTEGER,
181176
ShapeType.BLOB,
177+
ShapeType.DOCUMENT,
182178
ShapeType.TIMESTAMP -> renderEntry(elementShape, nestingLevel, isSparse, parentMemberName)
183179
ShapeType.SET,
184180
ShapeType.LIST -> renderListEntry(rootMemberShape, elementShape as CollectionShape, nestingLevel, isSparse, parentMemberName)
@@ -380,6 +376,7 @@ open class DeserializeStructGenerator(
380376
ShapeType.BIG_DECIMAL,
381377
ShapeType.BIG_INTEGER,
382378
ShapeType.BLOB,
379+
ShapeType.DOCUMENT,
383380
ShapeType.TIMESTAMP -> renderElement(elementShape, nestingLevel, isSparse, parentMemberName)
384381
ShapeType.LIST,
385382
ShapeType.SET -> renderListElement(rootMemberShape, elementShape as CollectionShape, nestingLevel, parentMemberName)
@@ -513,6 +510,7 @@ open class DeserializeStructGenerator(
513510
ShapeType.LONG -> "deserializeLong()"
514511
ShapeType.FLOAT -> "deserializeFloat()"
515512
ShapeType.DOUBLE -> "deserializeDouble()"
513+
ShapeType.DOCUMENT -> "deserializeDocument()"
516514
ShapeType.BLOB -> {
517515
writer.addImport("decodeBase64Bytes", KotlinDependency.UTILS)
518516
"deserializeString().decodeBase64Bytes()"

0 commit comments

Comments
 (0)