From 34cfe9e16edfad2827317c65f755df51575474ee Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 5 Aug 2024 16:49:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(api):=20ApplicationBuilder=E4=B8=AD?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8F=AF=E9=85=8D=E7=BD=AE=E9=A1=B9=20`seria?= =?UTF-8?q?lizersModule`=20=E4=BB=A5=E5=85=81=E8=AE=B8=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E8=87=AA=E5=AE=9A=E4=B9=89=E7=9A=84=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E5=8C=96=E6=A8=A1=E5=9D=97'=E5=9F=BA=E5=BA=95'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- simbot-api/api/simbot-api.api | 7 + .../application/ApplicationConfiguration.kt | 28 +++- .../love/forte/simbot/component/Components.kt | 22 ++- .../src/commonTest/kotlin/ComponentTests.kt | 3 +- .../src/main/java/module-info.java | 1 + .../common/application/SpringApplication.kt | 5 +- .../src/main/java/module-info.java | 1 + .../spring2/application/SpringApplication.kt | 4 +- .../src/main/java/module-info.java | 1 + .../spring/application/SpringApplication.kt | 4 +- .../application/SimpleApplicationFactory.kt | 16 +- .../ApplicationSerializersModuleTests.kt | 157 ++++++++++++++++++ .../src/jvmMain/java/module-info.java | 1 + 13 files changed, 235 insertions(+), 15 deletions(-) create mode 100644 simbot-cores/simbot-core/src/commonTest/kotlin/love/forte/simbot/core/application/ApplicationSerializersModuleTests.kt diff --git a/simbot-api/api/simbot-api.api b/simbot-api/api/simbot-api.api index bda89c92b..bab58399d 100644 --- a/simbot-api/api/simbot-api.api +++ b/simbot-api/api/simbot-api.api @@ -144,7 +144,9 @@ public final class love/forte/simbot/ability/StandardDeleteOption$StandardAnalys public abstract class love/forte/simbot/application/AbstractApplicationBuilder : love/forte/simbot/application/ApplicationBuilder { public fun ()V public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; + public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; public fun setCoroutineContext (Lkotlin/coroutines/CoroutineContext;)V + public fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V } public abstract class love/forte/simbot/application/AbstractApplicationEventRegistrar : love/forte/simbot/application/ApplicationEventRegistrar { @@ -189,11 +191,14 @@ public abstract interface class love/forte/simbot/application/Application : kotl public abstract interface class love/forte/simbot/application/ApplicationBuilder { public abstract fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; + public abstract fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; public abstract fun setCoroutineContext (Lkotlin/coroutines/CoroutineContext;)V + public abstract fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V } public abstract interface class love/forte/simbot/application/ApplicationConfiguration { public abstract fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; + public abstract fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; } public abstract interface class love/forte/simbot/application/ApplicationEventHandler { @@ -847,6 +852,8 @@ public abstract interface class love/forte/simbot/component/ComponentInstaller { public final class love/forte/simbot/component/ComponentUtil { public static final fun toComponents (Ljava/util/Collection;)Llove/forte/simbot/component/Components; + public static final fun toComponents (Ljava/util/Collection;Lkotlinx/serialization/modules/SerializersModule;)Llove/forte/simbot/component/Components; + public static synthetic fun toComponents$default (Ljava/util/Collection;Lkotlinx/serialization/modules/SerializersModule;ILjava/lang/Object;)Llove/forte/simbot/component/Components; } public abstract interface class love/forte/simbot/component/Components : java/util/Collection, kotlin/jvm/internal/markers/KMappedMarker { diff --git a/simbot-api/src/commonMain/kotlin/love/forte/simbot/application/ApplicationConfiguration.kt b/simbot-api/src/commonMain/kotlin/love/forte/simbot/application/ApplicationConfiguration.kt index c5b9058f1..38474b07a 100644 --- a/simbot-api/src/commonMain/kotlin/love/forte/simbot/application/ApplicationConfiguration.kt +++ b/simbot-api/src/commonMain/kotlin/love/forte/simbot/application/ApplicationConfiguration.kt @@ -4,7 +4,7 @@ * Project https://github.com/simple-robot/simpler-robot * Email ForteScarlet@163.com * - * This file is part of the Simple Robot Library. + * This file is part of the Simple Robot Library (Alias: simple-robot, simbot, etc.). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -24,7 +24,11 @@ package love.forte.simbot.application import kotlinx.coroutines.Job +import kotlinx.serialization.modules.EmptySerializersModule +import kotlinx.serialization.modules.SerializersModule import love.forte.simbot.common.coroutines.linkTo +import love.forte.simbot.component.Components +import love.forte.simbot.plugin.Plugins import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -51,6 +55,13 @@ public interface ApplicationConfiguration { */ public val coroutineContext: CoroutineContext + /** + * [ApplicationBuilder.serializersModule] 所配置的**基础**序列化模块信息。 + * 不包含 [Components] 中的所有其他组件合并后的结果。 + * + * @since 4.5.0 + */ + public val serializersModule: SerializersModule } /** @@ -67,6 +78,20 @@ public interface ApplicationBuilder { * @see ApplicationConfiguration.coroutineContext */ public var coroutineContext: CoroutineContext + + /** + * 一个用于 [Components.serializersModule] 的基础序列化模块, + * [Components] 中所有组件的 [SerializersModule] 聚合完成后, + * 会以此 [serializersModule] 为基准构建 [Components.serializersModule]。 + * + * ```kotlin + * val finalModule = + * parentSerializersModule overwriteWith allComponentsSerializersModule + * ``` + * + * @since 4.5.0 + */ + public var serializersModule: SerializersModule } /** @@ -75,4 +100,5 @@ public interface ApplicationBuilder { */ public abstract class AbstractApplicationBuilder : ApplicationBuilder { override var coroutineContext: CoroutineContext = EmptyCoroutineContext + override var serializersModule: SerializersModule = EmptySerializersModule() } diff --git a/simbot-api/src/commonMain/kotlin/love/forte/simbot/component/Components.kt b/simbot-api/src/commonMain/kotlin/love/forte/simbot/component/Components.kt index 30dc94ba3..f61b6f48d 100644 --- a/simbot-api/src/commonMain/kotlin/love/forte/simbot/component/Components.kt +++ b/simbot-api/src/commonMain/kotlin/love/forte/simbot/component/Components.kt @@ -26,10 +26,13 @@ package love.forte.simbot.component +import kotlinx.serialization.modules.EmptySerializersModule import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.overwriteWith import love.forte.simbot.common.collection.toImmutable import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName +import kotlin.jvm.JvmOverloads /** * 用于表示一组 [Component] 。 @@ -63,17 +66,24 @@ public inline fun Components.get(): C = /** * 将一个 [Component] 的集合转化为 [Components]。 */ -public fun Collection.toComponents(): Components = CollectionComponents(toImmutable()) +@JvmOverloads +public fun Collection.toComponents( + parentSerializersModule: SerializersModule = EmptySerializersModule() +): Components = + CollectionComponents(toImmutable(), parentSerializersModule) /** * @see Components */ -private class CollectionComponents(private val collections: Collection) : - Components, +private class CollectionComponents( + private val collections: Collection, + parentSerializersModule: SerializersModule +) : Components, Collection by collections { - override val serializersModule: SerializersModule = SerializersModule { - collections.forEach { include(it.serializersModule) } - } + override val serializersModule: SerializersModule = + parentSerializersModule overwriteWith SerializersModule { + collections.forEach { include(it.serializersModule) } + } override fun toString(): String = "Components(values=$collections)" override fun equals(other: Any?): Boolean { diff --git a/simbot-api/src/commonTest/kotlin/ComponentTests.kt b/simbot-api/src/commonTest/kotlin/ComponentTests.kt index 5240fc7ca..42ca3db86 100644 --- a/simbot-api/src/commonTest/kotlin/ComponentTests.kt +++ b/simbot-api/src/commonTest/kotlin/ComponentTests.kt @@ -4,7 +4,7 @@ * Project https://github.com/simple-robot/simpler-robot * Email ForteScarlet@163.com * - * This file is part of the Simple Robot Library. + * This file is part of the Simple Robot Library (Alias: simple-robot, simbot, etc.). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -65,6 +65,7 @@ class ComponentTests { private val testContext = object : ComponentConfigureContext { override val applicationConfiguration: ApplicationConfiguration = object : ApplicationConfiguration { override val coroutineContext: CoroutineContext = EmptyCoroutineContext + override val serializersModule: SerializersModule = EmptySerializersModule() } override val applicationEventRegistrar: ApplicationEventRegistrar = object : ApplicationEventRegistrar { diff --git a/simbot-cores/simbot-core-spring-boot-starter-common/src/main/java/module-info.java b/simbot-cores/simbot-core-spring-boot-starter-common/src/main/java/module-info.java index 12f2acd59..3a107e583 100644 --- a/simbot-cores/simbot-core-spring-boot-starter-common/src/main/java/module-info.java +++ b/simbot-cores/simbot-core-spring-boot-starter-common/src/main/java/module-info.java @@ -5,6 +5,7 @@ requires transitive simbot.core; requires static java.annotation; requires kotlinx.coroutines.core; + requires kotlinx.serialization.core; exports love.forte.simbot.spring.common.application; exports love.forte.simbot.spring.common; diff --git a/simbot-cores/simbot-core-spring-boot-starter-common/src/main/kotlin/love/forte/simbot/spring/common/application/SpringApplication.kt b/simbot-cores/simbot-core-spring-boot-starter-common/src/main/kotlin/love/forte/simbot/spring/common/application/SpringApplication.kt index 0ec758593..6a76629f3 100644 --- a/simbot-cores/simbot-core-spring-boot-starter-common/src/main/kotlin/love/forte/simbot/spring/common/application/SpringApplication.kt +++ b/simbot-cores/simbot-core-spring-boot-starter-common/src/main/kotlin/love/forte/simbot/spring/common/application/SpringApplication.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.serialization.modules.SerializersModule import love.forte.simbot.annotations.InternalSimbotAPI import love.forte.simbot.application.* import love.forte.simbot.core.event.SimpleEventDispatcherConfiguration @@ -73,7 +74,8 @@ public open class SpringApplicationBuilder : AbstractApplicationBuilder() { return SpringApplicationConfigurationImpl( context.minusKey(Job) + job, - applicationConfigurationProperties + serializersModule, + applicationConfigurationProperties, ) } @@ -119,5 +121,6 @@ public interface SpringEventDispatcherConfiguration : SimpleEventDispatcherConfi @InternalSimbotAPI public open class SpringApplicationConfigurationImpl( override val coroutineContext: CoroutineContext, + override val serializersModule: SerializersModule, override val applicationConfigurationProperties: SpringApplicationConfigurationProperties, ) : SpringApplicationConfiguration diff --git a/simbot-cores/simbot-core-spring-boot-starter-v2/src/main/java/module-info.java b/simbot-cores/simbot-core-spring-boot-starter-v2/src/main/java/module-info.java index 37c52de24..89c699455 100644 --- a/simbot-cores/simbot-core-spring-boot-starter-v2/src/main/java/module-info.java +++ b/simbot-cores/simbot-core-spring-boot-starter-v2/src/main/java/module-info.java @@ -3,6 +3,7 @@ requires kotlin.stdlib; requires kotlin.reflect; requires kotlinx.coroutines.core; + requires kotlinx.serialization.core; requires kotlinx.serialization.json; // spring requires spring.core; diff --git a/simbot-cores/simbot-core-spring-boot-starter-v2/src/main/kotlin/love/forte/simbot/spring2/application/SpringApplication.kt b/simbot-cores/simbot-core-spring-boot-starter-v2/src/main/kotlin/love/forte/simbot/spring2/application/SpringApplication.kt index 7a139987a..317428428 100644 --- a/simbot-cores/simbot-core-spring-boot-starter-v2/src/main/kotlin/love/forte/simbot/spring2/application/SpringApplication.kt +++ b/simbot-cores/simbot-core-spring-boot-starter-v2/src/main/kotlin/love/forte/simbot/spring2/application/SpringApplication.kt @@ -101,7 +101,9 @@ public object Spring : SpringApplicationFactory { override val applicationEventRegistrar: ApplicationEventRegistrar get() = registrar - }).toComponents() + }).toComponents( + parentSerializersModule = configuration.serializersModule + ) // plugins val pluginCollections = springConfigurer.pluginFactoriesConfigurator.createAll(object : PluginConfigureContext { diff --git a/simbot-cores/simbot-core-spring-boot-starter/src/main/java/module-info.java b/simbot-cores/simbot-core-spring-boot-starter/src/main/java/module-info.java index f1718332c..1b2a35be7 100644 --- a/simbot-cores/simbot-core-spring-boot-starter/src/main/java/module-info.java +++ b/simbot-cores/simbot-core-spring-boot-starter/src/main/java/module-info.java @@ -3,6 +3,7 @@ requires kotlin.stdlib; requires kotlin.reflect; requires kotlinx.coroutines.core; + requires kotlinx.serialization.core; requires kotlinx.serialization.json; // spring requires spring.core; diff --git a/simbot-cores/simbot-core-spring-boot-starter/src/main/kotlin/love/forte/simbot/spring/application/SpringApplication.kt b/simbot-cores/simbot-core-spring-boot-starter/src/main/kotlin/love/forte/simbot/spring/application/SpringApplication.kt index a81647bdd..031b5aa41 100644 --- a/simbot-cores/simbot-core-spring-boot-starter/src/main/kotlin/love/forte/simbot/spring/application/SpringApplication.kt +++ b/simbot-cores/simbot-core-spring-boot-starter/src/main/kotlin/love/forte/simbot/spring/application/SpringApplication.kt @@ -101,7 +101,9 @@ public object Spring : SpringApplicationFactory { override val applicationEventRegistrar: ApplicationEventRegistrar get() = registrar - }).toComponents() + }).toComponents( + parentSerializersModule = configuration.serializersModule + ) // plugins val pluginCollections = springConfigurer.pluginFactoriesConfigurator.createAll(object : PluginConfigureContext { diff --git a/simbot-cores/simbot-core/src/commonMain/kotlin/love/forte/simbot/core/application/SimpleApplicationFactory.kt b/simbot-cores/simbot-core/src/commonMain/kotlin/love/forte/simbot/core/application/SimpleApplicationFactory.kt index 226d4447f..0762d1cca 100644 --- a/simbot-cores/simbot-core/src/commonMain/kotlin/love/forte/simbot/core/application/SimpleApplicationFactory.kt +++ b/simbot-cores/simbot-core/src/commonMain/kotlin/love/forte/simbot/core/application/SimpleApplicationFactory.kt @@ -26,6 +26,7 @@ package love.forte.simbot.core.application import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob +import kotlinx.serialization.modules.SerializersModule import love.forte.simbot.ability.OnCompletion import love.forte.simbot.annotations.ExperimentalSimbotAPI import love.forte.simbot.application.* @@ -183,7 +184,9 @@ public object Simple : override val applicationEventRegistrar: ApplicationEventRegistrar get() = registrar - }).toComponents() + }).toComponents( + parentSerializersModule = configuration.serializersModule + ) // plugins val pluginCollections = simpleConfigurer.pluginFactoriesConfigurator.createAll(object : PluginConfigureContext { @@ -266,12 +269,17 @@ public class SimpleApplicationBuilder : AbstractApplicationBuilder() { val job = SupervisorJob(context[Job]) // 至少有个 Job - return SimpleApplicationConfigurationImpl(context.minusKey(Job) + job) + return SimpleApplicationConfigurationImpl( + coroutineContext = context.minusKey(Job) + job, + serializersModule = serializersModule + ) } } -private class SimpleApplicationConfigurationImpl(override val coroutineContext: CoroutineContext) : - SimpleApplicationConfiguration +private class SimpleApplicationConfigurationImpl( + override val coroutineContext: CoroutineContext, + override val serializersModule: SerializersModule +) : SimpleApplicationConfiguration private class SimpleApplicationLauncherImpl( private val applicationCreator: () -> SimpleApplicationImpl diff --git a/simbot-cores/simbot-core/src/commonTest/kotlin/love/forte/simbot/core/application/ApplicationSerializersModuleTests.kt b/simbot-cores/simbot-core/src/commonTest/kotlin/love/forte/simbot/core/application/ApplicationSerializersModuleTests.kt new file mode 100644 index 000000000..d9c34a5d2 --- /dev/null +++ b/simbot-cores/simbot-core/src/commonTest/kotlin/love/forte/simbot/core/application/ApplicationSerializersModuleTests.kt @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * Project https://github.com/simple-robot/simpler-robot + * Email ForteScarlet@163.com + * + * This file is part of the Simple Robot Library (Alias: simple-robot, simbot, etc.). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Lesser GNU General Public License for more details. + * + * You should have received a copy of the Lesser GNU General Public License + * along with this program. If not, see . + * + */ + +package love.forte.simbot.core.application + +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.PolymorphicSerializer +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.modules.subclass +import love.forte.simbot.common.function.ConfigurerFunction +import love.forte.simbot.component.Component +import love.forte.simbot.component.ComponentConfigureContext +import love.forte.simbot.component.ComponentFactory +import kotlin.test.Test +import kotlin.test.assertEquals + + +/** + * + * @author ForteScarlet + */ +class ApplicationSerializersModuleTests { + abstract class Father + + @Serializable + @SerialName("kitty") + data object Kitty : Father() + + @Serializable + @SerialName("child") + data object Child : Father() + + class TestComponent : Component { + override val id: String = "ApplicationSerializersModuleTests.component" + override val serializersModule: SerializersModule = SerializersModule { + polymorphic(Father::class) { + subclass(Child.serializer()) + } + } + + companion object : ComponentFactory { + override val key: ComponentFactory.Key = + object : ComponentFactory.Key {} + + override fun create( + context: ComponentConfigureContext, + configurer: ConfigurerFunction + ): TestComponent = TestComponent() + } + } + + class TestComponent2 : Component { + override val id: String = "ApplicationSerializersModuleTests.component2" + override val serializersModule: SerializersModule = SerializersModule { + polymorphic(Father::class) { + subclass(Child.serializer()) + defaultDeserializer { Child.serializer() } + } + } + + companion object : ComponentFactory { + override val key: ComponentFactory.Key = + object : ComponentFactory.Key {} + + override fun create( + context: ComponentConfigureContext, + configurer: ConfigurerFunction + ): TestComponent2 = TestComponent2() + } + } + + @Test + fun parentSerializersModuleTest() = runTest { + val app = launchSimpleApplication { + config { + serializersModule = SerializersModule { + polymorphic(Father::class) { + subclass(Kitty.serializer()) + } + } + } + install(TestComponent) + } + + val json = Json { serializersModule = app.components.serializersModule } + + assertEquals(Kitty, json.decodeFromString(PolymorphicSerializer(Father::class), """{"type":"kitty"}""")) + assertEquals(Child, json.decodeFromString(PolymorphicSerializer(Father::class), """{"type":"child"}""")) + } + + @Test + fun parentSerializersModuleWithDefaultTest() = runTest { + val app = launchSimpleApplication { + config { + serializersModule = SerializersModule { + polymorphic(Father::class) { + subclass(Kitty.serializer()) + defaultDeserializer { Kitty.serializer() } + } + } + } + install(TestComponent) + } + + val json = Json { serializersModule = app.components.serializersModule } + + assertEquals(Kitty, json.decodeFromString(PolymorphicSerializer(Father::class), """{"type":"kitty"}""")) + assertEquals(Child, json.decodeFromString(PolymorphicSerializer(Father::class), """{"type":"child"}""")) + assertEquals(Kitty, json.decodeFromString(PolymorphicSerializer(Father::class), """{}""")) + } + + @Test + fun parentSerializersModuleWithDefaultButBeOverWrittenTest() = runTest { + val app = launchSimpleApplication { + config { + serializersModule = SerializersModule { + polymorphic(Father::class) { + subclass(Kitty.serializer()) + defaultDeserializer { Kitty.serializer() } + } + } + } + install(TestComponent2) + } + + val json = Json { serializersModule = app.components.serializersModule } + + assertEquals(Kitty, json.decodeFromString(PolymorphicSerializer(Father::class), """{"type":"kitty"}""")) + assertEquals(Child, json.decodeFromString(PolymorphicSerializer(Father::class), """{"type":"child"}""")) + assertEquals(Child, json.decodeFromString(PolymorphicSerializer(Father::class), """{}""")) + } + +} diff --git a/simbot-cores/simbot-core/src/jvmMain/java/module-info.java b/simbot-cores/simbot-core/src/jvmMain/java/module-info.java index 2f5efa183..f2bcbb5db 100644 --- a/simbot-cores/simbot-core/src/jvmMain/java/module-info.java +++ b/simbot-cores/simbot-core/src/jvmMain/java/module-info.java @@ -4,6 +4,7 @@ requires static simbot.common.annotations; requires transitive simbot.common.collection; requires kotlinx.coroutines.core; + requires kotlinx.serialization.core; exports love.forte.simbot.core.application; exports love.forte.simbot.core.event;