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

software.amazon.smithy.kotlin.codegen.model.SymbolExt.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.SymbolReference
import software.amazon.smithy.kotlin.codegen.core.KotlinDependency
import software.amazon.smithy.kotlin.codegen.utils.getOrNull
import software.amazon.smithy.model.shapes.Shape

/**
 * Property bag keys used by symbol provider implementation
 */
object SymbolProperty {
    // The key that holds the default value for a type (symbol) as a string
    const val DEFAULT_VALUE_KEY: String = "defaultValue"

    // The key that holds the type of default value
    const val DEFAULT_VALUE_TYPE_KEY: String = "defaultValueType"

    // Boolean property indicating this symbol is nullable
    const val NULLABLE_KEY: String = "nullable"

    // the original shape the symbol was created from
    const val SHAPE_KEY: String = "shape"

    // Mutable collection type
    const val MUTABLE_COLLECTION_FUNCTION: String = "mutableCollectionType"

    // Immutable collection type
    const val IMMUTABLE_COLLECTION_FUNCTION: String = "immutableCollectionType"

    // Entry type for Maps
    const val ENTRY_EXPRESSION: String = "entryExpression"

    // Pseudo dependency on a snippet of code
    const val GENERATED_DEPENDENCY: String = "generatedDependency"

    // controls whether the property type is `var` vs `val` when formatted as a property
    const val PROPERTY_TYPE_MUTABILITY: String = "propertyTypeMutability"

    // Denotes whether a symbol represents an extension function
    const val IS_EXTENSION: String = "isExtension"

    // Denotes the symbol is a reference to a static member of an object (e.g. of an object or companion object)
    const val OBJECT_REF: String = "objectRef"

    // Adds a property to give a hint at what the fully qualified name should be. Used by #Q symbol formatter to
    // give symbols fine-grained control over their fully qualified name (e.g. collections with generics can fully
    // qualify the generic type)
    const val FULLY_QUALIFIED_NAME_HINT: String = "fullyQualifiedNameHint"
}

/**
 * Test if a symbol is required (not-nullable) with no default value set. This means there is no builder
 * set default so the constructor will have to generate a runtime check that a value is set.
 */
val Symbol.isRequiredWithNoDefault: Boolean
    get() = isNotNullable && defaultValue() == null

/**
 * Test if a symbol is nullable
 */
val Symbol.isNullable: Boolean
    get() = getProperty(SymbolProperty.NULLABLE_KEY).map {
        when (it) {
            is Boolean -> it
            else -> false
        }
    }.orElse(false)

/**
 * Test if a symbol is not nullable
 */
val Symbol.isNotNullable: Boolean
    get() = !isNullable

enum class PropertyTypeMutability {
    /**
     * Immutable property (e.g. `val`)
     */
    IMMUTABLE,

    /**
     * Mutable property (e.g. `var`)
     */
    MUTABLE,

    ;

    override fun toString(): String = when (this) {
        IMMUTABLE -> "val"
        MUTABLE -> "var"
    }
}

/**
 * Get the property type mutability of this symbol if set.
 */
val Symbol.propertyTypeMutability: PropertyTypeMutability?
    get() = getProperty(SymbolProperty.PROPERTY_TYPE_MUTABILITY)
        .map { it as PropertyTypeMutability }
        .getOrNull()

/**
 * Gets the default value for the symbol if present, else null
 */
fun Symbol.defaultValue(): String? {
    val default = getProperty(SymbolProperty.DEFAULT_VALUE_KEY, String::class.java)

    // nullable types should default to null if there is no modeled default
    if (isNullable && !default.isPresent) {
        return "null"
    }

    return default.getOrNull()
}

/**
 * Mark a symbol as being nullable (i.e. `T?`)
 */
fun Symbol.Builder.nullable(): Symbol.Builder = apply { putProperty(SymbolProperty.NULLABLE_KEY, true) }

/**
 * Mark a symbol as being non - nullable (i.e. `T?`)
 */
fun Symbol.Builder.nonNullable(): Symbol.Builder = apply { removeProperty(SymbolProperty.NULLABLE_KEY) }

/**
 * Set the default value used when formatting the symbol
 */
fun Symbol.Builder.defaultValue(value: String?): Symbol.Builder = apply {
    putProperty(SymbolProperty.DEFAULT_VALUE_KEY, value)
}

/**
 * Convenience function for specifying kotlin namespace
 */
fun Symbol.Builder.namespace(name: String): Symbol.Builder = namespace(name, ".")

/**
 * Convenience function for specifying a symbol is a subnamespace of the [dependency].
 *
 * This will implicitly add the dependecy to the builder since it is known that it comes from the
 * dependency namespace.
 *
 * Equivalent to:
 * ```
 * builder.addDependency(dependency)
 * builder.namespace("${dependency.namespace}.subnamespace")
 * ```
 */
fun Symbol.Builder.namespace(dependency: KotlinDependency, subnamespace: String = ""): Symbol.Builder {
    addDependency(dependency)
    return if (subnamespace.isEmpty()) {
        namespace(dependency.namespace)
    } else {
        namespace("${dependency.namespace}.${subnamespace.trimStart('.')}")
    }
}

/**
 * Add a reference to a symbol coming from a kotlin dependency
 */
fun Symbol.Builder.addReference(dependency: KotlinDependency, name: String, subnamespace: String = ""): Symbol.Builder {
    val refSymbol = Symbol.builder()
        .name(name)
        .namespace(dependency, subnamespace)
        .build()
    return addReference(refSymbol)
}

/**
 * Add a reference to the given symbol with the context option
 */
fun Symbol.Builder.addReference(symbol: Symbol, option: SymbolReference.ContextOption): Symbol.Builder {
    val ref = SymbolReference.builder()
        .symbol(symbol)
        .options(option)
        .build()

    return addReference(ref)
}

/**
 * Mark a symbol property type mutability
 */
fun Symbol.Builder.propertyTypeMutability(mutability: PropertyTypeMutability): Symbol.Builder = apply { putProperty(SymbolProperty.PROPERTY_TYPE_MUTABILITY, mutability) }

/**
 * Get the shape this symbol was created from
 */
val Symbol.shape: Shape?
    get() = getProperty(SymbolProperty.SHAPE_KEY, Shape::class.java).getOrNull()

/**
 * Get the nullable version of a symbol
 */
fun Symbol.asNullable(): Symbol = toBuilder().nullable().build()

/**
 * Get the non-nullable version of a symbol
 */
fun Symbol.asNonNullable(): Symbol = toBuilder().nonNullable().build()

/**
 * Check whether a symbol represents an extension
 */
val Symbol.isExtension: Boolean
    get() = getProperty(SymbolProperty.IS_EXTENSION).getOrNull() == true

/**
 * Check whether a symbol represents a static reference (member of object/companion object)
 */
val Symbol.isObjectRef: Boolean
    get() = getProperty(SymbolProperty.OBJECT_REF).getOrNull() != null

/**
 * Get the parent object/companion object symbol
 */
val Symbol.objectRef: Symbol?
    get() = getProperty(SymbolProperty.OBJECT_REF, Symbol::class.java).getOrNull()

/**
 * Get the fully qualified name hint if one is set
 */
val Symbol.fullNameHint: String?
    get() = getProperty(SymbolProperty.FULLY_QUALIFIED_NAME_HINT, String::class.java).getOrNull()




© 2015 - 2025 Weber Informatics LLC | Privacy Policy