
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