Unverified Commit 6cade905 authored by John DiSanti's avatar John DiSanti Committed by GitHub
Browse files

Move symbol extension functions into `SymbolExt` (#2378)

parent 1c1a3ef4
Loading
Loading
Loading
Loading
+138 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

package software.amazon.smithy.rust.codegen.core.smithy

import software.amazon.smithy.codegen.core.Symbol
import software.amazon.smithy.model.shapes.Shape
import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
import software.amazon.smithy.rust.codegen.core.rustlang.RustType
import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter
import software.amazon.smithy.rust.codegen.core.util.orNull

/** Set the symbolLocation for this symbol builder */
fun Symbol.Builder.locatedIn(rustModule: RustModule.LeafModule): Symbol.Builder {
    val currentRustType = this.build().rustType()
    check(currentRustType is RustType.Opaque) {
        "Only `RustType.Opaque` can have its namespace updated. Received $currentRustType."
    }
    val newRustType = currentRustType.copy(namespace = rustModule.fullyQualifiedPath())
    return this.definitionFile(rustModule.definitionFile())
        .namespace(rustModule.fullyQualifiedPath(), "::")
        .rustType(newRustType)
        .module(rustModule)
}

/**
 * Make the Rust type of a symbol optional (hold `Option<T>`)
 *
 * This is idempotent and will have no change if the type is already optional.
 */
fun Symbol.makeOptional(): Symbol =
    if (isOptional()) {
        this
    } else {
        val rustType = RustType.Option(this.rustType())
        Symbol.builder()
            .rustType(rustType)
            .addReference(this)
            .name(rustType.name)
            .build()
    }

/**
 * Make the Rust type of a symbol boxed (hold `Box<T>`).
 *
 * This is idempotent and will have no change if the type is already boxed.
 */
fun Symbol.makeRustBoxed(): Symbol =
    if (isRustBoxed()) {
        this
    } else {
        val rustType = RustType.Box(this.rustType())
        Symbol.builder()
            .rustType(rustType)
            .addReference(this)
            .name(rustType.name)
            .build()
    }

/**
 * Make the Rust type of a symbol wrapped in `MaybeConstrained`. (hold `MaybeConstrained<T>`).
 *
 * This is idempotent and will have no change if the type is already `MaybeConstrained<T>`.
 */
fun Symbol.makeMaybeConstrained(): Symbol =
    if (this.rustType() is RustType.MaybeConstrained) {
        this
    } else {
        val rustType = RustType.MaybeConstrained(this.rustType())
        Symbol.builder()
            .rustType(rustType)
            .addReference(this)
            .name(rustType.name)
            .build()
    }

/**
 * Map the [RustType] of a symbol with [f].
 *
 * WARNING: This function does not update any symbol references (e.g., `symbol.addReference()`) on the
 * returned symbol. You will have to add those yourself if your logic relies on them.
 **/
fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol {
    val newType = f(this.rustType())
    return Symbol.builder().rustType(newType)
        .name(newType.name)
        .build()
}

/**
 * Type representing the default value for a given type (e.g. for Strings, this is `""`).
 */
sealed class Default {
    /**
     * This symbol has no default value. If the symbol is not optional, this will error during builder construction
     */
    object NoDefault : Default()

    /**
     * This symbol should use the Rust `std::default::Default` when unset
     */
    object RustDefault : Default()
}

/**
 * Returns true when it's valid to use the default/0 value for [this] symbol during construction.
 */
fun Symbol.canUseDefault(): Boolean = this.defaultValue() != Default.NoDefault

/**
 * True when [this] is will be represented by Option<T> in Rust
 */
fun Symbol.isOptional(): Boolean = when (this.rustType()) {
    is RustType.Option -> true
    else -> false
}

fun Symbol.isRustBoxed(): Boolean = rustType().stripOuter<RustType.Option>() is RustType.Box

private const val RUST_TYPE_KEY = "rusttype"
private const val SHAPE_KEY = "shape"
private const val RUST_MODULE_KEY = "rustmodule"
private const val RENAMED_FROM_KEY = "renamedfrom"
private const val SYMBOL_DEFAULT = "symboldefault"

// Symbols should _always_ be created with a Rust type & shape attached
fun Symbol.rustType(): RustType = this.expectProperty(RUST_TYPE_KEY, RustType::class.java)
fun Symbol.Builder.rustType(rustType: RustType): Symbol.Builder = this.putProperty(RUST_TYPE_KEY, rustType)
fun Symbol.shape(): Shape = this.expectProperty(SHAPE_KEY, Shape::class.java)
fun Symbol.Builder.shape(shape: Shape?): Symbol.Builder = this.putProperty(SHAPE_KEY, shape)
fun Symbol.module(): RustModule.LeafModule = this.expectProperty(RUST_MODULE_KEY, RustModule.LeafModule::class.java)
fun Symbol.Builder.module(module: RustModule.LeafModule): Symbol.Builder = this.putProperty(RUST_MODULE_KEY, module)
fun Symbol.renamedFrom(): String? = this.getProperty(RENAMED_FROM_KEY, String::class.java).orNull()
fun Symbol.Builder.renamedFrom(name: String): Symbol.Builder = this.putProperty(RENAMED_FROM_KEY, name)
fun Symbol.defaultValue(): Default = this.getProperty(SYMBOL_DEFAULT, Default::class.java).orElse(Default.NoDefault)
fun Symbol.Builder.setDefault(default: Default): Symbol.Builder = this.putProperty(SYMBOL_DEFAULT, default)
+2 −135
Original line number Diff line number Diff line
@@ -43,7 +43,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustModule
import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords
import software.amazon.smithy.rust.codegen.core.rustlang.RustType
import software.amazon.smithy.rust.codegen.core.rustlang.Visibility
import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter
import software.amazon.smithy.rust.codegen.core.smithy.traits.RustBoxTrait
import software.amazon.smithy.rust.codegen.core.util.PANIC
import software.amazon.smithy.rust.codegen.core.util.hasTrait
@@ -86,83 +85,6 @@ data class SymbolVisitorConfig(
    val moduleProvider: ModuleProvider,
)

/**
 * Make the Rust type of a symbol optional (hold `Option<T>`)
 *
 * This is idempotent and will have no change if the type is already optional.
 */
fun Symbol.makeOptional(): Symbol =
    if (isOptional()) {
        this
    } else {
        val rustType = RustType.Option(this.rustType())
        Symbol.builder()
            .rustType(rustType)
            .addReference(this)
            .name(rustType.name)
            .build()
    }

/**
 * Make the Rust type of a symbol boxed (hold `Box<T>`).
 *
 * This is idempotent and will have no change if the type is already boxed.
 */
fun Symbol.makeRustBoxed(): Symbol =
    if (isRustBoxed()) {
        this
    } else {
        val rustType = RustType.Box(this.rustType())
        Symbol.builder()
            .rustType(rustType)
            .addReference(this)
            .name(rustType.name)
            .build()
    }

/**
 * Make the Rust type of a symbol wrapped in `MaybeConstrained`. (hold `MaybeConstrained<T>`).
 *
 * This is idempotent and will have no change if the type is already `MaybeConstrained<T>`.
 */
fun Symbol.makeMaybeConstrained(): Symbol =
    if (this.rustType() is RustType.MaybeConstrained) {
        this
    } else {
        val rustType = RustType.MaybeConstrained(this.rustType())
        Symbol.builder()
            .rustType(rustType)
            .addReference(this)
            .name(rustType.name)
            .build()
    }

/**
 * Map the [RustType] of a symbol with [f].
 *
 * WARNING: This function does not set any `SymbolReference`s on the returned symbol. You will have to add those
 * yourself if your logic relies on them.
 **/
fun Symbol.mapRustType(f: (RustType) -> RustType): Symbol {
    val newType = f(this.rustType())
    return Symbol.builder().rustType(newType)
        .name(newType.name)
        .build()
}

/** Set the symbolLocation for this symbol builder */
fun Symbol.Builder.locatedIn(rustModule: RustModule.LeafModule): Symbol.Builder {
    val currentRustType = this.build().rustType()
    check(currentRustType is RustType.Opaque) {
        "Only `Opaque` can have their namespace updated"
    }
    val newRustType = currentRustType.copy(namespace = rustModule.fullyQualifiedPath())
    return this.definitionFile(rustModule.definitionFile())
        .namespace(rustModule.fullyQualifiedPath(), "::")
        .rustType(newRustType)
        .module(rustModule)
}

/**
 * Track both the past and current name of a symbol
 *
@@ -401,28 +323,16 @@ fun handleRustBoxing(symbol: Symbol, shape: MemberShape): Symbol =
        symbol.makeRustBoxed()
    } else symbol

fun symbolBuilder(shape: Shape?, rustType: RustType): Symbol.Builder {
    val builder = Symbol.builder().putProperty(SHAPE_KEY, shape)
    return builder.rustType(rustType)
fun symbolBuilder(shape: Shape?, rustType: RustType): Symbol.Builder =
    Symbol.builder().shape(shape).rustType(rustType)
        .name(rustType.name)
        // Every symbol that actually gets defined somewhere should set a definition file
        // If we ever generate a `thisisabug.rs`, there is a bug in our symbol generation
        .definitionFile("thisisabug.rs")
}

fun handleOptionality(symbol: Symbol, member: MemberShape, nullableIndex: NullableIndex, nullabilityCheckMode: CheckMode): Symbol =
    symbol.letIf(nullableIndex.isMemberNullable(member, nullabilityCheckMode)) { symbol.makeOptional() }

private const val RUST_TYPE_KEY = "rusttype"
private const val RUST_MODULE_KEY = "rustmodule"
private const val SHAPE_KEY = "shape"
private const val SYMBOL_DEFAULT = "symboldefault"
private const val RENAMED_FROM_KEY = "renamedfrom"

fun Symbol.Builder.rustType(rustType: RustType): Symbol.Builder = this.putProperty(RUST_TYPE_KEY, rustType)
fun Symbol.Builder.module(module: RustModule.LeafModule): Symbol.Builder = this.putProperty(RUST_MODULE_KEY, module)
fun Symbol.module(): RustModule.LeafModule = this.expectProperty(RUST_MODULE_KEY, RustModule.LeafModule::class.java)

/**
 * Creates a test module for this symbol.
 * For example if the symbol represents the name for the struct `struct MyStruct { ... }`,
@@ -445,49 +355,6 @@ fun SymbolProvider.testModuleForShape(shape: Shape): RustModule.LeafModule {
    )
}

fun Symbol.Builder.renamedFrom(name: String): Symbol.Builder {
    return this.putProperty(RENAMED_FROM_KEY, name)
}

fun Symbol.renamedFrom(): String? = this.getProperty(RENAMED_FROM_KEY, String::class.java).orNull()

fun Symbol.defaultValue(): Default = this.getProperty(SYMBOL_DEFAULT, Default::class.java).orElse(Default.NoDefault)
fun Symbol.Builder.setDefault(default: Default): Symbol.Builder = this.putProperty(SYMBOL_DEFAULT, default)

/**
 * Type representing the default value for a given type. (eg. for Strings, this is `""`)
 */
sealed class Default {
    /**
     * This symbol has no default value. If the symbol is not optional, this will be an error during builder construction
     */
    object NoDefault : Default()

    /**
     * This symbol should use the Rust `std::default::Default` when unset
     */
    object RustDefault : Default()
}

/**
 * True when it is valid to use the default/0 value for [this] symbol during construction.
 */
fun Symbol.canUseDefault(): Boolean = this.defaultValue() != Default.NoDefault

/**
 * True when [this] is will be represented by Option<T> in Rust
 */
fun Symbol.isOptional(): Boolean = when (this.rustType()) {
    is RustType.Option -> true
    else -> false
}

fun Symbol.isRustBoxed(): Boolean = rustType().stripOuter<RustType.Option>() is RustType.Box

// Symbols should _always_ be created with a Rust type & shape attached
fun Symbol.rustType(): RustType = this.expectProperty(RUST_TYPE_KEY, RustType::class.java)
fun Symbol.shape(): Shape = this.expectProperty(SHAPE_KEY, Shape::class.java)

/**
 *  You should rarely need this function, rust names in general should be symbol-aware,
 *  this is "automatic" if you use things like [software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate].