All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.amazon.smithy.kotlin.codegen.rendering.serde.SerializeUnionGenerator.kt Maven / Gradle / Ivy

/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */
package software.amazon.smithy.kotlin.codegen.rendering.serde

import software.amazon.smithy.kotlin.codegen.core.KotlinWriter
import software.amazon.smithy.kotlin.codegen.core.RuntimeTypes
import software.amazon.smithy.kotlin.codegen.core.unionVariantName
import software.amazon.smithy.kotlin.codegen.core.withBlock
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
import software.amazon.smithy.model.shapes.CollectionShape
import software.amazon.smithy.model.shapes.MapShape
import software.amazon.smithy.model.shapes.MemberShape
import software.amazon.smithy.model.shapes.UnionShape
import software.amazon.smithy.model.traits.TimestampFormatTrait

/**
 * Generate serialization for members of unions to the payload.
 *
 * Refer to [SerializeStructGenerator] for more details.
 *
 * NOTE: If the serialization order is important then [members] MUST already be sorted correctly
 *
 * Example output this class generates:
 * ```
 * serializer.serializeStruct(OBJ_DESCRIPTOR) {
 *     when (input) {
 *         is FooUnion.IntVal -> field(INTVAL_DESCRIPTOR, input.value)
 *         is FooUnion.StrVal -> field(STRVAL_DESCRIPTOR, input.value)
 *         is FooUnion.Timestamp4 -> field(TIMESTAMP4_DESCRIPTOR, input.value.format(TimestampFormat.ISO_8601))```
 *     }
 * }
 * ```
 */
class SerializeUnionGenerator(
    ctx: ProtocolGenerator.GenerationContext,
    private val shape: UnionShape,
    members: List,
    writer: KotlinWriter,
    defaultTimestampFormat: TimestampFormatTrait.Format,
) : SerializeStructGenerator(ctx, members, writer, defaultTimestampFormat) {

    // Unions do not directly nest, so parent is static.
    override fun parentName(defaultName: String): String = "value"

    // Return the union instance
    override fun valueToSerializeName(defaultName: String): String = when (defaultName) {
        "it" -> "input.value" // Union populates a singular value
        else -> defaultName // Otherwise return the default
    }

    /**
     * Iterate over all supplied [MemberShape]s to generate serializers. Example:
     *
     * ```
     * serializer.serializeStruct(OBJ_DESCRIPTOR) {
     *    when (input) {
     *      ...
     *    }
     *  }
     * ```
     */
    override fun render() {
        val unionSymbol = ctx.symbolProvider.toSymbol(shape)
        val objDescriptor = if (members.isNotEmpty()) "OBJ_DESCRIPTOR" else "SdkObjectDescriptor.build{}"
        writer.withBlock("serializer.serializeStruct($objDescriptor) {", "}") {
            writer.withBlock("when (input) {", "}") {
                members.forEach { memberShape ->
                    renderMemberShape(memberShape)
                }

                write(
                    """is #T.SdkUnknown -> throw #T("Cannot serialize SdkUnknown")""",
                    unionSymbol,
                    RuntimeTypes.Serde.SerializationException,
                )
            }
        }
    }

    /**
     * Produces serialization for a map member.  Example:
     * ```
     * is FooUnion.StrMapVal -> {
     *      mapField(STRMAPVAL_DESCRIPTOR) {
     *          ...
     *      }
     * }
     * ```
     */
    override fun renderMapMemberSerializer(memberShape: MemberShape, targetShape: MapShape) {
        val unionMemberName = memberShape.unionTypeName(ctx)
        val descriptorName = memberShape.descriptorName()
        val nestingLevel = 0

        writer.withBlock("is $unionMemberName -> {", "}") {
            writer.withBlock("mapField($descriptorName) {", "}") {
                delegateMapSerialization(memberShape, targetShape, nestingLevel, "value")
            }
        }
    }

    /**
     * Produces serialization for a list member.  Example:
     * ```
     * is FooUnion.IntListVal -> {
     *      listField(INTLISTVAL_DESCRIPTOR) {
     *          ...
     *      }
     * }
     * ```
     */
    override fun renderListMemberSerializer(memberShape: MemberShape, targetShape: CollectionShape) {
        val unionMemberName = memberShape.unionTypeName(ctx)
        val descriptorName = memberShape.descriptorName()
        val nestingLevel = 0

        writer.withBlock("is $unionMemberName -> {", "}") {
            writer.withBlock("listField($descriptorName) {", "}") {
                delegateListSerialization(memberShape, targetShape, nestingLevel, "value")
            }
        }
    }

    /**
     * Render code to serialize a primitive or structure shape. Example:
     *
     * ```
     * is FooUnion.StringMember -> field(TIMESTAMP4_DESCRIPTOR, input.value)
     * ```
     *
     * @param memberShape [MemberShape] referencing the primitive type
     */
    override fun renderShapeSerializer(
        memberShape: MemberShape,
        serializerFn: SerializeFunction,
    ) {
        val unionTypeName = memberShape.unionTypeName(ctx)
        val fn = serializerFn.format(memberShape, "input.value")
        writer.write("is $unionTypeName -> $fn")
    }
}

/**
 * Generate the fully qualified type name of Union variant
 * e.g. `FooUnion.VariantName`
 */
internal fun MemberShape.unionTypeName(ctx: ProtocolGenerator.GenerationContext): String {
    val unionShape = ctx.model.expectShape(id.withoutMember())
    val unionSymbol = ctx.symbolProvider.toSymbol(unionShape)
    val variantName = unionVariantName()
    return "${unionSymbol.name}.$variantName"
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy