diff --git a/src/commonMain/kotlin/dev/inmo/sdi/HelperTools.kt b/src/commonMain/kotlin/dev/inmo/sdi/HelperTools.kt index 8a04b64..145f4f9 100644 --- a/src/commonMain/kotlin/dev/inmo/sdi/HelperTools.kt +++ b/src/commonMain/kotlin/dev/inmo/sdi/HelperTools.kt @@ -21,9 +21,9 @@ fun Json.loadModule( moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null ): Module = decodeFromString( if (moduleBuilder != null) { - ModuleDeserializerStrategy(moduleBuilder, *additionalClassesToInclude) + ModuleSerializer(moduleBuilder, *additionalClassesToInclude) } else { - ModuleDeserializerStrategy(additionalClassesToInclude = *additionalClassesToInclude) + ModuleSerializer(additionalClassesToInclude = *additionalClassesToInclude) }, json ) diff --git a/src/commonMain/kotlin/dev/inmo/sdi/Module.kt b/src/commonMain/kotlin/dev/inmo/sdi/Module.kt index 60a8b9a..de9ae6d 100644 --- a/src/commonMain/kotlin/dev/inmo/sdi/Module.kt +++ b/src/commonMain/kotlin/dev/inmo/sdi/Module.kt @@ -3,5 +3,8 @@ package dev.inmo.sdi import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable -@Serializable(ModuleFullSerializer::class) -class Module internal constructor(base: Map) : Map by base \ No newline at end of file +@Serializable(ModuleSerializer::class) +class Module internal constructor( + base: Map, + internal val serialContext: SerializationContext +) : Map by base \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/inmo/sdi/ModuleDeserializerStrategy.kt b/src/commonMain/kotlin/dev/inmo/sdi/ModuleDeserializerStrategy.kt deleted file mode 100644 index 33e085b..0000000 --- a/src/commonMain/kotlin/dev/inmo/sdi/ModuleDeserializerStrategy.kt +++ /dev/null @@ -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 { - 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, - DeserializationStrategy 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") -} diff --git a/src/commonMain/kotlin/dev/inmo/sdi/ModuleSerializer.kt b/src/commonMain/kotlin/dev/inmo/sdi/ModuleSerializer.kt new file mode 100644 index 0000000..ee35f16 --- /dev/null +++ b/src/commonMain/kotlin/dev/inmo/sdi/ModuleSerializer.kt @@ -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.serialize(encoder: Encoder) = ( + this::class.serializer() as KSerializer +).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( + val serializersModule: SerializersModuleBuilder, + private val kClass: KClass, + private val otherDependencyResolver: (String) -> Any? +) : KSerializer { + private val deserializedByLink = mutableMapOf() + private val jsonElementSerializer = JsonElement.serializer() + @InternalSerializationApi + private val originalSerializer = kClass.serializer() + override val descriptor: SerialDescriptor + get() = jsonElementSerializer.descriptor + + init { + fun KClass.contextual() { + serializersModule.optionalContextual(this, this@TypeSerializer as KSerializer) + } + 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> +) { + @InternalSerializationApi + fun serialize(key: String, value: T) = (keysSerializers.getValue(key) as TypeSerializer).let { + it.forceSerialization(json, value) + } +} + +@Serializer(Module::class) +class ModuleSerializer( + private val moduleBuilder: (SerializersModuleBuilder.() -> Unit)? = null, + private vararg val additionalClassesToInclude: KClass<*> +) : KSerializer { + 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() + val serializers = mutableMapOf>() + 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() diff --git a/src/commonMain/kotlin/dev/inmo/sdi/utils/DependencyResolver.kt b/src/commonMain/kotlin/dev/inmo/sdi/utils/DependencyResolver.kt deleted file mode 100644 index 50bda14..0000000 --- a/src/commonMain/kotlin/dev/inmo/sdi/utils/DependencyResolver.kt +++ /dev/null @@ -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( - serialModuleBuilder: SerializersModuleBuilder, - kClass: KClass, - private val formatterGetter: () -> Json, - private val dependencyGetter: (String) -> Any -) : KSerializer { - @InternalSerializationApi - private val originalSerializer: KSerializer = kClass.serializerOrNull() ?: ContextualSerializer(kClass) - private val objectsCache = mutableMapOf() - @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) - } -} diff --git a/src/commonMain/kotlin/dev/inmo/sdi/utils/JsonAdaptations.kt b/src/commonMain/kotlin/dev/inmo/sdi/utils/JsonAdaptations.kt deleted file mode 100644 index 689efe2..0000000 --- a/src/commonMain/kotlin/dev/inmo/sdi/utils/JsonAdaptations.kt +++ /dev/null @@ -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 - -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): 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 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 = 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 - } -} diff --git a/src/commonMain/kotlin/dev/inmo/sdi/utils/OptionalContextualIncluding.kt b/src/commonMain/kotlin/dev/inmo/sdi/utils/OptionalContextualIncluding.kt index e6e315e..d07c1ad 100644 --- a/src/commonMain/kotlin/dev/inmo/sdi/utils/OptionalContextualIncluding.kt +++ b/src/commonMain/kotlin/dev/inmo/sdi/utils/OptionalContextualIncluding.kt @@ -12,9 +12,6 @@ fun SerializersModuleBuilder.optionalContextual( true } catch (e: SerializationException) { false +} catch (e: IllegalArgumentException) { // can be a SerializerAlreadyRegisteredException + false } - -@InternalSerializationApi -inline fun SerializersModuleBuilder.optionalContextual( - kSerializer: KSerializer -) = optionalContextual(T::class, kSerializer) \ No newline at end of file diff --git a/src/commonMain/kotlin/dev/inmo/sdi/utils/TypesHelper.kt b/src/commonMain/kotlin/dev/inmo/sdi/utils/TypesHelper.kt index 06210f0..229fe99 100644 --- a/src/commonMain/kotlin/dev/inmo/sdi/utils/TypesHelper.kt +++ b/src/commonMain/kotlin/dev/inmo/sdi/utils/TypesHelper.kt @@ -1,22 +1,13 @@ package dev.inmo.sdi.utils -import kotlinx.serialization.* import kotlin.reflect.KClass import kotlin.reflect.KType internal expect fun resolveKClassByPackageName(packageName: String): KClass<*> -@InternalSerializationApi -internal fun resolveSerializerByKClass(kClass: KClass): KSerializer = kClass.serializer() - -@InternalSerializationApi -internal fun resolveSerializerByPackageName(packageName: String): KSerializer<*> = resolveSerializerByKClass( - resolveKClassByPackageName(packageName) -) - internal expect val KClass.supertypes: List -internal val KClass<*>.allSubclasses: Set> +internal val KClass<*>.superclasses: Set> get() { val subclasses = mutableSetOf>() val leftToVisit = supertypes.mapNotNull { it.classifier as? KClass<*> }.toMutableList() @@ -24,7 +15,7 @@ internal val KClass<*>.allSubclasses: Set> val top = leftToVisit.removeAt(0) if (subclasses.add(top)) { leftToVisit.addAll( - top.allSubclasses + top.superclasses ) } } diff --git a/src/commonTest/kotlin/dev/inmo/sdi/ListTest.kt b/src/commonTest/kotlin/dev/inmo/sdi/ListTest.kt index e22ca4c..1309463 100644 --- a/src/commonTest/kotlin/dev/inmo/sdi/ListTest.kt +++ b/src/commonTest/kotlin/dev/inmo/sdi/ListTest.kt @@ -1,6 +1,7 @@ package dev.inmo.sdi import kotlinx.serialization.* +import kotlinx.serialization.json.JsonObject import kotlin.test.Test import kotlin.test.assertEquals @@ -12,9 +13,9 @@ interface List_ChildAPI { } @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 -class List_Child(override val names: List) : List_ChildAPI +data class List_Child(override val names: List) : List_ChildAPI class ListTest { val servicesNum = 10 @@ -50,5 +51,7 @@ class ListTest { controller.services.forEachIndexed { i, service -> assertEquals(names[i].second.toList(), service.names) } + + testModuleSerialization(module) } } diff --git a/src/commonTest/kotlin/dev/inmo/sdi/SimpleCustomObjectTest.kt b/src/commonTest/kotlin/dev/inmo/sdi/SimpleCustomObjectTest.kt index 9f5f1c0..af533c4 100644 --- a/src/commonTest/kotlin/dev/inmo/sdi/SimpleCustomObjectTest.kt +++ b/src/commonTest/kotlin/dev/inmo/sdi/SimpleCustomObjectTest.kt @@ -12,7 +12,7 @@ interface SimpleCustomObject_ServiceAPI { } @Serializable -class SimpleCustomObject_Controller(@Contextual val service: SimpleCustomObject_ServiceAPI) : +data class SimpleCustomObject_Controller(@Contextual val service: SimpleCustomObject_ServiceAPI) : SimpleCustomObject_ControllerAPI { override fun showUp() { println("Inited with name \"${service.names}\"") @@ -20,7 +20,7 @@ class SimpleCustomObject_Controller(@Contextual val service: SimpleCustomObject_ } @Serializable -class SimpleCustomObject_CustomController1(@Contextual val service: SimpleCustomObject_ServiceAPI) : +data class SimpleCustomObject_CustomController1(@Contextual val service: SimpleCustomObject_ServiceAPI) : SimpleCustomObject_ControllerAPI { override fun showUp() { println("Inited with name \"${service.names}\"") @@ -28,7 +28,7 @@ class SimpleCustomObject_CustomController1(@Contextual val service: SimpleCustom } @Serializable -class SimpleCustomObject_CustomController2(@Contextual val service: SimpleCustomObject_BusinessService) : +data class SimpleCustomObject_CustomController2(@Contextual val service: SimpleCustomObject_BusinessService) : SimpleCustomObject_ControllerAPI { override fun showUp() { println("Inited with name \"${service.names}\"") @@ -36,16 +36,16 @@ class SimpleCustomObject_CustomController2(@Contextual val service: SimpleCustom } @Serializable -class SimpleCustomObject_CustomController3(@Contextual val service: SimpleCustomObject_ServiceAPI) : +data class SimpleCustomObject_CustomController3(@Contextual val service: SimpleCustomObject_ServiceAPI) : SimpleCustomObject_ControllerAPI { override fun showUp() { println("Inited with name \"${service.names}\"") } } @Serializable -class SimpleCustomObject_BusinessService(override val names: List) : SimpleCustomObject_ServiceAPI +data class SimpleCustomObject_BusinessService(override val names: List) : SimpleCustomObject_ServiceAPI @Serializable -class SimpleCustomObject_BusinessService1(override val names: List) : SimpleCustomObject_ServiceAPI +data class SimpleCustomObject_BusinessService1(override val names: List) : SimpleCustomObject_ServiceAPI class SimpleCustomObjectTest { @InternalSerializationApi @@ -118,5 +118,7 @@ class SimpleCustomObjectTest { (module[customController2Name] as SimpleCustomObject_ControllerAPI) val customController2 = (module[customController2Name] as SimpleCustomObject_CustomController2) assertEquals(customNames.toList(), customController2.service.names) + + testModuleSerialization(module) } } diff --git a/src/commonTest/kotlin/dev/inmo/sdi/SimpleTest.kt b/src/commonTest/kotlin/dev/inmo/sdi/SimpleTest.kt index 4d66dcd..12fcd05 100644 --- a/src/commonTest/kotlin/dev/inmo/sdi/SimpleTest.kt +++ b/src/commonTest/kotlin/dev/inmo/sdi/SimpleTest.kt @@ -12,13 +12,13 @@ interface Simple_ServiceAPI { } @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() { println("Inited with name \"${service.names}\"") } } @Serializable -class Simple_BusinessService(override val names: List) : Simple_ServiceAPI +data class Simple_BusinessService(override val names: List) : Simple_ServiceAPI class SimpleTest { @InternalSerializationApi @@ -46,5 +46,7 @@ class SimpleTest { (module[controllerName] as Simple_ControllerAPI) val controller = (module["controller"] as Simple_Controller) assertEquals(names.toList(), controller.service.names) + + testModuleSerialization(module) } } diff --git a/src/commonTest/kotlin/dev/inmo/sdi/TestModuleSerialization.kt b/src/commonTest/kotlin/dev/inmo/sdi/TestModuleSerialization.kt new file mode 100644 index 0000000..3b8ec59 --- /dev/null +++ b/src/commonTest/kotlin/dev/inmo/sdi/TestModuleSerialization.kt @@ -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)) + } +}