mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-11 02:50:15 +08:00
Support structured serializers for more extensibility, support comments in serialized files
This commit is contained in:
parent
42bcde38b8
commit
1dcff8149c
@ -1,10 +1,7 @@
|
|||||||
@file:Suppress("UnusedImport")
|
@file:Suppress("UnusedImport")
|
||||||
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.Date
|
|
||||||
import java.util.TimeZone
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
@ -65,7 +62,7 @@ dependencies {
|
|||||||
implementation(kotlinx("serialization-core", Versions.serialization))
|
implementation(kotlinx("serialization-core", Versions.serialization))
|
||||||
implementation(kotlin("reflect"))
|
implementation(kotlin("reflect"))
|
||||||
|
|
||||||
implementation("net.mamoe.yamlkt:yamlkt:${Versions.yamlkt}")
|
api("net.mamoe.yamlkt:yamlkt:${Versions.yamlkt}")
|
||||||
implementation("org.jetbrains.kotlinx:atomicfu:${Versions.atomicFU}")
|
implementation("org.jetbrains.kotlinx:atomicfu:${Versions.atomicFU}")
|
||||||
api("org.jetbrains:annotations:19.0.0")
|
api("org.jetbrains:annotations:19.0.0")
|
||||||
api(kotlinx("coroutines-jdk8", Versions.coroutines))
|
api(kotlinx("coroutines-jdk8", Versions.coroutines))
|
||||||
|
@ -33,8 +33,8 @@ public abstract class AbstractPluginData : PluginData, PluginDataImpl() {
|
|||||||
/**
|
/**
|
||||||
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
||||||
*/
|
*/
|
||||||
public override fun <T : SerializerAwareValue<*>> T.track(valueName: String): T =
|
public override fun <T : SerializerAwareValue<*>> T.track(valueName: String, annotations: List<Annotation>): T =
|
||||||
apply { valueNodes.add(ValueNode(valueName, this, this.serializer)) }
|
apply { valueNodes.add(ValueNode(valueName, this, annotations, this.serializer)) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所有 [valueNodes] 更新和保存序列化器. 仅供内部使用
|
* 所有 [valueNodes] 更新和保存序列化器. 仅供内部使用
|
||||||
|
@ -127,6 +127,7 @@ public interface PluginData {
|
|||||||
/**
|
/**
|
||||||
* 由 [provideDelegate] 创建, 来自一个通过 `by value` 初始化的属性节点.
|
* 由 [provideDelegate] 创建, 来自一个通过 `by value` 初始化的属性节点.
|
||||||
*/
|
*/
|
||||||
|
@ConsoleExperimentalAPI
|
||||||
public data class ValueNode<T>(
|
public data class ValueNode<T>(
|
||||||
/**
|
/**
|
||||||
* 节点名称.
|
* 节点名称.
|
||||||
@ -139,10 +140,12 @@ public interface PluginData {
|
|||||||
* 属性值代理
|
* 属性值代理
|
||||||
*/
|
*/
|
||||||
val value: Value<out T>,
|
val value: Value<out T>,
|
||||||
|
/**
|
||||||
|
* 注解列表
|
||||||
|
*/
|
||||||
|
val annotations: List<Annotation>,
|
||||||
/**
|
/**
|
||||||
* 属性值更新器
|
* 属性值更新器
|
||||||
*
|
|
||||||
* @suppress 注意, 这是实验性 API.
|
|
||||||
*/
|
*/
|
||||||
val updaterSerializer: KSerializer<Unit>
|
val updaterSerializer: KSerializer<Unit>
|
||||||
)
|
)
|
||||||
@ -153,7 +156,7 @@ public interface PluginData {
|
|||||||
public operator fun <T : SerializerAwareValue<*>> T.provideDelegate(
|
public operator fun <T : SerializerAwareValue<*>> T.provideDelegate(
|
||||||
thisRef: Any?,
|
thisRef: Any?,
|
||||||
property: KProperty<*>
|
property: KProperty<*>
|
||||||
): T = track(property.valueName)
|
): T = track(property.valueName, property.getAnnotationListForValueSerialization())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
||||||
@ -167,7 +170,8 @@ public interface PluginData {
|
|||||||
*
|
*
|
||||||
* @see [ValueNode.value]
|
* @see [ValueNode.value]
|
||||||
*/
|
*/
|
||||||
valueName: String
|
valueName: String,
|
||||||
|
annotations: List<Annotation>
|
||||||
): T
|
): T
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.data
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列化之后的注释.
|
||||||
|
*
|
||||||
|
* 例:
|
||||||
|
* ```
|
||||||
|
* object AccountPluginData : PluginData by ... {
|
||||||
|
* @ValueDescription("""
|
||||||
|
* 一个 map
|
||||||
|
* """)
|
||||||
|
* val map: Map<String, String> by value("a" to "b")
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* 将被保存为配置 (YAML 作为示例):
|
||||||
|
* ```yaml
|
||||||
|
* AccountPluginData:
|
||||||
|
* # 一个 map
|
||||||
|
* map:
|
||||||
|
* a: b
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
public annotation class ValueDescription(val value: String)
|
@ -23,10 +23,10 @@ package net.mamoe.mirai.console.data
|
|||||||
* 将被保存为配置 (YAML 作为示例):
|
* 将被保存为配置 (YAML 作为示例):
|
||||||
* ```yaml
|
* ```yaml
|
||||||
* AccountPluginData:
|
* AccountPluginData:
|
||||||
* info:
|
* map:
|
||||||
* a: b
|
* a: b
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
|
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
public annotation class ValueName(val value: String)
|
public annotation class ValueName(val value: String)
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
package net.mamoe.mirai.console.internal.data
|
package net.mamoe.mirai.console.internal.data
|
||||||
|
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.MapSerializer
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.builtins.serializer
|
import kotlinx.serialization.builtins.serializer
|
||||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
import kotlinx.serialization.encoding.CompositeDecoder
|
import kotlinx.serialization.encoding.CompositeDecoder
|
||||||
@ -21,7 +21,12 @@ import kotlinx.serialization.encoding.Encoder
|
|||||||
import net.mamoe.mirai.console.data.PluginData
|
import net.mamoe.mirai.console.data.PluginData
|
||||||
import net.mamoe.mirai.console.data.PluginData.ValueNode
|
import net.mamoe.mirai.console.data.PluginData.ValueNode
|
||||||
import net.mamoe.mirai.console.data.Value
|
import net.mamoe.mirai.console.data.Value
|
||||||
|
import net.mamoe.mirai.console.data.ValueDescription
|
||||||
|
import net.mamoe.mirai.console.data.ValueName
|
||||||
|
import net.mamoe.yamlkt.Comment
|
||||||
import net.mamoe.yamlkt.YamlNullableDynamicSerializer
|
import net.mamoe.yamlkt.YamlNullableDynamicSerializer
|
||||||
|
import java.lang.reflect.Constructor
|
||||||
|
import kotlin.reflect.KAnnotatedElement
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal implementation for [PluginData] including:
|
* Internal implementation for [PluginData] including:
|
||||||
@ -53,21 +58,14 @@ internal abstract class PluginDataImpl {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
outerLoop@ while (true) {
|
outerLoop@ while (true) {
|
||||||
var valueName: String? = null
|
|
||||||
innerLoop@ while (true) {
|
innerLoop@ while (true) {
|
||||||
val index = decodeElementIndex(descriptor)
|
val index = decodeElementIndex(descriptor)
|
||||||
if (index == CompositeDecoder.DECODE_DONE) {
|
if (index == CompositeDecoder.DECODE_DONE) {
|
||||||
check(valueName == null) { "name must be null at this moment." }
|
//check(valueName == null) { "name must be null at this moment." }
|
||||||
break@outerLoop
|
break@outerLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!index.isOdd()) { // key
|
val node = findNodeInstance(descriptor.getElementName(index))
|
||||||
check(valueName == null) { "name must be null at this moment" }
|
|
||||||
valueName = decodeSerializableElement(descriptor, index, String.serializer())
|
|
||||||
} else {
|
|
||||||
check(valueName != null) { "name must not be null at this moment" }
|
|
||||||
|
|
||||||
val node = findNodeInstance(valueName)
|
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
decodeSerializableElement(descriptor, index, YamlNullableDynamicSerializer)
|
decodeSerializableElement(descriptor, index, YamlNullableDynamicSerializer)
|
||||||
} else {
|
} else {
|
||||||
@ -77,7 +75,6 @@ internal abstract class PluginDataImpl {
|
|||||||
|
|
||||||
break@innerLoop
|
break@innerLoop
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,8 +89,8 @@ internal abstract class PluginDataImpl {
|
|||||||
var index = 0
|
var index = 0
|
||||||
|
|
||||||
// val vSerializer = dataUpdaterSerializerTypeArguments[1] as KSerializer<Any?>
|
// val vSerializer = dataUpdaterSerializerTypeArguments[1] as KSerializer<Any?>
|
||||||
valueNodes.forEach { (valueName, _, valueSerializer) ->
|
valueNodes.forEach { (_, _, _, valueSerializer) ->
|
||||||
encodeStringElement(descriptor, index++, valueName)
|
//encodeStringElement(descriptor, index, valueName)
|
||||||
encodeSerializableElement(descriptor, index++, valueSerializer, Unit)
|
encodeSerializableElement(descriptor, index++, valueSerializer, Unit)
|
||||||
}
|
}
|
||||||
endStructure(descriptor)
|
endStructure(descriptor)
|
||||||
@ -106,10 +103,37 @@ internal abstract class PluginDataImpl {
|
|||||||
* flatten
|
* flatten
|
||||||
*/
|
*/
|
||||||
abstract fun onValueChanged(value: Value<*>)
|
abstract fun onValueChanged(value: Value<*>)
|
||||||
|
private val dataUpdaterSerializerDescriptor by lazy {
|
||||||
|
kotlinx.serialization.descriptors.buildClassSerialDescriptor((this as PluginData).saveName) {
|
||||||
|
for (valueNode in valueNodes) valueNode.run {
|
||||||
|
element(valueName, updaterSerializer.descriptor, isOptional = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
internal fun KAnnotatedElement.getAnnotationListForValueSerialization(): List<Annotation> {
|
||||||
private val dataUpdaterSerializerTypeArguments = arrayOf(String.serializer(), YamlNullableDynamicSerializer)
|
return this.annotations.mapNotNull {
|
||||||
private val dataUpdaterSerializerDescriptor =
|
when (it) {
|
||||||
MapSerializer(dataUpdaterSerializerTypeArguments[0], dataUpdaterSerializerTypeArguments[1]).descriptor
|
is SerialName -> error("@SerialName is not supported on Value. Please use @ValueName instead")
|
||||||
|
is ValueName -> null
|
||||||
|
is ValueDescription -> COMMENT_CONSTRUCTOR(it.value)
|
||||||
|
else -> it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val COMMENT_CONSTRUCTOR = findAnnotationImplementationClassConstructor<Comment>()!!
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
internal inline operator fun <T : Any?> Constructor<T>.invoke(vararg args: Any?): T = this.newInstance(*args)
|
||||||
|
|
||||||
|
internal inline fun <reified T : Any> findAnnotationImplementationClassConstructor(): Constructor<out T>? {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
return T::class.nestedClasses
|
||||||
|
.also { println(it.joinToString()) }
|
||||||
|
.find { it.simpleName?.endsWith("Impl") == true }?.java?.run {
|
||||||
|
constructors.singleOrNull()
|
||||||
|
} as Constructor<out T>?
|
||||||
|
}
|
@ -15,6 +15,7 @@ import net.mamoe.mirai.Bot
|
|||||||
import net.mamoe.mirai.console.data.AutoSavePluginConfig
|
import net.mamoe.mirai.console.data.AutoSavePluginConfig
|
||||||
import net.mamoe.mirai.console.data.PluginDataExtensions.mapKeys
|
import net.mamoe.mirai.console.data.PluginDataExtensions.mapKeys
|
||||||
import net.mamoe.mirai.console.data.PluginDataExtensions.withEmptyDefault
|
import net.mamoe.mirai.console.data.PluginDataExtensions.withEmptyDefault
|
||||||
|
import net.mamoe.mirai.console.data.ValueDescription
|
||||||
import net.mamoe.mirai.console.data.value
|
import net.mamoe.mirai.console.data.value
|
||||||
import net.mamoe.mirai.console.util.BotManager
|
import net.mamoe.mirai.console.util.BotManager
|
||||||
import net.mamoe.mirai.contact.User
|
import net.mamoe.mirai.contact.User
|
||||||
@ -38,6 +39,11 @@ internal object ManagersConfig : AutoSavePluginConfig() {
|
|||||||
override val saveName: String
|
override val saveName: String
|
||||||
get() = "Managers"
|
get() = "Managers"
|
||||||
|
|
||||||
|
@ValueDescription(
|
||||||
|
"""
|
||||||
|
管理员列表
|
||||||
|
"""
|
||||||
|
)
|
||||||
private val managers by value<MutableMap<Long, MutableSet<Long>>>().withEmptyDefault()
|
private val managers by value<MutableMap<Long, MutableSet<Long>>>().withEmptyDefault()
|
||||||
.mapKeys(Bot::getInstance, Bot::id)
|
.mapKeys(Bot::getInstance, Bot::id)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user