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

com.gw2tb.apigen.internal.dsl.schema.kt Maven / Gradle / Ivy

Go to download

A library for generating programs that interface with the official Guild Wars 2 API.

The newest version!
/*
 * Copyright (c) 2019-2024 Leon Linhart
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
@file:OptIn(LowLevelApiGenApi::class)
@file:Suppress("FunctionName")
package com.gw2tb.apigen.internal.dsl

import com.gw2tb.apigen.internal.impl.*
import com.gw2tb.apigen.ir.*
import com.gw2tb.apigen.ir.model.IRAPIType
import com.gw2tb.apigen.model.*
import com.gw2tb.apigen.model.v2.*
import com.gw2tb.apigen.schema.*
import com.gw2tb.apigen.schema.model.APIType

internal fun > DeferredSchemaType(
    factory: (ScopedTypeRegistry<*>?, Boolean) -> SchemaVersionedDataImpl
): DeferredType = object : DeferredType() {

    override fun get(
        typeRegistry: ScopedTypeRegistry<*>?,
        interpretationHint: APIType.InterpretationHint?,
        isTopLevel: Boolean
    ): SchemaVersionedDataImpl = factory(typeRegistry, isTopLevel)

}



@APIGenDSL
internal abstract class DeferredSchemaClass : DeferredType() {

    abstract val name: Name

    abstract val apiTypeFactory: (SchemaVersionedDataImpl>, APIType.InterpretationHint?, Boolean) -> T

    abstract val typeRegistry: ScopedTypeRegistry?

    open val nestedTypeRegistry: ScopedTypeRegistry? get() = typeRegistry?.nestedScope(name)

    fun array(
        items: DeferredType>,
        nullableItems: Boolean = false
    ): DeferredType =
        DeferredSchemaType { typeRegistry, isTopLevel ->
            items.get(typeRegistry, interpretationHint = null, isTopLevel).mapData { IRArray(it, nullableItems, description = null) }
        }

    fun map(
        keys: DeferredPrimitiveType<*>,
        values: DeferredType>,
        nullableValues: Boolean = false
    ): DeferredType =
        DeferredSchemaType { typeRegistry, isTopLevel ->
            values.get(typeRegistry, interpretationHint = null, isTopLevel).mapData { IRMap(keys.getFlat() as IRPrimitiveOrAlias, it, nullableValues, description = null) }
        }


    fun conditional(
        name: String,
        description: String,
        disambiguationBy: String = "type",
        disambiguationBySideProperty: Boolean = false,
        interpretationInNestedProperty: Boolean = false,
        sharedConfigure: (AbstractSchemaRecordBuilder.() -> Unit)? = null,
        block: SchemaConditionalBuilder.() -> Unit
    ): DeferredSchemaClass =
        conditional(Name.deriveFromTitleCase(name), description, disambiguationBy, disambiguationBySideProperty, interpretationInNestedProperty, sharedConfigure, block)

    fun conditional(
        name: Name,
        description: String,
        disambiguationBy: String = "type",
        disambiguationBySideProperty: Boolean = false,
        interpretationInNestedProperty: Boolean = false,
        sharedConfigure: (AbstractSchemaRecordBuilder.() -> Unit)? = null,
        block: SchemaConditionalBuilder.() -> Unit
    ): DeferredSchemaClass =
        SchemaConditionalBuilder(
            name, description, disambiguationBy, disambiguationBySideProperty, interpretationInNestedProperty,
            sharedConfigure, apiTypeFactory, nestedTypeRegistry
        ).also(block)


    fun enum(type: DeferredPrimitiveType<*>, name: String, description: String, block: SchemaEnumBuilder.() -> Unit): DeferredSchemaClass =
        enum(type, Name.deriveFromTitleCase(name), description, block)

    fun enum(type: DeferredPrimitiveType<*>, name: Name, description: String, block: SchemaEnumBuilder.() -> Unit): DeferredSchemaClass =
        SchemaEnumBuilder(type, name, description, apiTypeFactory, nestedTypeRegistry).also(block)


    fun record(name: String, description: String, block: SchemaRecordBuilder.() -> Unit): DeferredSchemaClass =
        record(Name.deriveFromTitleCase(name), description, block)

    fun record(name: Name, description: String, block: SchemaRecordBuilder.() -> Unit): DeferredSchemaClass =
        SchemaRecordBuilder(name, description, apiTypeFactory, nestedTypeRegistry).also(block)


    fun tuple(name: String, description: String, block: SchemaTupleBuilder.() -> Unit): DeferredSchemaClass =
        tuple(Name.deriveFromTitleCase(name), description, block)

    fun tuple(name: Name, description: String, block: SchemaTupleBuilder.() -> Unit): DeferredSchemaClass =
        SchemaTupleBuilder(name, description, apiTypeFactory, nestedTypeRegistry).also(block)

}

internal abstract class AbstractSchemaRecordBuilder : DeferredSchemaClass() {

    private val _properties = mutableListOf()

    fun buildProperties(typeRegistry: ScopedTypeRegistry?): SchemaVersionedDataImpl>? =
        if (_properties.isEmpty()) {
            null
        } else {
            buildVersionedSchemaData {
                SchemaVersion.values().forEach { version ->
                    if (_properties.any { it.hasChangedInVersion(typeRegistry, version) }) {
                        val relevantProperties = _properties.getForVersion(
                            SchemaRecordPropertyBuilder::since,
                            SchemaRecordPropertyBuilder::until,
                            version
                        )

                        add(relevantProperties.map { it.get(typeRegistry, version) }.associateBy { it.serialName }, since = version)
                    }
                }
            }
        }

    operator fun String.invoke(type: DeferredType>, description: String): SchemaRecordPropertyBuilder =
        SchemaRecordPropertyBuilder(this, type, description).also { _properties += it }

    /** Marks an optional property whose presents is mandated by the given `scope`. */
    fun optional(scope: TokenScope): IPropertyModifier = object : IPropertyModifier {
        override fun applyTo(property: SchemaRecordPropertyBuilder) {
            property.optionality = Optionality.MANDATED(scope)
        }
    }

    /** The minimal [SchemaVersion] (inclusive) required for the property. */
    fun since(version: SchemaVersion): IPropertyModifier = object : IPropertyModifier {
        override fun applyTo(property: SchemaRecordPropertyBuilder) {
            property.since = version
        }
    }

    /** The maximum [SchemaVersion] (exclusive) required for the property. */
    fun until(version: SchemaVersion): IPropertyModifier = object : IPropertyModifier {
        override fun applyTo(property: SchemaRecordPropertyBuilder) {
            property.until = version
        }
    }

    /** Explicitly specifies the _camelCase_ name for the property. */
    @Suppress("FunctionName")
    fun CamelCase(value: String): IPropertyModifier = object : IPropertyModifier {
        override fun applyTo(property: SchemaRecordPropertyBuilder) {
            property.camelCase = value
        }
    }

    /** Explicitly specifies the serial name for the property. */
    @Suppress("FunctionName")
    fun SerialName(value: String): IPropertyModifier = object : IPropertyModifier {
        override fun applyTo(property: SchemaRecordPropertyBuilder) {
            property.serialName = value
        }
    }

}

internal class SchemaConditionalSharedPropertyBuilder(
    override val apiTypeFactory: (SchemaVersionedDataImpl>, APIType.InterpretationHint?, Boolean) -> T,
    override val typeRegistry: ScopedTypeRegistry?
) : AbstractSchemaRecordBuilder() {

    override val nestedTypeRegistry: ScopedTypeRegistry?
        get() = typeRegistry

    override val name: Name
        get() = error("Not implemented")

    override fun get(
        typeRegistry: ScopedTypeRegistry<*>?,
        interpretationHint: APIType.InterpretationHint?,
        isTopLevel: Boolean
    ): SchemaVersionedDataImpl {
        error("Not implemented")
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy