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

com.lightningkite.lightningdb.DataClassPathSerializer.kt Maven / Gradle / Ivy

package com.lightningkite.lightningdb

import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.InternalSerializationApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.SerialKind
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.internal.GeneratedSerializer
import kotlin.reflect.KProperty1


@OptIn(InternalSerializationApi::class)
private class KProperty1Parser(val serializer: KSerializer) {
    val children = run {
        val c: Map> = (serializer as GeneratedSerializer).childSerializers().withIndex()
            .associate { serializer.descriptor.getElementName(it.index) to it.value }
        val f: Map> = serializer.attemptGrabFields()
        c.mapNotNull {
            it.key to ((f[it.key] ?: return@mapNotNull null) to it.value)
        }.associate { it }
    }
    companion object {
        val existing = HashMap, KProperty1Parser<*>>()
        @Suppress("UNCHECKED_CAST")
        operator fun  get(serializer: KSerializer): KProperty1Parser = existing.getOrPut(serializer) {
            KProperty1Parser(serializer)
        } as KProperty1Parser
    }
    operator fun invoke(key: String): Pair, KSerializer<*>> {
        @Suppress("UNCHECKED_CAST")
        return children[key]
            ?: throw IllegalStateException("Could find no property with name '$key' on ${serializer.descriptor.serialName}")
    }
}

class DataClassPathSerializer(val inner: KSerializer): KSerializer> {
    @OptIn(ExperimentalSerializationApi::class)
    override val descriptor: SerialDescriptor = object: SerialDescriptor {
        override val kind: SerialKind = PrimitiveKind.STRING
        override val serialName: String = "com.lightningkite.lightningdb.DataClassPathPartial"
        override val elementsCount: Int get() = 0
        override fun getElementName(index: Int): String = error()
        override fun getElementIndex(name: String): Int = error()
        override fun isElementOptional(index: Int): Boolean = error()
        override fun getElementDescriptor(index: Int): SerialDescriptor = error()
        override fun getElementAnnotations(index: Int): List = error()
        override fun toString(): String = "PrimitiveDescriptor($serialName)"
        private fun error(): Nothing = throw IllegalStateException("Primitive descriptor does not have elements")
        override val annotations: List = DataClassPathPartial::class.annotations
    }

    override fun deserialize(decoder: Decoder): DataClassPathPartial {
        val value = decoder.decodeString()
        return fromString(value)
    }

    override fun serialize(encoder: Encoder, value: DataClassPathPartial) {
        encoder.encodeString(value.toString())
    }

    fun fromString(value: String): DataClassPathPartial {
        var current: DataClassPathPartial? = null
        var currentSerializer: KSerializer<*> = inner
        for(part in value.split('.')) {
            val name = part.removeSuffix("?")
            if(name == "this") continue
            val prop = KProperty1Parser[currentSerializer](name)
            currentSerializer = prop.second
            val c = current
            @Suppress("UNCHECKED_CAST")
            current = if(c == null) DataClassPathAccess(DataClassPathSelf(), prop.first as KProperty1)
            else DataClassPathAccess(c as DataClassPath, prop.first as KProperty1)
            if(part.endsWith('?')) {
                current = DataClassPathNotNull(current as DataClassPath)
                currentSerializer = currentSerializer.nullElement()!!
            }
        }

        return current ?: DataClassPathSelf()
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy