mirror of
https://github.com/InsanusMokrassar/SDI.git
synced 2024-11-23 10:38:52 +00:00
big refactoring
This commit is contained in:
parent
4d105caf24
commit
436345b751
@ -21,9 +21,9 @@ fun Json.loadModule(
|
|||||||
moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null
|
moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null
|
||||||
): Module = decodeFromString(
|
): Module = decodeFromString(
|
||||||
if (moduleBuilder != null) {
|
if (moduleBuilder != null) {
|
||||||
ModuleDeserializerStrategy(moduleBuilder, *additionalClassesToInclude)
|
ModuleSerializer(moduleBuilder, *additionalClassesToInclude)
|
||||||
} else {
|
} else {
|
||||||
ModuleDeserializerStrategy(additionalClassesToInclude = *additionalClassesToInclude)
|
ModuleSerializer(additionalClassesToInclude = *additionalClassesToInclude)
|
||||||
},
|
},
|
||||||
json
|
json
|
||||||
)
|
)
|
||||||
|
@ -3,5 +3,8 @@ package dev.inmo.sdi
|
|||||||
import kotlinx.serialization.Contextual
|
import kotlinx.serialization.Contextual
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable(ModuleFullSerializer::class)
|
@Serializable(ModuleSerializer::class)
|
||||||
class Module internal constructor(base: Map<String, @Contextual Any>) : Map<String, Any> by base
|
class Module internal constructor(
|
||||||
|
base: Map<String, @Contextual Any>,
|
||||||
|
internal val serialContext: SerializationContext
|
||||||
|
) : Map<String, Any> by base
|
@ -1,51 +0,0 @@
|
|||||||
package dev.inmo.sdi
|
|
||||||
|
|
||||||
import dev.inmo.sdi.utils.createModuleBasedOnConfigRoot
|
|
||||||
import kotlinx.serialization.*
|
|
||||||
import kotlinx.serialization.builtins.MapSerializer
|
|
||||||
import kotlinx.serialization.builtins.serializer
|
|
||||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
|
||||||
import kotlinx.serialization.encoding.Decoder
|
|
||||||
import kotlinx.serialization.encoding.Encoder
|
|
||||||
import kotlinx.serialization.json.*
|
|
||||||
import kotlinx.serialization.modules.SerializersModuleBuilder
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
internal class ModuleDeserializerStrategy(
|
|
||||||
private val moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null,
|
|
||||||
private vararg val additionalClassesToInclude: KClass<*>
|
|
||||||
) : DeserializationStrategy<Module> {
|
|
||||||
constructor() : this(null)
|
|
||||||
|
|
||||||
private val internalSerializer = MapSerializer(String.serializer(), ContextualSerializer(Any::class))
|
|
||||||
override val descriptor: SerialDescriptor
|
|
||||||
get() = internalSerializer.descriptor
|
|
||||||
|
|
||||||
@InternalSerializationApi
|
|
||||||
override fun deserialize(decoder: Decoder): Module {
|
|
||||||
val json = JsonObject.serializer().deserialize(decoder)
|
|
||||||
val jsonSerialFormat = createModuleBasedOnConfigRoot(
|
|
||||||
(decoder as? JsonDecoder) ?.json,
|
|
||||||
json,
|
|
||||||
moduleBuilder,
|
|
||||||
decoder.serializersModule,
|
|
||||||
*additionalClassesToInclude
|
|
||||||
)
|
|
||||||
val resultJson = JsonObject(
|
|
||||||
json.keys.associateWith { JsonPrimitive(it) }
|
|
||||||
)
|
|
||||||
val map = jsonSerialFormat.decodeFromJsonElement(internalSerializer, resultJson)
|
|
||||||
return Module(map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializer(Module::class)
|
|
||||||
internal class ModuleFullSerializer(
|
|
||||||
private val moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null,
|
|
||||||
private vararg val additionalClassesToInclude: KClass<*>
|
|
||||||
) : KSerializer<Module>,
|
|
||||||
DeserializationStrategy<Module> by ModuleDeserializerStrategy(moduleBuilder, *additionalClassesToInclude) {
|
|
||||||
constructor() : this(null)
|
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, value: Module) = throw NotImplementedError("Currently there is no support for serialization of modules")
|
|
||||||
}
|
|
156
src/commonMain/kotlin/dev/inmo/sdi/ModuleSerializer.kt
Normal file
156
src/commonMain/kotlin/dev/inmo/sdi/ModuleSerializer.kt
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package dev.inmo.sdi
|
||||||
|
|
||||||
|
import dev.inmo.sdi.utils.*
|
||||||
|
import dev.inmo.sdi.utils.resolveKClassByPackageName
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import kotlinx.serialization.json.*
|
||||||
|
import kotlinx.serialization.modules.*
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@InternalSerializationApi
|
||||||
|
private fun <T : Any> T.serialize(encoder: Encoder) = (
|
||||||
|
this::class.serializer() as KSerializer<T>
|
||||||
|
).serialize(encoder, this)
|
||||||
|
|
||||||
|
private fun JsonElement.detectType(valueKey: String) = when (this) {
|
||||||
|
is JsonObject -> this["type"] ?.jsonPrimitive ?.contentOrNull
|
||||||
|
is JsonPrimitive -> contentOrNull
|
||||||
|
is JsonArray -> getOrNull(0) ?.jsonPrimitive ?.contentOrNull
|
||||||
|
} ?: valueKey
|
||||||
|
|
||||||
|
@InternalSerializationApi
|
||||||
|
private data class TypeSerializer<T : Any>(
|
||||||
|
val serializersModule: SerializersModuleBuilder,
|
||||||
|
private val kClass: KClass<T>,
|
||||||
|
private val otherDependencyResolver: (String) -> Any?
|
||||||
|
) : KSerializer<T> {
|
||||||
|
private val deserializedByLink = mutableMapOf<T, String>()
|
||||||
|
private val jsonElementSerializer = JsonElement.serializer()
|
||||||
|
@InternalSerializationApi
|
||||||
|
private val originalSerializer = kClass.serializer()
|
||||||
|
override val descriptor: SerialDescriptor
|
||||||
|
get() = jsonElementSerializer.descriptor
|
||||||
|
|
||||||
|
init {
|
||||||
|
fun <T : Any> KClass<T>.contextual() {
|
||||||
|
serializersModule.optionalContextual(this, this@TypeSerializer as KSerializer<T>)
|
||||||
|
}
|
||||||
|
serializersModule.contextual(kClass, this)
|
||||||
|
kClass.superclasses.forEach {
|
||||||
|
it.contextual()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@InternalSerializationApi
|
||||||
|
override fun deserialize(decoder: Decoder): T {
|
||||||
|
return when (val element = jsonElementSerializer.deserialize(decoder)) {
|
||||||
|
is JsonPrimitive -> (otherDependencyResolver(element.content) as T).also {
|
||||||
|
deserializedByLink[it] = element.content
|
||||||
|
}
|
||||||
|
else -> ((decoder as? JsonDecoder) ?.json ?: nonStrictJson).decodeFromJsonElement(
|
||||||
|
originalSerializer,
|
||||||
|
when (element) {
|
||||||
|
is JsonArray -> element[1].jsonObject
|
||||||
|
else -> element
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@InternalSerializationApi
|
||||||
|
override fun serialize(encoder: Encoder, value: T) {
|
||||||
|
deserializedByLink[value] ?.also {
|
||||||
|
encoder.encodeSerializableValue(JsonPrimitive.serializer(), JsonPrimitive(it))
|
||||||
|
} ?: value.serialize(encoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InternalSerializationApi
|
||||||
|
fun forceSerialization(json: Json, value: T) = json.encodeToJsonElement(originalSerializer, value).let { encoded ->
|
||||||
|
when (encoded) {
|
||||||
|
is JsonObject -> JsonObject(
|
||||||
|
encoded + ("type" to JsonPrimitive(value::class.qualifiedName))
|
||||||
|
)
|
||||||
|
is JsonArray -> JsonArray(
|
||||||
|
listOf(JsonPrimitive(value::class.qualifiedName)) + encoded
|
||||||
|
)
|
||||||
|
else -> encoded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal data class SerializationContext(
|
||||||
|
val json: Json,
|
||||||
|
val keysSerializers: Map<String, KSerializer<*>>
|
||||||
|
) {
|
||||||
|
@InternalSerializationApi
|
||||||
|
fun <T : Any> serialize(key: String, value: T) = (keysSerializers.getValue(key) as TypeSerializer<T>).let {
|
||||||
|
it.forceSerialization(json, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializer(Module::class)
|
||||||
|
class ModuleSerializer(
|
||||||
|
private val moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null,
|
||||||
|
private vararg val additionalClassesToInclude: KClass<*>
|
||||||
|
) : KSerializer<Module> {
|
||||||
|
private val jsonObjectSerializer = JsonObject.serializer()
|
||||||
|
override val descriptor: SerialDescriptor = jsonObjectSerializer.descriptor
|
||||||
|
|
||||||
|
@InternalSerializationApi
|
||||||
|
override fun deserialize(decoder: Decoder): Module {
|
||||||
|
val json = jsonObjectSerializer.deserialize(decoder)
|
||||||
|
lateinit var newFormat: Json
|
||||||
|
|
||||||
|
val cacheMap = mutableMapOf<String, Any>()
|
||||||
|
val serializers = mutableMapOf<String, KSerializer<*>>()
|
||||||
|
val dependencyResolver: (String) -> Any? = {
|
||||||
|
cacheMap[it] ?: newFormat.decodeFromJsonElement(
|
||||||
|
serializers.getValue(it),
|
||||||
|
json.getValue(it)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newSerializersModule = decoder.serializersModule.overwriteWith(
|
||||||
|
SerializersModule {
|
||||||
|
moduleBuilder ?.invoke(this)
|
||||||
|
json.forEach { (key, value) ->
|
||||||
|
val kclass = resolveKClassByPackageName(value.detectType(key))
|
||||||
|
serializers[key] = TypeSerializer(this, kclass, dependencyResolver)
|
||||||
|
}
|
||||||
|
additionalClassesToInclude.forEach {
|
||||||
|
TypeSerializer(this, it, dependencyResolver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
newFormat = Json((decoder as? JsonDecoder) ?.json ?: nonStrictJson) {
|
||||||
|
serializersModule = newSerializersModule
|
||||||
|
}
|
||||||
|
|
||||||
|
return Module(
|
||||||
|
json.mapNotNull { (key) ->
|
||||||
|
key to (dependencyResolver(key) ?: return@mapNotNull null)
|
||||||
|
}.toMap(),
|
||||||
|
SerializationContext(newFormat, serializers.toMap())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InternalSerializationApi
|
||||||
|
override fun serialize(encoder: Encoder, value: Module) {
|
||||||
|
val serialContext = value.serialContext
|
||||||
|
jsonObjectSerializer.serialize(
|
||||||
|
encoder,
|
||||||
|
JsonObject(
|
||||||
|
value.map { (key, data) ->
|
||||||
|
key to serialContext.serialize(key, data)
|
||||||
|
}.toMap()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val DefaultModuleSerializer = ModuleSerializer()
|
@ -1,69 +0,0 @@
|
|||||||
package dev.inmo.sdi.utils
|
|
||||||
|
|
||||||
import kotlinx.serialization.*
|
|
||||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
|
||||||
import kotlinx.serialization.encoding.Decoder
|
|
||||||
import kotlinx.serialization.encoding.Encoder
|
|
||||||
import kotlinx.serialization.json.*
|
|
||||||
import kotlinx.serialization.modules.SerializersModuleBuilder
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
internal object AlreadyRegisteredException : Exception()
|
|
||||||
|
|
||||||
internal class DependencyResolver<T : Any>(
|
|
||||||
serialModuleBuilder: SerializersModuleBuilder,
|
|
||||||
kClass: KClass<T>,
|
|
||||||
private val formatterGetter: () -> Json,
|
|
||||||
private val dependencyGetter: (String) -> Any
|
|
||||||
) : KSerializer<T> {
|
|
||||||
@InternalSerializationApi
|
|
||||||
private val originalSerializer: KSerializer<T> = kClass.serializerOrNull() ?: ContextualSerializer(kClass)
|
|
||||||
private val objectsCache = mutableMapOf<String, T>()
|
|
||||||
@InternalSerializationApi
|
|
||||||
override val descriptor: SerialDescriptor = originalSerializer.descriptor
|
|
||||||
|
|
||||||
init {
|
|
||||||
serialModuleBuilder.apply {
|
|
||||||
try {
|
|
||||||
contextual(kClass, this@DependencyResolver)
|
|
||||||
} catch (e: IllegalArgumentException) {
|
|
||||||
throw AlreadyRegisteredException
|
|
||||||
}
|
|
||||||
kClass.allSubclasses.forEach { currentKClass ->
|
|
||||||
try {
|
|
||||||
DependencyResolver(serialModuleBuilder, currentKClass, formatterGetter, dependencyGetter)
|
|
||||||
} catch (e: AlreadyRegisteredException) {
|
|
||||||
// ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@InternalSerializationApi
|
|
||||||
override fun deserialize(decoder: Decoder): T {
|
|
||||||
val decoded = decoder.decodeSerializableValue(JsonElement.serializer())
|
|
||||||
return when {
|
|
||||||
decoded is JsonPrimitive && decoded.contentOrNull != null -> decoded.content.let { dependencyName ->
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
(dependencyGetter(dependencyName) as T).also {
|
|
||||||
objectsCache[dependencyName] = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
decoded is JsonArray -> {
|
|
||||||
val serializer = resolveSerializerByPackageName(decoded[0].jsonPrimitive.content)
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
formatterGetter().decodeFromJsonElement(serializer, decoded[1]) as T
|
|
||||||
}
|
|
||||||
else -> formatterGetter().decodeFromJsonElement(originalSerializer, decoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@InternalSerializationApi
|
|
||||||
override fun serialize(encoder: Encoder, value: T) {
|
|
||||||
objectsCache.keys.firstOrNull {
|
|
||||||
objectsCache[it] === value
|
|
||||||
} ?.also { dependencyName ->
|
|
||||||
encoder.encodeString(dependencyName)
|
|
||||||
} ?: originalSerializer.serialize(encoder, value)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,126 +0,0 @@
|
|||||||
package dev.inmo.sdi.utils
|
|
||||||
|
|
||||||
import dev.inmo.sdi.getClassesForIncludingInSDI
|
|
||||||
import kotlinx.serialization.InternalSerializationApi
|
|
||||||
import kotlinx.serialization.encoding.Decoder
|
|
||||||
import kotlinx.serialization.json.*
|
|
||||||
import kotlinx.serialization.modules.*
|
|
||||||
import kotlin.reflect.KClass
|
|
||||||
|
|
||||||
private typealias PackageOrOtherDependencyNamePair = Pair<String?, String?>
|
|
||||||
|
|
||||||
private val namesToTheirClasses = getClassesForIncludingInSDI().flatMap {
|
|
||||||
(it.second + it.first.qualifiedName!!).map { name ->
|
|
||||||
name to it.first.qualifiedName!!
|
|
||||||
}
|
|
||||||
}.toMap()
|
|
||||||
|
|
||||||
private fun JsonElement.resolvePackageName(currentKey: String, otherDependenciesKeys: Set<String>): PackageOrOtherDependencyNamePair {
|
|
||||||
return when (this) {
|
|
||||||
is JsonPrimitive -> contentOrNull ?.let {
|
|
||||||
if (it in otherDependenciesKeys) {
|
|
||||||
null to it
|
|
||||||
} else {
|
|
||||||
it to null
|
|
||||||
}
|
|
||||||
} ?: throw IllegalArgumentException("Value on dependency name \"$currentKey\" is invalid: provided $this, but expected package name or other dependency name string")
|
|
||||||
is JsonObject -> if (currentKey in otherDependenciesKeys) {
|
|
||||||
null to currentKey
|
|
||||||
} else {
|
|
||||||
(get("type") ?.jsonPrimitive ?.contentOrNull ?: namesToTheirClasses[currentKey] ?: currentKey) to null
|
|
||||||
}
|
|
||||||
is JsonArray -> return get(0).jsonPrimitive.contentOrNull ?.let { (namesToTheirClasses[it] ?: it) to null } ?: throw IllegalArgumentException("Value on first argument of dependency value must be its package as a string, but was provided ${get(0)}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@InternalSerializationApi
|
|
||||||
internal fun createModuleBasedOnConfigRoot(
|
|
||||||
baseJsonFormat: Json?,
|
|
||||||
jsonObject: JsonObject,
|
|
||||||
moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null,
|
|
||||||
baseContext: SerializersModule,
|
|
||||||
vararg additionalClassesToInclude: KClass<*>
|
|
||||||
): Json {
|
|
||||||
lateinit var caches: Map<String, () -> Any>
|
|
||||||
lateinit var jsonStringFormat: Json
|
|
||||||
caches = jsonObject.keys.map { key ->
|
|
||||||
key to callback@{
|
|
||||||
val elemValue = jsonObject[key] ?: throw IllegalStateException("Value for key $key must be provided, but was not")
|
|
||||||
|
|
||||||
val packageName: String = elemValue.resolvePackageName(key, jsonObject.keys).let { (packageName, otherDependencyName) ->
|
|
||||||
when {
|
|
||||||
packageName != null -> packageName
|
|
||||||
otherDependencyName != null -> return@callback caches.getValue(otherDependencyName).invoke()
|
|
||||||
else -> throw IllegalStateException("Internal error: can't resolve other dependency name and package name for key $key")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val argumentsObject = when (elemValue) {
|
|
||||||
is JsonPrimitive -> {
|
|
||||||
elemValue.contentOrNull ?.let { _ ->
|
|
||||||
JsonObject(emptyMap())
|
|
||||||
} ?: throw IllegalArgumentException("Value on dependency name \"$key\" is invalid: provided $elemValue, but expected package name or other dependency name string")
|
|
||||||
}
|
|
||||||
is JsonObject -> {
|
|
||||||
elemValue
|
|
||||||
}
|
|
||||||
is JsonArray -> {
|
|
||||||
if (elemValue.size > 1) {
|
|
||||||
elemValue[1].jsonObject
|
|
||||||
} else {
|
|
||||||
JsonObject(emptyMap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val serializer = resolveSerializerByPackageName(packageName)
|
|
||||||
return@callback jsonStringFormat.decodeFromJsonElement(serializer, argumentsObject) as Any
|
|
||||||
}
|
|
||||||
}.toMap()
|
|
||||||
|
|
||||||
val keysToPackages: Map<String, String> = jsonObject.mapNotNull { (key, element) ->
|
|
||||||
val packageName = element.resolvePackageName(key, jsonObject.keys).first ?: return@mapNotNull null
|
|
||||||
key to packageName
|
|
||||||
}.toMap()
|
|
||||||
|
|
||||||
val context = baseContext.overwriteWith(
|
|
||||||
SerializersModule {
|
|
||||||
keysToPackages.values.forEach {
|
|
||||||
val kclass = resolveKClassByPackageName(it)
|
|
||||||
|
|
||||||
try {
|
|
||||||
DependencyResolver(
|
|
||||||
this,
|
|
||||||
kclass,
|
|
||||||
{ jsonStringFormat }
|
|
||||||
) {
|
|
||||||
caches.getValue(it).invoke()
|
|
||||||
}
|
|
||||||
} catch (e: AlreadyRegisteredException) {
|
|
||||||
// here we are thinking that already registered
|
|
||||||
}
|
|
||||||
}
|
|
||||||
additionalClassesToInclude.forEach {
|
|
||||||
try {
|
|
||||||
DependencyResolver(
|
|
||||||
this,
|
|
||||||
it,
|
|
||||||
{ jsonStringFormat }
|
|
||||||
) {
|
|
||||||
caches.getValue(it).invoke()
|
|
||||||
}
|
|
||||||
} catch (e: AlreadyRegisteredException) {
|
|
||||||
// here we are thinking that already registered
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (moduleBuilder != null) {
|
|
||||||
moduleBuilder()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return Json(baseJsonFormat ?: Json.Default) {
|
|
||||||
useArrayPolymorphism = true
|
|
||||||
serializersModule = context
|
|
||||||
}.also {
|
|
||||||
jsonStringFormat = it
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,9 +12,6 @@ fun <T : Any> SerializersModuleBuilder.optionalContextual(
|
|||||||
true
|
true
|
||||||
} catch (e: SerializationException) {
|
} catch (e: SerializationException) {
|
||||||
false
|
false
|
||||||
|
} catch (e: IllegalArgumentException) { // can be a SerializerAlreadyRegisteredException
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@InternalSerializationApi
|
|
||||||
inline fun <reified T : Any> SerializersModuleBuilder.optionalContextual(
|
|
||||||
kSerializer: KSerializer<T>
|
|
||||||
) = optionalContextual(T::class, kSerializer)
|
|
@ -1,22 +1,13 @@
|
|||||||
package dev.inmo.sdi.utils
|
package dev.inmo.sdi.utils
|
||||||
|
|
||||||
import kotlinx.serialization.*
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
|
|
||||||
internal expect fun resolveKClassByPackageName(packageName: String): KClass<*>
|
internal expect fun resolveKClassByPackageName(packageName: String): KClass<*>
|
||||||
|
|
||||||
@InternalSerializationApi
|
|
||||||
internal fun <T : Any> resolveSerializerByKClass(kClass: KClass<T>): KSerializer<T> = kClass.serializer()
|
|
||||||
|
|
||||||
@InternalSerializationApi
|
|
||||||
internal fun resolveSerializerByPackageName(packageName: String): KSerializer<*> = resolveSerializerByKClass(
|
|
||||||
resolveKClassByPackageName(packageName)
|
|
||||||
)
|
|
||||||
|
|
||||||
internal expect val <T : Any> KClass<T>.supertypes: List<KType>
|
internal expect val <T : Any> KClass<T>.supertypes: List<KType>
|
||||||
|
|
||||||
internal val KClass<*>.allSubclasses: Set<KClass<*>>
|
internal val KClass<*>.superclasses: Set<KClass<*>>
|
||||||
get() {
|
get() {
|
||||||
val subclasses = mutableSetOf<KClass<*>>()
|
val subclasses = mutableSetOf<KClass<*>>()
|
||||||
val leftToVisit = supertypes.mapNotNull { it.classifier as? KClass<*> }.toMutableList()
|
val leftToVisit = supertypes.mapNotNull { it.classifier as? KClass<*> }.toMutableList()
|
||||||
@ -24,7 +15,7 @@ internal val KClass<*>.allSubclasses: Set<KClass<*>>
|
|||||||
val top = leftToVisit.removeAt(0)
|
val top = leftToVisit.removeAt(0)
|
||||||
if (subclasses.add(top)) {
|
if (subclasses.add(top)) {
|
||||||
leftToVisit.addAll(
|
leftToVisit.addAll(
|
||||||
top.allSubclasses
|
top.superclasses
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package dev.inmo.sdi
|
package dev.inmo.sdi
|
||||||
|
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@ -12,9 +13,9 @@ interface List_ChildAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class List_Parent(override val services: List<@Contextual List_ChildAPI>) : List_ParentalAPI
|
data class List_Parent(override val services: List<@Contextual List_ChildAPI>) : List_ParentalAPI
|
||||||
@Serializable
|
@Serializable
|
||||||
class List_Child(override val names: List<String>) : List_ChildAPI
|
data class List_Child(override val names: List<String>) : List_ChildAPI
|
||||||
|
|
||||||
class ListTest {
|
class ListTest {
|
||||||
val servicesNum = 10
|
val servicesNum = 10
|
||||||
@ -50,5 +51,7 @@ class ListTest {
|
|||||||
controller.services.forEachIndexed { i, service ->
|
controller.services.forEachIndexed { i, service ->
|
||||||
assertEquals(names[i].second.toList(), service.names)
|
assertEquals(names[i].second.toList(), service.names)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testModuleSerialization(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ interface SimpleCustomObject_ServiceAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SimpleCustomObject_Controller(@Contextual val service: SimpleCustomObject_ServiceAPI) :
|
data class SimpleCustomObject_Controller(@Contextual val service: SimpleCustomObject_ServiceAPI) :
|
||||||
SimpleCustomObject_ControllerAPI {
|
SimpleCustomObject_ControllerAPI {
|
||||||
override fun showUp() {
|
override fun showUp() {
|
||||||
println("Inited with name \"${service.names}\"")
|
println("Inited with name \"${service.names}\"")
|
||||||
@ -20,7 +20,7 @@ class SimpleCustomObject_Controller(@Contextual val service: SimpleCustomObject_
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SimpleCustomObject_CustomController1(@Contextual val service: SimpleCustomObject_ServiceAPI) :
|
data class SimpleCustomObject_CustomController1(@Contextual val service: SimpleCustomObject_ServiceAPI) :
|
||||||
SimpleCustomObject_ControllerAPI {
|
SimpleCustomObject_ControllerAPI {
|
||||||
override fun showUp() {
|
override fun showUp() {
|
||||||
println("Inited with name \"${service.names}\"")
|
println("Inited with name \"${service.names}\"")
|
||||||
@ -28,7 +28,7 @@ class SimpleCustomObject_CustomController1(@Contextual val service: SimpleCustom
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SimpleCustomObject_CustomController2(@Contextual val service: SimpleCustomObject_BusinessService) :
|
data class SimpleCustomObject_CustomController2(@Contextual val service: SimpleCustomObject_BusinessService) :
|
||||||
SimpleCustomObject_ControllerAPI {
|
SimpleCustomObject_ControllerAPI {
|
||||||
override fun showUp() {
|
override fun showUp() {
|
||||||
println("Inited with name \"${service.names}\"")
|
println("Inited with name \"${service.names}\"")
|
||||||
@ -36,16 +36,16 @@ class SimpleCustomObject_CustomController2(@Contextual val service: SimpleCustom
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class SimpleCustomObject_CustomController3(@Contextual val service: SimpleCustomObject_ServiceAPI) :
|
data class SimpleCustomObject_CustomController3(@Contextual val service: SimpleCustomObject_ServiceAPI) :
|
||||||
SimpleCustomObject_ControllerAPI {
|
SimpleCustomObject_ControllerAPI {
|
||||||
override fun showUp() {
|
override fun showUp() {
|
||||||
println("Inited with name \"${service.names}\"")
|
println("Inited with name \"${service.names}\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Serializable
|
@Serializable
|
||||||
class SimpleCustomObject_BusinessService(override val names: List<String>) : SimpleCustomObject_ServiceAPI
|
data class SimpleCustomObject_BusinessService(override val names: List<String>) : SimpleCustomObject_ServiceAPI
|
||||||
@Serializable
|
@Serializable
|
||||||
class SimpleCustomObject_BusinessService1(override val names: List<String>) : SimpleCustomObject_ServiceAPI
|
data class SimpleCustomObject_BusinessService1(override val names: List<String>) : SimpleCustomObject_ServiceAPI
|
||||||
|
|
||||||
class SimpleCustomObjectTest {
|
class SimpleCustomObjectTest {
|
||||||
@InternalSerializationApi
|
@InternalSerializationApi
|
||||||
@ -118,5 +118,7 @@ class SimpleCustomObjectTest {
|
|||||||
(module[customController2Name] as SimpleCustomObject_ControllerAPI)
|
(module[customController2Name] as SimpleCustomObject_ControllerAPI)
|
||||||
val customController2 = (module[customController2Name] as SimpleCustomObject_CustomController2)
|
val customController2 = (module[customController2Name] as SimpleCustomObject_CustomController2)
|
||||||
assertEquals(customNames.toList(), customController2.service.names)
|
assertEquals(customNames.toList(), customController2.service.names)
|
||||||
|
|
||||||
|
testModuleSerialization(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,13 @@ interface Simple_ServiceAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class Simple_Controller(@Contextual val service: Simple_ServiceAPI) : Simple_ControllerAPI {
|
data class Simple_Controller(@Contextual val service: Simple_ServiceAPI) : Simple_ControllerAPI {
|
||||||
override fun showUp() {
|
override fun showUp() {
|
||||||
println("Inited with name \"${service.names}\"")
|
println("Inited with name \"${service.names}\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Serializable
|
@Serializable
|
||||||
class Simple_BusinessService(override val names: List<String>) : Simple_ServiceAPI
|
data class Simple_BusinessService(override val names: List<String>) : Simple_ServiceAPI
|
||||||
|
|
||||||
class SimpleTest {
|
class SimpleTest {
|
||||||
@InternalSerializationApi
|
@InternalSerializationApi
|
||||||
@ -46,5 +46,7 @@ class SimpleTest {
|
|||||||
(module[controllerName] as Simple_ControllerAPI)
|
(module[controllerName] as Simple_ControllerAPI)
|
||||||
val controller = (module["controller"] as Simple_Controller)
|
val controller = (module["controller"] as Simple_Controller)
|
||||||
assertEquals(names.toList(), controller.service.names)
|
assertEquals(names.toList(), controller.service.names)
|
||||||
|
|
||||||
|
testModuleSerialization(module)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package dev.inmo.sdi
|
||||||
|
|
||||||
|
import kotlinx.serialization.InternalSerializationApi
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
@InternalSerializationApi
|
||||||
|
fun testModuleSerialization(
|
||||||
|
module: Module
|
||||||
|
) {
|
||||||
|
val serializedModule = loadModule(nonStrictJson.encodeToString(DefaultModuleSerializer, module))
|
||||||
|
module.forEach { (key, value) ->
|
||||||
|
assertEquals(value, serializedModule.getValue(key))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user