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

software.amazon.smithy.kotlin.codegen.rendering.ExceptionBaseClassGenerator.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

import software.amazon.smithy.codegen.core.CodegenException
import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.kotlin.codegen.KotlinSettings
import software.amazon.smithy.kotlin.codegen.core.*
import software.amazon.smithy.kotlin.codegen.integration.SectionId
import software.amazon.smithy.kotlin.codegen.model.buildSymbol
import software.amazon.smithy.kotlin.codegen.rendering.protocol.ProtocolGenerator
import software.amazon.smithy.model.knowledge.TopDownIndex

/**
 * Renders the base class that all (modeled) exceptions inherit from.
 * Protocol generators are allowed to override this but they MUST inherit from the base `ServiceException`
 * with the expected constructors.
 */
object ExceptionBaseClassGenerator {

    /**
     * Defines a section in which code can be added to the body of the base exception type.
     */
    object ExceptionBaseClassSection : SectionId

    fun render(ctx: CodegenContext, writer: KotlinWriter) {
        val baseException = ctx.protocolGenerator?.exceptionBaseClassSymbol ?: ProtocolGenerator.DefaultServiceExceptionSymbol
        writer.addImport(baseException)
        val serviceException = baseExceptionSymbol(ctx.settings).also { checkForCollision(ctx, it) }

        val name = clientName(ctx.settings.sdkId)
        writer.dokka("Base class for all service related exceptions thrown by the $name client")
        writer.withBlock(
            "open class #T : #T {", "}",
            serviceException,
            baseException
        ) {
            write("constructor() : super()")
            write("constructor(message: String?) : super(message)")
            write("constructor(message: String?, cause: Throwable?) : super(message, cause)")
            write("constructor(cause: Throwable?) : super(cause)")

            writer.declareSection(ExceptionBaseClassSection)
        }
    }

    /**
     * Get the (generated) symbol that constitutes the base class exceptions will inherit from
     */
    fun baseExceptionSymbol(settings: KotlinSettings): Symbol = buildSymbol {
        val serviceName = clientName(settings.sdkId)
        name = "${serviceName}Exception"
        namespace = "${settings.pkg.name}.model"
        definitionFile = "$name.kt"
    }

    // Compare generated base exception name with all error type names.  Throw exception if not unique.
    private fun checkForCollision(ctx: CodegenContext, exceptionSymbol: Symbol) {
        val topDownIndex = TopDownIndex.of(ctx.model)
        val operations = topDownIndex.getContainedOperations(ctx.settings.service)

        operations.forEach { operationShape ->
            val errorNameToShapeIndex = operationShape.errors.associateBy { shapeId -> shapeId.name }
            if (errorNameToShapeIndex.containsKey(exceptionSymbol.name)) {
                throw CodegenException("Generated base error type '${exceptionSymbol.name}' collides with ${errorNameToShapeIndex[exceptionSymbol.name]}.")
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy