jvmMain.definitions.RaptorGraphDefinition.kt Maven / Gradle / Ivy
The newest version!
package io.fluidsonic.raptor
import io.fluidsonic.graphql.*
import io.fluidsonic.raptor.graphql.internal.*
import kotlin.properties.*
import kotlin.reflect.*
public sealed class RaptorGraphNode(
internal val additionalDefinitions: Collection,
internal val stackTrace: List,
) {
protected abstract fun debugString(): String
final override fun toString(): String =
"${debugString()}\n" + stackTrace.joinToString(separator = "\n") { "\tat $it" }
}
public sealed class RaptorGraphDefinition(
additionalDefinitions: Collection,
stackTrace: List,
) : RaptorGraphNode(
additionalDefinitions = additionalDefinitions,
stackTrace = stackTrace
) {
public companion object {
@PublishedApi
internal const val defaultName: String = ""
// TODO move
internal fun resolveName(
name: String,
defaultNamePrefix: String? = null,
type: KType,
) =
resolveName(name) { defaultNamePrefix.orEmpty() + type.defaultGraphName() }
// TODO move
internal inline fun resolveName(
name: String,
defaultName: () -> String,
) = when (name) {
RaptorGraphDefinition.defaultName -> defaultName()
else -> name
}
}
}
internal class AliasGraphTypeDefinition(
additionalDefinitions: Collection,
val convertReferencedToAlias: RaptorGraphInputScope.(input: Any) -> Any,
val convertAliasToReferenced: RaptorGraphOutputScope.(output: Any) -> Any,
val isId: Boolean,
override val isInput: Boolean,
override val isOutput: Boolean,
kotlinType: KotlinType,
val referencedKotlinType: KotlinType,
stackTrace: List,
) : GraphTypeDefinition(
additionalDefinitions = additionalDefinitions,
kotlinType = kotlinType,
stackTrace = stackTrace
) {
init {
require(!kotlinType.isGeneric || !isId)
require(isInput || isOutput)
}
override fun debugString() =
"${if (isId) "id" else "alias"} $kotlinType = $referencedKotlinType"
override fun specialize(typeArguments: List, namePrefix: String): AliasGraphTypeDefinition {
check(kotlinType.isGeneric)
return AliasGraphTypeDefinition(
additionalDefinitions = additionalDefinitions,
convertAliasToReferenced = convertAliasToReferenced,
convertReferencedToAlias = convertReferencedToAlias,
isId = isId,
isInput = isInput,
isOutput = isOutput,
kotlinType = kotlinType.specialize(typeArguments),
referencedKotlinType = referencedKotlinType,
stackTrace = stackTrace
)
}
}
internal class EnumGraphDefinition(
additionalDefinitions: Collection,
description: String?,
override val isInput: Boolean,
override val isOutput: Boolean,
kotlinType: KotlinType,
name: String,
val parse: RaptorGraphInputScope.(input: String) -> Any,
val serialize: RaptorGraphOutputScope.(output: Any) -> String,
stackTrace: List,
val values: Set,
) : NamedGraphTypeDefinition(
additionalDefinitions = additionalDefinitions,
description = description,
kotlinType = kotlinType,
name = name,
stackTrace = stackTrace,
) {
init {
require(!kotlinType.isGeneric)
require(isInput || isOutput)
}
override fun debugString() =
"enum $name = $kotlinType"
override fun specialize(typeArguments: List, namePrefix: String): EnumGraphDefinition {
error("Enum definitions cannot be generic.")
}
}
internal class GraphArgumentDefinition(
val defaultValue: GValue?,
val description: String?,
val kotlinType: KotlinType,
name: String?,
val resolver: ArgumentResolver,
stackTrace: List,
) : RaptorGraphNode(
additionalDefinitions = emptyList(),
stackTrace = stackTrace
),
RaptorGraphArgumentDelegate {
private var isProvided = false
private var transforms = emptyList Any?>()
internal var name = name // TODO validate uniqueness
private set
override fun debugString() =
"argument '$name': $kotlinType"
override fun map(transform: RaptorGraphInputScope.(value: Any?) -> TransformedType): RaptorGraphArgumentDelegate {
// TODO freeze after builder is done
transforms = transforms + transform
@Suppress("UNCHECKED_CAST")
return this as RaptorGraphArgumentDelegate
}
override operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): ReadOnlyProperty {
check(!isProvided) { "Cannot delegate multiple variables to the same argument." }
val variableName = property.name
val name = name ?: variableName
this.isProvided = true
this.name = name
return ReadOnlyProperty { _, _ ->
resolveArgument(name = name, variableName = variableName)
}
}
private fun resolveArgument(name: String, variableName: String): Any? =
resolver.resolveArgument(name = name, variableName = variableName, transforms = transforms)
fun specialize(typeArguments: List): GraphArgumentDefinition {
if (kotlinType.isSpecialized)
return this
return GraphArgumentDefinition(
defaultValue = defaultValue,
description = description,
kotlinType = kotlinType.specialize(typeArguments),
name = name,
resolver = resolver,
stackTrace = stackTrace
)
}
}
internal sealed class GraphFieldDefinition(
val argumentDefinitions: Collection,
val description: String?,
val kotlinType: KotlinType,
val name: String,
stackTrace: List,
) : RaptorGraphNode(
additionalDefinitions = emptyList(),
stackTrace = stackTrace
) {
override fun debugString() =
"field '$name': $kotlinType"
abstract fun specialize(typeArguments: List): GraphFieldDefinition
class Resolvable(
argumentDefinitions: Collection,
val argumentResolver: ArgumentResolver,
description: String?,
kotlinType: KotlinType,
name: String,
val resolve: suspend RaptorGraphOutputScope.(parent: Any) -> Any?,
stackTrace: List,
) : GraphFieldDefinition(
argumentDefinitions = argumentDefinitions,
description = description,
kotlinType = kotlinType,
name = name,
stackTrace = stackTrace
) {
override fun specialize(typeArguments: List) = Resolvable(
argumentDefinitions = argumentDefinitions.map { it.specialize(typeArguments) },
argumentResolver = argumentResolver,
description = description,
kotlinType = kotlinType.specialize(typeArguments),
name = name,
resolve = resolve,
stackTrace = stackTrace
)
}
class Unresolvable(
argumentDefinitions: Collection,
description: String?,
kotlinType: KotlinType,
name: String,
stackTrace: List,
) : GraphFieldDefinition(
argumentDefinitions = argumentDefinitions,
description = description,
kotlinType = kotlinType,
name = name,
stackTrace = stackTrace
) {
override fun specialize(typeArguments: List) = Unresolvable(
argumentDefinitions = argumentDefinitions.map { it.specialize(typeArguments) },
description = description,
kotlinType = kotlinType.specialize(typeArguments),
name = name,
stackTrace = stackTrace
)
}
}
internal class GraphOperationDefinition(
additionalDefinitions: Collection,
val fieldDefinition: GraphFieldDefinition,
val operationType: RaptorGraphOperationType,
stackTrace: List,
) : RaptorGraphDefinition(
additionalDefinitions = additionalDefinitions,
stackTrace = stackTrace
) {
override fun debugString() =
"$operationType '${fieldDefinition.name}': ${fieldDefinition.kotlinType}"
}
internal sealed class GraphTypeDefinition(
additionalDefinitions: Collection,
kotlinType: KotlinType,
stackTrace: List,
) : GraphTypeSystemDefinition(
additionalDefinitions = additionalDefinitions,
stackTrace = stackTrace,
kotlinType = kotlinType
) {
abstract val isInput: Boolean
abstract val isOutput: Boolean
}
internal sealed class GraphTypeExtensionDefinition(
additionalDefinitions: Collection,
kotlinType: KotlinType,
stackTrace: List,
) : GraphTypeSystemDefinition(
additionalDefinitions = additionalDefinitions,
kotlinType = kotlinType,
stackTrace = stackTrace
)
internal sealed class GraphTypeSystemDefinition(
additionalDefinitions: Collection,
val kotlinType: KotlinType,
stackTrace: List,
) : RaptorGraphDefinition(
additionalDefinitions = additionalDefinitions,
stackTrace = stackTrace
) {
abstract fun specialize(typeArguments: List, namePrefix: String): GraphTypeSystemDefinition
}
internal class InputObjectGraphDefinition(
additionalDefinitions: Collection,
val argumentDefinitions: Collection,
val argumentResolver: ArgumentResolver,
val create: RaptorGraphInputScope.() -> Any,
description: String?,
kotlinType: KotlinType,
name: String,
stackTrace: List,
) : NamedGraphTypeDefinition(
additionalDefinitions = additionalDefinitions,
description = description,
kotlinType = kotlinType,
name = name,
stackTrace = stackTrace
) {
override fun debugString() =
"input $name = $kotlinType"
override val isInput: Boolean
get() = true
override val isOutput: Boolean
get() = false
override fun specialize(typeArguments: List, namePrefix: String): InputObjectGraphDefinition {
check(kotlinType.isGeneric)
return InputObjectGraphDefinition(
additionalDefinitions = additionalDefinitions,
argumentDefinitions = argumentDefinitions.map { it.specialize(typeArguments) },
argumentResolver = argumentResolver,
create = create,
description = description,
kotlinType = kotlinType.specialize(typeArguments),
name = "${namePrefix}$name",
stackTrace = stackTrace
)
}
}
internal class InterfaceGraphDefinition(
additionalDefinitions: Collection,
description: String?,
val fieldDefinitions: Collection,
kotlinType: KotlinType,
name: String,
stackTrace: List,
) : NamedGraphTypeDefinition(
additionalDefinitions = additionalDefinitions,
description = description,
kotlinType = kotlinType,
name = name,
stackTrace = stackTrace
) {
override fun debugString() =
"interface $name: $kotlinType"
override val isInput: Boolean
get() = false
override val isOutput: Boolean
get() = true
override fun specialize(typeArguments: List, namePrefix: String): InterfaceGraphDefinition {
check(kotlinType.isGeneric)
return InterfaceGraphDefinition(
additionalDefinitions = additionalDefinitions,
description = description,
fieldDefinitions = fieldDefinitions.map { it.specialize(typeArguments) },
kotlinType = kotlinType.specialize(typeArguments),
name = "${namePrefix}$name",
stackTrace = stackTrace
)
}
}
internal sealed class NamedGraphTypeDefinition(
additionalDefinitions: Collection,
val description: String?,
kotlinType: KotlinType,
val name: String,
stackTrace: List,
) : GraphTypeDefinition(
additionalDefinitions = additionalDefinitions,
kotlinType = kotlinType,
stackTrace = stackTrace
)
internal class InterfaceExtensionGraphDefinition(
additionalDefinitions: Collection,
val fieldDefinitions: Collection,
kotlinType: KotlinType,
stackTrace: List,
) : GraphTypeExtensionDefinition(
additionalDefinitions = additionalDefinitions,
kotlinType = kotlinType,
stackTrace = stackTrace
) {
override fun debugString() =
"extension interface: $kotlinType"
override fun specialize(typeArguments: List, namePrefix: String): InterfaceExtensionGraphDefinition {
check(kotlinType.isGeneric)
return InterfaceExtensionGraphDefinition(
additionalDefinitions = additionalDefinitions,
fieldDefinitions = fieldDefinitions.map { it.specialize(typeArguments) },
kotlinType = kotlinType.specialize(typeArguments),
stackTrace = stackTrace
)
}
}
internal class ObjectGraphDefinition(
additionalDefinitions: Collection,
description: String?,
val fieldDefinitions: Collection,
kotlinType: KotlinType,
name: String,
stackTrace: List,
) : NamedGraphTypeDefinition(
additionalDefinitions = additionalDefinitions,
description = description,
kotlinType = kotlinType,
name = name,
stackTrace = stackTrace
) {
override fun debugString() =
"type $name: $kotlinType"
override val isInput: Boolean
get() = false
override val isOutput: Boolean
get() = true
override fun specialize(typeArguments: List, namePrefix: String): ObjectGraphDefinition {
check(kotlinType.isGeneric)
return ObjectGraphDefinition(
additionalDefinitions = additionalDefinitions,
description = description,
fieldDefinitions = fieldDefinitions.map { it.specialize(typeArguments) },
kotlinType = kotlinType.specialize(typeArguments),
name = "${namePrefix}$name",
stackTrace = stackTrace
)
}
}
internal class ObjectExtensionGraphDefinition(
additionalDefinitions: Collection,
val fieldDefinitions: Collection,
kotlinType: KotlinType,
stackTrace: List,
) : GraphTypeExtensionDefinition(
additionalDefinitions = additionalDefinitions,
kotlinType = kotlinType,
stackTrace = stackTrace
) {
override fun debugString() =
"extension object: $kotlinType"
override fun specialize(typeArguments: List, namePrefix: String): ObjectExtensionGraphDefinition {
check(kotlinType.isGeneric)
return ObjectExtensionGraphDefinition(
additionalDefinitions = additionalDefinitions,
fieldDefinitions = fieldDefinitions.map { it.specialize(typeArguments) },
kotlinType = kotlinType.specialize(typeArguments),
stackTrace = stackTrace
)
}
}
internal class ScalarGraphDefinition(
additionalDefinitions: Collection,
description: String?,
override val isInput: Boolean,
override val isOutput: Boolean,
kotlinType: KotlinType,
name: String,
val parse: RaptorGraphInputScope.(input: Any) -> Any,
val serialize: RaptorGraphOutputScope.(output: Any) -> Any,
stackTrace: List,
) : NamedGraphTypeDefinition(
additionalDefinitions = additionalDefinitions,
description = description,
kotlinType = kotlinType,
name = name,
stackTrace = stackTrace
) {
init {
require(isInput || isOutput)
}
override fun debugString() =
"scalar $name: $kotlinType"
override fun specialize(typeArguments: List, namePrefix: String): ScalarGraphDefinition {
check(kotlinType.isGeneric)
return ScalarGraphDefinition(
additionalDefinitions = additionalDefinitions,
description = description,
isInput = isInput,
isOutput = isOutput,
kotlinType = kotlinType.specialize(typeArguments),
name = "${namePrefix}$name",
parse = parse,
serialize = serialize,
stackTrace = stackTrace
)
}
}
internal class UnionGraphDefinition(
additionalDefinitions: Collection,
description: String?,
kotlinType: KotlinType,
name: String,
stackTrace: List,
) : NamedGraphTypeDefinition(
additionalDefinitions = additionalDefinitions,
description = description,
kotlinType = kotlinType,
name = name,
stackTrace = stackTrace
) {
override fun debugString() =
"union $name: $kotlinType"
override val isInput: Boolean
get() = false
override val isOutput: Boolean
get() = true
override fun specialize(typeArguments: List, namePrefix: String): UnionGraphDefinition {
check(kotlinType.isGeneric)
return UnionGraphDefinition(
additionalDefinitions = additionalDefinitions,
description = description,
kotlinType = kotlinType.specialize(typeArguments),
name = "${namePrefix}$name",
stackTrace = stackTrace
)
}
}