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")
|
||||
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.Instant
|
||||
import java.util.Date
|
||||
import java.util.TimeZone
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
@ -65,7 +62,7 @@ dependencies {
|
||||
implementation(kotlinx("serialization-core", Versions.serialization))
|
||||
implementation(kotlin("reflect"))
|
||||
|
||||
implementation("net.mamoe.yamlkt:yamlkt:${Versions.yamlkt}")
|
||||
api("net.mamoe.yamlkt:yamlkt:${Versions.yamlkt}")
|
||||
implementation("org.jetbrains.kotlinx:atomicfu:${Versions.atomicFU}")
|
||||
api("org.jetbrains:annotations:19.0.0")
|
||||
api(kotlinx("coroutines-jdk8", Versions.coroutines))
|
||||
|
@ -33,8 +33,8 @@ public abstract class AbstractPluginData : PluginData, PluginDataImpl() {
|
||||
/**
|
||||
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
||||
*/
|
||||
public override fun <T : SerializerAwareValue<*>> T.track(valueName: String): T =
|
||||
apply { valueNodes.add(ValueNode(valueName, this, this.serializer)) }
|
||||
public override fun <T : SerializerAwareValue<*>> T.track(valueName: String, annotations: List<Annotation>): T =
|
||||
apply { valueNodes.add(ValueNode(valueName, this, annotations, this.serializer)) }
|
||||
|
||||
/**
|
||||
* 所有 [valueNodes] 更新和保存序列化器. 仅供内部使用
|
||||
|
@ -127,6 +127,7 @@ public interface PluginData {
|
||||
/**
|
||||
* 由 [provideDelegate] 创建, 来自一个通过 `by value` 初始化的属性节点.
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public data class ValueNode<T>(
|
||||
/**
|
||||
* 节点名称.
|
||||
@ -139,10 +140,12 @@ public interface PluginData {
|
||||
* 属性值代理
|
||||
*/
|
||||
val value: Value<out T>,
|
||||
/**
|
||||
* 注解列表
|
||||
*/
|
||||
val annotations: List<Annotation>,
|
||||
/**
|
||||
* 属性值更新器
|
||||
*
|
||||
* @suppress 注意, 这是实验性 API.
|
||||
*/
|
||||
val updaterSerializer: KSerializer<Unit>
|
||||
)
|
||||
@ -153,7 +156,7 @@ public interface PluginData {
|
||||
public operator fun <T : SerializerAwareValue<*>> T.provideDelegate(
|
||||
thisRef: Any?,
|
||||
property: KProperty<*>
|
||||
): T = track(property.valueName)
|
||||
): T = track(property.valueName, property.getAnnotationListForValueSerialization())
|
||||
|
||||
/**
|
||||
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
||||
@ -167,7 +170,8 @@ public interface PluginData {
|
||||
*
|
||||
* @see [ValueNode.value]
|
||||
*/
|
||||
valueName: String
|
||||
valueName: String,
|
||||
annotations: List<Annotation>
|
||||
): 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
|
||||
* AccountPluginData:
|
||||
* info:
|
||||
* map:
|
||||
* a: b
|
||||
* ```
|
||||
*/
|
||||
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
public annotation class ValueName(val value: String)
|
||||
|
@ -12,7 +12,7 @@
|
||||
package net.mamoe.mirai.console.internal.data
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.MapSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
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.ValueNode
|
||||
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 java.lang.reflect.Constructor
|
||||
import kotlin.reflect.KAnnotatedElement
|
||||
|
||||
/**
|
||||
* Internal implementation for [PluginData] including:
|
||||
@ -53,21 +58,14 @@ internal abstract class PluginDataImpl {
|
||||
}
|
||||
} else {
|
||||
outerLoop@ while (true) {
|
||||
var valueName: String? = null
|
||||
innerLoop@ while (true) {
|
||||
val index = decodeElementIndex(descriptor)
|
||||
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
|
||||
}
|
||||
|
||||
if (!index.isOdd()) { // key
|
||||
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)
|
||||
val node = findNodeInstance(descriptor.getElementName(index))
|
||||
if (node == null) {
|
||||
decodeSerializableElement(descriptor, index, YamlNullableDynamicSerializer)
|
||||
} else {
|
||||
@ -77,7 +75,6 @@ internal abstract class PluginDataImpl {
|
||||
|
||||
break@innerLoop
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -92,8 +89,8 @@ internal abstract class PluginDataImpl {
|
||||
var index = 0
|
||||
|
||||
// val vSerializer = dataUpdaterSerializerTypeArguments[1] as KSerializer<Any?>
|
||||
valueNodes.forEach { (valueName, _, valueSerializer) ->
|
||||
encodeStringElement(descriptor, index++, valueName)
|
||||
valueNodes.forEach { (_, _, _, valueSerializer) ->
|
||||
//encodeStringElement(descriptor, index, valueName)
|
||||
encodeSerializableElement(descriptor, index++, valueSerializer, Unit)
|
||||
}
|
||||
endStructure(descriptor)
|
||||
@ -106,10 +103,37 @@ internal abstract class PluginDataImpl {
|
||||
* flatten
|
||||
*/
|
||||
abstract fun onValueChanged(value: Value<*>)
|
||||
|
||||
companion object {
|
||||
private val dataUpdaterSerializerTypeArguments = arrayOf(String.serializer(), YamlNullableDynamicSerializer)
|
||||
private val dataUpdaterSerializerDescriptor =
|
||||
MapSerializer(dataUpdaterSerializerTypeArguments[0], dataUpdaterSerializerTypeArguments[1]).descriptor
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KAnnotatedElement.getAnnotationListForValueSerialization(): List<Annotation> {
|
||||
return this.annotations.mapNotNull {
|
||||
when (it) {
|
||||
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.PluginDataExtensions.mapKeys
|
||||
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.util.BotManager
|
||||
import net.mamoe.mirai.contact.User
|
||||
@ -38,6 +39,11 @@ internal object ManagersConfig : AutoSavePluginConfig() {
|
||||
override val saveName: String
|
||||
get() = "Managers"
|
||||
|
||||
@ValueDescription(
|
||||
"""
|
||||
管理员列表
|
||||
"""
|
||||
)
|
||||
private val managers by value<MutableMap<Long, MutableSet<Long>>>().withEmptyDefault()
|
||||
.mapKeys(Bot::getInstance, Bot::id)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user