From 408589b6154bd8f952c241de16b5e2cbfda17f1c Mon Sep 17 00:00:00 2001 From: Him188 Date: Sat, 22 Aug 2020 14:24:36 +0800 Subject: [PATCH] Rearrange implementations --- .../mirai/console/data/AbstractPluginData.kt | 28 ++- .../mamoe/mirai/console/data/PluginConfig.kt | 1 + .../internal/data/AutoSavePluginData.kt | 66 ++++++ .../data/MemoryPluginDataStorageImpl.kt | 44 ++++ .../data/MultiFilePluginDataStorageImpl.kt | 72 +++++++ .../console/internal/data/PluginDataImpl.kt | 5 - .../data/PluginDataStorage internal.kt | 190 ------------------ .../data/_PrimitiveValueDeclarations.kt | 2 +- .../data/{asKClass.kt => reflectionUtils.kt} | 34 ++++ .../console/internal/data/serializerHelper.kt | 32 ++- .../console/internal/data/serializerUtil.kt | 47 ----- ...omposite impl.kt => valueFromKTypeImpl.kt} | 0 .../mirai/console/plugin/jvm/JvmPlugin.kt | 26 ++- .../{JavaFriendlyAPI.kt => Annotations.kt} | 0 14 files changed, 294 insertions(+), 253 deletions(-) create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/AutoSavePluginData.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MemoryPluginDataStorageImpl.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/{asKClass.kt => reflectionUtils.kt} (60%) delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerUtil.kt rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/{PluginData.value composite impl.kt => valueFromKTypeImpl.kt} (100%) rename backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/{JavaFriendlyAPI.kt => Annotations.kt} (100%) diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/AbstractPluginData.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/AbstractPluginData.kt index 34797fe68..636f1b1ea 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/AbstractPluginData.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/AbstractPluginData.kt @@ -16,6 +16,7 @@ import net.mamoe.mirai.console.data.PluginData.ValueNode import net.mamoe.mirai.console.internal.data.PluginDataImpl import net.mamoe.mirai.console.internal.data.serialName import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +import kotlin.annotation.AnnotationTarget.* import kotlin.reflect.KProperty /** @@ -54,4 +55,29 @@ public abstract class AbstractPluginData : PluginData, PluginDataImpl() { * 当所属于这个 [PluginData] 的 [Value] 的 [值][Value.value] 被修改时被调用. */ public abstract override fun onValueChanged(value: Value<*>) -} \ No newline at end of file +} + +/** + * [PluginConfig] 的默认实现. + * + * 支持所有 [PluginData] 支持的功能, 支持通过 UI + * + * @see PluginConfig + */ +@ExperimentalPluginConfig +public abstract class AbstractPluginConfig : AbstractPluginData(), PluginConfig + + +/** + * 标记实验性的 [PluginConfig] API. + * + * @see ConsoleExperimentalAPI + */ +@ConsoleExperimentalAPI +@Retention(AnnotationRetention.BINARY) +@RequiresOptIn(level = RequiresOptIn.Level.WARNING) +@Target(CLASS, TYPEALIAS, FUNCTION, PROPERTY, FIELD, CONSTRUCTOR) +@MustBeDocumented +public annotation class ExperimentalPluginConfig( + val message: String = "" +) \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginConfig.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginConfig.kt index a2a0ec216..91cdc6524 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginConfig.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/data/PluginConfig.kt @@ -14,4 +14,5 @@ package net.mamoe.mirai.console.data * * 用户可通过 mirai-console 前端修改这些配置, 修改会自动写入这个对象中. */ +@ExperimentalPluginConfig public interface PluginConfig : PluginData \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/AutoSavePluginData.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/AutoSavePluginData.kt new file mode 100644 index 000000000..6c3822fd8 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/AutoSavePluginData.kt @@ -0,0 +1,66 @@ +package net.mamoe.mirai.console.internal.data + +import kotlinx.atomicfu.atomic +import kotlinx.coroutines.* +import net.mamoe.mirai.console.data.* +import net.mamoe.mirai.console.internal.plugin.updateWhen +import net.mamoe.mirai.console.util.ConsoleInternalAPI +import net.mamoe.mirai.utils.currentTimeMillis +import kotlin.reflect.KClass + +/** + * 链接自动保存的 [PluginData]. + * 当任一相关 [Value] 的值被修改时, 将在一段时间无其他修改时保存 + * + * 若 [AutoSavePluginDataHolder.coroutineContext] 含有 [Job], 则 [AutoSavePluginData] 会通过 [Job.invokeOnCompletion] 在 Job 完结时触发自动保存. + * + * @see loadPluginData + */ +internal open class AutoSavePluginData( + private val owner: AutoSavePluginDataHolder, + internal val originPluginDataClass: KClass +) : + AbstractPluginData(), PluginConfig { + private lateinit var storage: PluginDataStorage + + override fun setStorage(storage: PluginDataStorage) { + check(!this::storage.isInitialized) { "storage is already initialized" } + this.storage = storage + } + + @JvmField + @Volatile + internal var lastAutoSaveJob: Job? = null + + @JvmField + @Volatile + internal var currentFirstStartTime = atomic(0L) + + init { + @OptIn(InternalCoroutinesApi::class) + owner.coroutineContext[Job]?.invokeOnCompletion(true) { doSave() } + } + + private val updaterBlock: suspend CoroutineScope.() -> Unit = { + currentFirstStartTime.updateWhen({ it == 0L }, { currentTimeMillis }) + + delay(owner.autoSaveIntervalMillis.first.coerceAtLeast(1000)) // for safety + + if (lastAutoSaveJob == this.coroutineContext[Job]) { + doSave() + } else { + if (currentFirstStartTime.updateWhen( + { currentTimeMillis - it >= owner.autoSaveIntervalMillis.last }, + { 0 }) + ) doSave() + } + } + + @Suppress("RedundantVisibilityModifier") + @ConsoleInternalAPI + public final override fun onValueChanged(value: Value<*>) { + lastAutoSaveJob = owner.launch(block = updaterBlock) + } + + private fun doSave() = storage.store(owner, this) +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MemoryPluginDataStorageImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MemoryPluginDataStorageImpl.kt new file mode 100644 index 000000000..67f4e38d0 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MemoryPluginDataStorageImpl.kt @@ -0,0 +1,44 @@ +package net.mamoe.mirai.console.internal.data + +import net.mamoe.mirai.console.data.* +import net.mamoe.mirai.console.util.ConsoleInternalAPI + +internal class MemoryPluginDataStorageImpl( + private val onChanged: MemoryPluginDataStorage.OnChangedCallback +) : PluginDataStorage, MemoryPluginDataStorage, + MutableMap, PluginData> by mutableMapOf() { + + internal inner class MemoryPluginDataImpl : AbstractPluginData() { + @ConsoleInternalAPI + override fun onValueChanged(value: Value<*>) { + onChanged.onChanged(this@MemoryPluginDataStorageImpl, value) + } + + override fun setStorage(storage: PluginDataStorage) { + check(storage is MemoryPluginDataStorageImpl) { "storage is not MemoryPluginDataStorageImpl" } + } + } + + @Suppress("UNCHECKED_CAST") + override fun load(holder: PluginDataHolder, dataClass: Class): T = (synchronized(this) { + this.getOrPut(dataClass) { + dataClass.kotlin.run { + objectInstance ?: createInstanceOrNull() ?: kotlin.run { + if (dataClass != PluginData::class.java) { + throw IllegalArgumentException( + "Cannot create PluginData instance. Make sure dataClass is PluginData::class.java or a Kotlin's object, " + + "or has a constructor which either has no parameters or all parameters of which are optional" + ) + } + MemoryPluginDataImpl() + } + } + } + } as T).also { it.setStorage(this) } + + override fun store(holder: PluginDataHolder, pluginData: PluginData) { + synchronized(this) { + this[pluginData::class.java] = pluginData + } + } +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt new file mode 100644 index 000000000..61c687256 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/MultiFilePluginDataStorageImpl.kt @@ -0,0 +1,72 @@ +package net.mamoe.mirai.console.internal.data + +import net.mamoe.mirai.console.data.* +import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip +import net.mamoe.mirai.console.util.ConsoleExperimentalAPI +import net.mamoe.yamlkt.Yaml +import java.io.File +import kotlin.reflect.KClass + +@Suppress("RedundantVisibilityModifier") // might be public in the future +internal open class MultiFilePluginDataStorageImpl( + public final override val directory: File +) : PluginDataStorage, MultiFilePluginDataStorage { + init { + directory.mkdir() + } + + public override fun load(holder: PluginDataHolder, dataClass: Class): T = + with(dataClass.kotlin) { + @Suppress("UNCHECKED_CAST") + val instance = objectInstance ?: this.createInstanceOrNull() ?: kotlin.run { + require(dataClass == PluginData::class.java) { + "Cannot create PluginData instance. Make sure dataClass is PluginData::class.java or a Kotlin's object, " + + "or has a constructor which either has no parameters or all parameters of which are optional" + } + if (holder is AutoSavePluginDataHolder) { + AutoSavePluginData(holder, this) as T? + } else null + } ?: throw IllegalArgumentException( + "Cannot create PluginData instance. Make sure 'holder' is a AutoSavePluginDataHolder, " + + "or 'data' is an object or has a constructor which either has no parameters or all parameters of which are optional" + ) + + val file = getPluginDataFile(holder, this) + file.createNewFile() + check(file.exists() && file.isFile && file.canRead()) { "${file.absolutePath} cannot be read" } + val text = file.readText() + if (text.isNotBlank()) { + Yaml.default.decodeFromString(instance.updaterSerializer, file.readText()) + } + instance + }.also { it.setStorage(this) } + + protected open fun getPluginDataFile(holder: PluginDataHolder, clazz: KClass<*>): File = with(clazz) { + val name = findASerialName() + + val dir = File(directory, holder.name) + if (dir.isFile) { + error("Target directory ${dir.path} for holder $holder is occupied by a file therefore data $qualifiedNameOrTip can't be saved.") + } + dir.mkdir() + + val file = File(directory, name) + if (file.isDirectory) { + error("Target file $file is occupied by a directory therefore data $qualifiedNameOrTip can't be saved.") + } + return file + } + + @ConsoleExperimentalAPI + public override fun store(holder: PluginDataHolder, pluginData: PluginData) { + val file = + getPluginDataFile( + holder, + if (pluginData is AutoSavePluginData) pluginData.originPluginDataClass else pluginData::class + ) + + if (file.exists() && file.isFile && file.canRead()) { + file.writeText(Yaml.default.encodeToString(pluginData.updaterSerializer, Unit)) + } + } +} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/PluginDataImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/PluginDataImpl.kt index 88eb0b3cf..89527e335 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/PluginDataImpl.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/PluginDataImpl.kt @@ -12,7 +12,6 @@ package net.mamoe.mirai.console.internal.data import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerialName import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer import kotlinx.serialization.descriptors.SerialDescriptor @@ -23,10 +22,6 @@ import net.mamoe.mirai.console.data.PluginData import net.mamoe.mirai.console.data.PluginData.ValueNode import net.mamoe.mirai.console.data.Value import net.mamoe.yamlkt.YamlNullableDynamicSerializer -import kotlin.reflect.KProperty -import kotlin.reflect.full.findAnnotation - -internal val KProperty<*>.serialName: String get() = this.findAnnotation()?.value ?: this.name /** * Internal implementation for [PluginData] including: diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/PluginDataStorage internal.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/PluginDataStorage internal.kt index b83886b02..a62e8907a 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/PluginDataStorage internal.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/PluginDataStorage internal.kt @@ -9,193 +9,3 @@ package net.mamoe.mirai.console.internal.data -import kotlinx.atomicfu.atomic -import kotlinx.coroutines.* -import net.mamoe.mirai.console.data.* -import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip -import net.mamoe.mirai.console.internal.plugin.updateWhen -import net.mamoe.mirai.console.plugin.jvm.loadPluginData -import net.mamoe.mirai.console.util.ConsoleExperimentalAPI -import net.mamoe.mirai.console.util.ConsoleInternalAPI -import net.mamoe.mirai.utils.currentTimeMillis -import net.mamoe.yamlkt.Yaml -import java.io.File -import kotlin.reflect.KClass -import kotlin.reflect.KParameter -import kotlin.reflect.full.findAnnotation - - -/** - * 链接自动保存的 [PluginData]. - * 当任一相关 [Value] 的值被修改时, 将在一段时间无其他修改时保存 - * - * 若 [AutoSavePluginDataHolder.coroutineContext] 含有 [Job], 则 [AutoSavePluginData] 会通过 [Job.invokeOnCompletion] 在 Job 完结时触发自动保存. - * - * @see loadPluginData - */ -internal open class AutoSavePluginData( - private val owner: AutoSavePluginDataHolder, - internal val originPluginDataClass: KClass -) : - AbstractPluginData() { - private lateinit var storage: PluginDataStorage - - override fun setStorage(storage: PluginDataStorage) { - check(!this::storage.isInitialized) { "storage is already initialized" } - this.storage = storage - } - - @JvmField - @Volatile - internal var lastAutoSaveJob: Job? = null - - @JvmField - @Volatile - internal var currentFirstStartTime = atomic(0L) - - init { - @OptIn(InternalCoroutinesApi::class) - owner.coroutineContext[Job]?.invokeOnCompletion(true) { doSave() } - } - - private val updaterBlock: suspend CoroutineScope.() -> Unit = { - currentFirstStartTime.updateWhen({ it == 0L }, { currentTimeMillis }) - - delay(owner.autoSaveIntervalMillis.first.coerceAtLeast(1000)) // for safety - - if (lastAutoSaveJob == this.coroutineContext[Job]) { - doSave() - } else { - if (currentFirstStartTime.updateWhen( - { currentTimeMillis - it >= owner.autoSaveIntervalMillis.last }, - { 0 }) - ) doSave() - } - } - - @Suppress("RedundantVisibilityModifier") - @ConsoleInternalAPI - public final override fun onValueChanged(value: Value<*>) { - lastAutoSaveJob = owner.launch(block = updaterBlock) - } - - private fun doSave() = storage.store(owner, this) -} - -internal class MemoryPluginDataStorageImpl( - private val onChanged: MemoryPluginDataStorage.OnChangedCallback -) : PluginDataStorage, MemoryPluginDataStorage, - MutableMap, PluginData> by mutableMapOf() { - - internal inner class MemoryPluginDataImpl : AbstractPluginData() { - @ConsoleInternalAPI - override fun onValueChanged(value: Value<*>) { - onChanged.onChanged(this@MemoryPluginDataStorageImpl, value) - } - - override fun setStorage(storage: PluginDataStorage) { - check(storage is MemoryPluginDataStorageImpl) { "storage is not MemoryPluginDataStorageImpl" } - } - } - - @Suppress("UNCHECKED_CAST") - override fun load(holder: PluginDataHolder, dataClass: Class): T = (synchronized(this) { - this.getOrPut(dataClass) { - dataClass.kotlin.run { - objectInstance ?: createInstanceOrNull() ?: kotlin.run { - if (dataClass != PluginData::class.java) { - throw IllegalArgumentException( - "Cannot create PluginData instance. Make sure dataClass is PluginData::class.java or a Kotlin's object, " + - "or has a constructor which either has no parameters or all parameters of which are optional" - ) - } - MemoryPluginDataImpl() - } - } - } - } as T).also { it.setStorage(this) } - - override fun store(holder: PluginDataHolder, pluginData: PluginData) { - synchronized(this) { - this[pluginData::class.java] = pluginData - } - } -} - -@Suppress("RedundantVisibilityModifier") // might be public in the future -internal open class MultiFilePluginDataStorageImpl( - public final override val directory: File -) : PluginDataStorage, MultiFilePluginDataStorage { - init { - directory.mkdir() - } - - public override fun load(holder: PluginDataHolder, dataClass: Class): T = - with(dataClass.kotlin) { - @Suppress("UNCHECKED_CAST") - val instance = objectInstance ?: this.createInstanceOrNull() ?: kotlin.run { - require(dataClass == PluginData::class.java) { - "Cannot create PluginData instance. Make sure dataClass is PluginData::class.java or a Kotlin's object, " + - "or has a constructor which either has no parameters or all parameters of which are optional" - } - if (holder is AutoSavePluginDataHolder) { - AutoSavePluginData(holder, this) as T? - } else null - } ?: throw IllegalArgumentException( - "Cannot create PluginData instance. Make sure 'holder' is a AutoSavePluginDataHolder, " + - "or 'data' is an object or has a constructor which either has no parameters or all parameters of which are optional" - ) - - val file = getPluginDataFile(holder, this) - file.createNewFile() - check(file.exists() && file.isFile && file.canRead()) { "${file.absolutePath} cannot be read" } - val text = file.readText() - if (text.isNotBlank()) { - Yaml.default.decodeFromString(instance.updaterSerializer, file.readText()) - } - instance - }.also { it.setStorage(this) } - - protected open fun getPluginDataFile(holder: PluginDataHolder, clazz: KClass<*>): File = with(clazz) { - val name = findASerialName() - - val dir = File(directory, holder.name) - if (dir.isFile) { - error("Target directory ${dir.path} for holder $holder is occupied by a file therefore data $qualifiedNameOrTip can't be saved.") - } - dir.mkdir() - - val file = File(directory, name) - if (file.isDirectory) { - error("Target file $file is occupied by a directory therefore data $qualifiedNameOrTip can't be saved.") - } - return file - } - - @ConsoleExperimentalAPI - public override fun store(holder: PluginDataHolder, pluginData: PluginData) { - val file = - getPluginDataFile( - holder, - if (pluginData is AutoSavePluginData) pluginData.originPluginDataClass else pluginData::class - ) - - if (file.exists() && file.isFile && file.canRead()) { - file.writeText(Yaml.default.encodeToString(pluginData.updaterSerializer, Unit)) - } - } -} - -@JvmSynthetic -internal fun KClass.createInstanceOrNull(): T? { - val noArgsConstructor = constructors.singleOrNull { it.parameters.all(KParameter::isOptional) } - ?: return null - - return noArgsConstructor.callBy(emptyMap()) -} - -@JvmSynthetic -internal fun KClass<*>.findASerialName(): String = - findAnnotation()?.value - ?: qualifiedName - ?: throw IllegalArgumentException("Cannot find a serial name for $this") \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/_PrimitiveValueDeclarations.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/_PrimitiveValueDeclarations.kt index df89a4931..77045ec6c 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/_PrimitiveValueDeclarations.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/_PrimitiveValueDeclarations.kt @@ -17,7 +17,7 @@ import kotlinx.serialization.encoding.Encoder import net.mamoe.mirai.console.data.* /** - * The super class to all ValueImpl s + * The super class to all ValueImpl */ internal abstract class AbstractValueImpl : Value { open fun setValueBySerializer(value: T) { diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/asKClass.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/reflectionUtils.kt similarity index 60% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/asKClass.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/reflectionUtils.kt index 1ea34d799..490395615 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/asKClass.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/reflectionUtils.kt @@ -9,10 +9,14 @@ package net.mamoe.mirai.console.internal.data +import kotlinx.serialization.SerialName import net.mamoe.mirai.console.data.PluginData import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip import kotlin.reflect.KClass +import kotlin.reflect.KParameter +import kotlin.reflect.KProperty import kotlin.reflect.KType +import kotlin.reflect.full.findAnnotation import kotlin.reflect.full.isSubclassOf @Suppress("UNCHECKED_CAST") @@ -42,3 +46,33 @@ internal inline fun newPluginDataInstanceUsingReflectio ) } } + + +private fun isReferenceArray(rootClass: KClass): Boolean = rootClass.java.isArray + +@Suppress("UNCHECKED_CAST") +private fun KType.kclass() = when (val t = classifier) { + is KClass<*> -> t + else -> error("Only KClass supported as classifier, got $t") +} as KClass + +@JvmSynthetic +internal fun KClass.createInstanceOrNull(): T? { + val noArgsConstructor = constructors.singleOrNull { it.parameters.all(KParameter::isOptional) } + ?: return null + + return noArgsConstructor.callBy(emptyMap()) +} + +@JvmSynthetic +internal fun KClass<*>.findASerialName(): String = + findAnnotation()?.value + ?: qualifiedName + ?: throw IllegalArgumentException("Cannot find a serial name for $this") + + +internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation()?.value ?: this.name + +internal fun Int.isOdd() = this and 0b1 != 0 + +internal val KProperty<*>.serialName: String get() = this.findAnnotation()?.value ?: this.name diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerHelper.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerHelper.kt index 8a5afe07c..d2ad4352c 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerHelper.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerHelper.kt @@ -13,6 +13,9 @@ import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.builtins.* +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.serializer import net.mamoe.yamlkt.YamlDynamicSerializer import net.mamoe.yamlkt.YamlNullableDynamicSerializer @@ -129,10 +132,27 @@ private fun findObjectSerializer(jClass: Class): KSerializer? { return result as? KSerializer } -private fun isReferenceArray(rootClass: KClass): Boolean = rootClass.java.isArray +internal inline fun KSerializer.bind( + crossinline setter: (E) -> Unit, + crossinline getter: () -> E +): KSerializer { + return object : KSerializer { + override val descriptor: SerialDescriptor get() = this@bind.descriptor + override fun deserialize(decoder: Decoder): E = this@bind.deserialize(decoder).also { setter(it) } -@Suppress("UNCHECKED_CAST") -private fun KType.kclass() = when (val t = classifier) { - is KClass<*> -> t - else -> error("Only KClass supported as classifier, got $t") -} as KClass + @Suppress("UNCHECKED_CAST") + override fun serialize(encoder: Encoder, value: E) = + this@bind.serialize(encoder, getter()) + } +} + +internal inline fun KSerializer.map( + crossinline serializer: (R) -> E, + crossinline deserializer: (E) -> R +): KSerializer { + return object : KSerializer { + override val descriptor: SerialDescriptor get() = this@map.descriptor + override fun deserialize(decoder: Decoder): R = this@map.deserialize(decoder).let(deserializer) + override fun serialize(encoder: Encoder, value: R) = this@map.serialize(encoder, value.let(serializer)) + } +} diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerUtil.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerUtil.kt deleted file mode 100644 index 1800eafe6..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/serializerUtil.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2019-2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 with Mamoe Exceptions license that can be found via the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -package net.mamoe.mirai.console.internal.data - -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerialName -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import kotlin.reflect.KProperty -import kotlin.reflect.full.findAnnotation - -internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation()?.value ?: this.name - -internal fun Int.isOdd() = this and 0b1 != 0 - -internal inline fun KSerializer.bind( - crossinline setter: (E) -> Unit, - crossinline getter: () -> E -): KSerializer { - return object : KSerializer { - override val descriptor: SerialDescriptor get() = this@bind.descriptor - override fun deserialize(decoder: Decoder): E = this@bind.deserialize(decoder).also { setter(it) } - - @Suppress("UNCHECKED_CAST") - override fun serialize(encoder: Encoder, value: E) = - this@bind.serialize(encoder, getter()) - } -} - -internal inline fun KSerializer.map( - crossinline serializer: (R) -> E, - crossinline deserializer: (E) -> R -): KSerializer { - return object : KSerializer { - override val descriptor: SerialDescriptor get() = this@map.descriptor - override fun deserialize(decoder: Decoder): R = this@map.deserialize(decoder).let(deserializer) - override fun serialize(encoder: Encoder, value: R) = this@map.serialize(encoder, value.let(serializer)) - } -} diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/PluginData.value composite impl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/valueFromKTypeImpl.kt similarity index 100% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/PluginData.value composite impl.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/internal/data/valueFromKTypeImpl.kt diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt index 867b4002a..cfa5031f2 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugin/jvm/JvmPlugin.kt @@ -13,6 +13,7 @@ package net.mamoe.mirai.console.plugin.jvm import kotlinx.coroutines.CoroutineScope import net.mamoe.mirai.console.data.AutoSavePluginDataHolder +import net.mamoe.mirai.console.data.PluginConfig import net.mamoe.mirai.console.data.PluginData import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.PluginFileExtensions @@ -54,6 +55,12 @@ public interface JvmPlugin : Plugin, CoroutineScope, @JvmDefault public fun loadPluginData(clazz: Class): T = loader.dataStorage.load(this, clazz) + /** + * 从 [JarPluginLoader.dataStorage] 获取一个 [PluginData] 实例 + */ + @JvmDefault + public fun loadPluginConfig(clazz: Class): T = loader.dataStorage.load(this, clazz) + /** * 在插件被加载时调用. 只会被调用一次. */ @@ -76,13 +83,26 @@ public interface JvmPlugin : Plugin, CoroutineScope, } } +/** + * 读取一个插件数据. + */ @JvmSynthetic public inline fun JvmPlugin.loadPluginData(clazz: KClass): T = this.loadPluginData(clazz.java) /** * 读取一个插件数据. - * - * 插件数据 */ @JvmSynthetic -public inline fun JvmPlugin.loadPluginData(): T = this.loadPluginData(T::class) \ No newline at end of file +public inline fun JvmPlugin.loadPluginData(): T = this.loadPluginData(T::class) + +/** + * 读取一个插件配置. + */ +@JvmSynthetic +public inline fun JvmPlugin.loadPluginConfig(clazz: KClass): T = this.loadPluginConfig(clazz.java) + +/** + * 读取一个插件配置. + */ +@JvmSynthetic +public inline fun JvmPlugin.loadPluginConfig(): T = this.loadPluginConfig(T::class) \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/JavaFriendlyAPI.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/Annotations.kt similarity index 100% rename from backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/JavaFriendlyAPI.kt rename to backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/util/Annotations.kt