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

software.amazon.smithy.kotlin.codegen.model.SymbolBuilder.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.model

import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.codegen.core.SymbolDependencyContainer
import software.amazon.smithy.codegen.core.SymbolReference
import software.amazon.smithy.kotlin.codegen.core.GeneratedDependency
import software.amazon.smithy.kotlin.codegen.core.KotlinDependency
import software.amazon.smithy.kotlin.codegen.core.SymbolRenderer

@DslMarker
annotation class SymbolDsl

/**
 * Kotlin DSL wrapper around Symbol.Builder
 */
@SymbolDsl
open class SymbolBuilder {
    private val builder = Symbol.builder()
    var name: String? = null
    var nullable: Boolean = true
    var isExtension: Boolean = false
    var objectRef: Symbol? = null
    var namespace: String? = null

    var definitionFile: String? = null
    var declarationFile: String? = null
    var defaultValue: String? = null

    /**
     * Register the function (dependency) responsible for rendering this symbol.
     */
    var renderBy: SymbolRenderer? = null

    val dependencies: MutableSet = mutableSetOf()
    val references: MutableList = mutableListOf()

    fun dependency(dependency: SymbolDependencyContainer) = dependencies.add(dependency)

    fun reference(ref: SymbolReference) = references.add(ref)

    fun reference(symbol: Symbol, vararg options: SymbolReference.ContextOption) {
        if (options.isEmpty()) {
            builder.addReference(symbol)
        } else {
            val ref = SymbolReference.builder()
                .symbol(symbol)
                .options(options.toSet())
                .build()
            references += ref
        }
    }

    fun reference(block: SymbolBuilder.() -> Unit) {
        val refSymbol = SymbolBuilder().apply(block).build()
        reference(refSymbol)
    }

    fun setProperty(key: String, value: Any) { builder.putProperty(key, value) }
    fun removeProperty(key: String) { builder.removeProperty(key) }
    fun properties(block: PropertiesBuilder.() -> Unit) {
        val propBuilder = object : PropertiesBuilder {
            override fun set(key: String, value: Any) = setProperty(key, value)
            override fun remove(key: String) = removeProperty(key)
        }

        block(propBuilder)
    }

    interface PropertiesBuilder {
        fun set(key: String, value: Any)
        fun remove(key: String)
    }

    fun build(): Symbol {
        builder.name(name)
        if (nullable) {
            builder.nullable()
        }
        builder.putProperty(SymbolProperty.IS_EXTENSION, isExtension)
        if (objectRef != null) {
            builder.putProperty(SymbolProperty.OBJECT_REF, objectRef)
        }

        namespace?.let { builder.namespace(namespace, ".") }
        declarationFile?.let { builder.declarationFile(it) }
        definitionFile?.let { builder.definitionFile(it) }
        defaultValue?.let { builder.defaultValue(it) }

        if (renderBy != null) {
            checkNotNull(name) { "a rendered dependency must declare a name!" }
            checkNotNull(definitionFile) { "a rendered dependency must declare a definition file!" }
            checkNotNull(namespace) { "a rendered dependency must declare a namespace" }
            // abuse dependencies to get the delegator to eventually render this
            val generatedDep = GeneratedDependency(name!!, namespace!!, definitionFile!!, renderBy!!)
            dependency(generatedDep)
        }

        dependencies.forEach { builder.addDependency(it) }
        references.forEach { builder.addReference(it) }

        return builder.build()
    }
}

/**
 * Build a symbol inside the given block
 */
fun buildSymbol(block: SymbolBuilder.() -> Unit): Symbol =
    SymbolBuilder().apply(block).build()

fun SymbolBuilder.namespace(dependency: KotlinDependency, subpackage: String = "") {
    namespace = if (subpackage.isNotEmpty()) {
        "${dependency.namespace}.${subpackage.trimStart('.')}"
    } else {
        dependency.namespace
    }

    dependency(dependency)
}

/**
 * Convert a String in "." format to a symbol where the last segment of a '.' delimited list becomes
 * the name and the rest becomes the namespace.
 *
 * ex: com.foo.bar.Thing -> (name=Thing, namespace=com.foo.bar)
 */
fun String.toSymbol(): Symbol =
    buildSymbol {
        require([email protected]()) { "Invalid string to convert to symbol" }
        val segments = split(".")
        name = segments.last()
        namespace = segments
            .dropLast(1)
            .joinToString(separator = ".") { it }
    }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy