mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-11 02:50:15 +08:00
Rearrange implementations
This commit is contained in:
parent
50958868cf
commit
408589b615
@ -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.PluginDataImpl
|
||||||
import net.mamoe.mirai.console.internal.data.serialName
|
import net.mamoe.mirai.console.internal.data.serialName
|
||||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||||
|
import kotlin.annotation.AnnotationTarget.*
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,4 +55,29 @@ public abstract class AbstractPluginData : PluginData, PluginDataImpl() {
|
|||||||
* 当所属于这个 [PluginData] 的 [Value] 的 [值][Value.value] 被修改时被调用.
|
* 当所属于这个 [PluginData] 的 [Value] 的 [值][Value.value] 被修改时被调用.
|
||||||
*/
|
*/
|
||||||
public abstract override fun onValueChanged(value: Value<*>)
|
public abstract override fun onValueChanged(value: Value<*>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [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 = ""
|
||||||
|
)
|
@ -14,4 +14,5 @@ package net.mamoe.mirai.console.data
|
|||||||
*
|
*
|
||||||
* 用户可通过 mirai-console 前端修改这些配置, 修改会自动写入这个对象中.
|
* 用户可通过 mirai-console 前端修改这些配置, 修改会自动写入这个对象中.
|
||||||
*/
|
*/
|
||||||
|
@ExperimentalPluginConfig
|
||||||
public interface PluginConfig : PluginData
|
public interface PluginConfig : PluginData
|
@ -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<out PluginData>
|
||||||
|
) :
|
||||||
|
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)
|
||||||
|
}
|
@ -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<Class<out PluginData>, 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 <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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 <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,6 @@
|
|||||||
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.SerialName
|
|
||||||
import kotlinx.serialization.builtins.MapSerializer
|
import kotlinx.serialization.builtins.MapSerializer
|
||||||
import kotlinx.serialization.builtins.serializer
|
import kotlinx.serialization.builtins.serializer
|
||||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
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.PluginData.ValueNode
|
||||||
import net.mamoe.mirai.console.data.Value
|
import net.mamoe.mirai.console.data.Value
|
||||||
import net.mamoe.yamlkt.YamlNullableDynamicSerializer
|
import net.mamoe.yamlkt.YamlNullableDynamicSerializer
|
||||||
import kotlin.reflect.KProperty
|
|
||||||
import kotlin.reflect.full.findAnnotation
|
|
||||||
|
|
||||||
internal val KProperty<*>.serialName: String get() = this.findAnnotation<SerialName>()?.value ?: this.name
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal implementation for [PluginData] including:
|
* Internal implementation for [PluginData] including:
|
||||||
|
@ -9,193 +9,3 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.internal.data
|
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<out PluginData>
|
|
||||||
) :
|
|
||||||
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<Class<out PluginData>, 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 <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): 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 <T : PluginData> load(holder: PluginDataHolder, dataClass: Class<T>): 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 <T : Any> KClass<T>.createInstanceOrNull(): T? {
|
|
||||||
val noArgsConstructor = constructors.singleOrNull { it.parameters.all(KParameter::isOptional) }
|
|
||||||
?: return null
|
|
||||||
|
|
||||||
return noArgsConstructor.callBy(emptyMap())
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmSynthetic
|
|
||||||
internal fun KClass<*>.findASerialName(): String =
|
|
||||||
findAnnotation<SerialName>()?.value
|
|
||||||
?: qualifiedName
|
|
||||||
?: throw IllegalArgumentException("Cannot find a serial name for $this")
|
|
@ -17,7 +17,7 @@ import kotlinx.serialization.encoding.Encoder
|
|||||||
import net.mamoe.mirai.console.data.*
|
import net.mamoe.mirai.console.data.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The super class to all ValueImpl s
|
* The super class to all ValueImpl
|
||||||
*/
|
*/
|
||||||
internal abstract class AbstractValueImpl<T> : Value<T> {
|
internal abstract class AbstractValueImpl<T> : Value<T> {
|
||||||
open fun setValueBySerializer(value: T) {
|
open fun setValueBySerializer(value: T) {
|
||||||
|
@ -9,10 +9,14 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.internal.data
|
package net.mamoe.mirai.console.internal.data
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
import net.mamoe.mirai.console.data.PluginData
|
import net.mamoe.mirai.console.data.PluginData
|
||||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
import kotlin.reflect.KParameter
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
|
import kotlin.reflect.full.findAnnotation
|
||||||
import kotlin.reflect.full.isSubclassOf
|
import kotlin.reflect.full.isSubclassOf
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@ -42,3 +46,33 @@ internal inline fun <reified T : PluginData> newPluginDataInstanceUsingReflectio
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun isReferenceArray(rootClass: KClass<Any>): 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<Any>
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
internal fun <T : Any> KClass<T>.createInstanceOrNull(): T? {
|
||||||
|
val noArgsConstructor = constructors.singleOrNull { it.parameters.all(KParameter::isOptional) }
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
return noArgsConstructor.callBy(emptyMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmSynthetic
|
||||||
|
internal fun KClass<*>.findASerialName(): String =
|
||||||
|
findAnnotation<SerialName>()?.value
|
||||||
|
?: qualifiedName
|
||||||
|
?: throw IllegalArgumentException("Cannot find a serial name for $this")
|
||||||
|
|
||||||
|
|
||||||
|
internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation<SerialName>()?.value ?: this.name
|
||||||
|
|
||||||
|
internal fun Int.isOdd() = this and 0b1 != 0
|
||||||
|
|
||||||
|
internal val KProperty<*>.serialName: String get() = this.findAnnotation<SerialName>()?.value ?: this.name
|
@ -13,6 +13,9 @@ import kotlinx.serialization.ExperimentalSerializationApi
|
|||||||
import kotlinx.serialization.InternalSerializationApi
|
import kotlinx.serialization.InternalSerializationApi
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.*
|
import kotlinx.serialization.builtins.*
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
import kotlinx.serialization.serializer
|
import kotlinx.serialization.serializer
|
||||||
import net.mamoe.yamlkt.YamlDynamicSerializer
|
import net.mamoe.yamlkt.YamlDynamicSerializer
|
||||||
import net.mamoe.yamlkt.YamlNullableDynamicSerializer
|
import net.mamoe.yamlkt.YamlNullableDynamicSerializer
|
||||||
@ -129,10 +132,27 @@ private fun <T : Any> findObjectSerializer(jClass: Class<T>): KSerializer<T>? {
|
|||||||
return result as? KSerializer<T>
|
return result as? KSerializer<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isReferenceArray(rootClass: KClass<Any>): Boolean = rootClass.java.isArray
|
internal inline fun <E> KSerializer<E>.bind(
|
||||||
|
crossinline setter: (E) -> Unit,
|
||||||
|
crossinline getter: () -> E
|
||||||
|
): KSerializer<E> {
|
||||||
|
return object : KSerializer<E> {
|
||||||
|
override val descriptor: SerialDescriptor get() = this@bind.descriptor
|
||||||
|
override fun deserialize(decoder: Decoder): E = this@bind.deserialize(decoder).also { setter(it) }
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun KType.kclass() = when (val t = classifier) {
|
override fun serialize(encoder: Encoder, value: E) =
|
||||||
is KClass<*> -> t
|
this@bind.serialize(encoder, getter())
|
||||||
else -> error("Only KClass supported as classifier, got $t")
|
}
|
||||||
} as KClass<Any>
|
}
|
||||||
|
|
||||||
|
internal inline fun <E, R> KSerializer<E>.map(
|
||||||
|
crossinline serializer: (R) -> E,
|
||||||
|
crossinline deserializer: (E) -> R
|
||||||
|
): KSerializer<R> {
|
||||||
|
return object : KSerializer<R> {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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<SerialName>()?.value ?: this.name
|
|
||||||
|
|
||||||
internal fun Int.isOdd() = this and 0b1 != 0
|
|
||||||
|
|
||||||
internal inline fun <E> KSerializer<E>.bind(
|
|
||||||
crossinline setter: (E) -> Unit,
|
|
||||||
crossinline getter: () -> E
|
|
||||||
): KSerializer<E> {
|
|
||||||
return object : KSerializer<E> {
|
|
||||||
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 <E, R> KSerializer<E>.map(
|
|
||||||
crossinline serializer: (R) -> E,
|
|
||||||
crossinline deserializer: (E) -> R
|
|
||||||
): KSerializer<R> {
|
|
||||||
return object : KSerializer<R> {
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,6 +13,7 @@ package net.mamoe.mirai.console.plugin.jvm
|
|||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import net.mamoe.mirai.console.data.AutoSavePluginDataHolder
|
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.data.PluginData
|
||||||
import net.mamoe.mirai.console.plugin.Plugin
|
import net.mamoe.mirai.console.plugin.Plugin
|
||||||
import net.mamoe.mirai.console.plugin.PluginFileExtensions
|
import net.mamoe.mirai.console.plugin.PluginFileExtensions
|
||||||
@ -54,6 +55,12 @@ public interface JvmPlugin : Plugin, CoroutineScope,
|
|||||||
@JvmDefault
|
@JvmDefault
|
||||||
public fun <T : PluginData> loadPluginData(clazz: Class<T>): T = loader.dataStorage.load(this, clazz)
|
public fun <T : PluginData> loadPluginData(clazz: Class<T>): T = loader.dataStorage.load(this, clazz)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 [JarPluginLoader.dataStorage] 获取一个 [PluginData] 实例
|
||||||
|
*/
|
||||||
|
@JvmDefault
|
||||||
|
public fun <T : PluginConfig> loadPluginConfig(clazz: Class<T>): T = loader.dataStorage.load(this, clazz)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在插件被加载时调用. 只会被调用一次.
|
* 在插件被加载时调用. 只会被调用一次.
|
||||||
*/
|
*/
|
||||||
@ -76,13 +83,26 @@ public interface JvmPlugin : Plugin, CoroutineScope,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取一个插件数据.
|
||||||
|
*/
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
public inline fun <T : PluginData> JvmPlugin.loadPluginData(clazz: KClass<T>): T = this.loadPluginData(clazz.java)
|
public inline fun <T : PluginData> JvmPlugin.loadPluginData(clazz: KClass<T>): T = this.loadPluginData(clazz.java)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取一个插件数据.
|
* 读取一个插件数据.
|
||||||
*
|
|
||||||
* 插件数据
|
|
||||||
*/
|
*/
|
||||||
@JvmSynthetic
|
@JvmSynthetic
|
||||||
public inline fun <reified T : PluginData> JvmPlugin.loadPluginData(): T = this.loadPluginData(T::class)
|
public inline fun <reified T : PluginData> JvmPlugin.loadPluginData(): T = this.loadPluginData(T::class)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取一个插件配置.
|
||||||
|
*/
|
||||||
|
@JvmSynthetic
|
||||||
|
public inline fun <T : PluginConfig> JvmPlugin.loadPluginConfig(clazz: KClass<T>): T = this.loadPluginConfig(clazz.java)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取一个插件配置.
|
||||||
|
*/
|
||||||
|
@JvmSynthetic
|
||||||
|
public inline fun <reified T : PluginConfig> JvmPlugin.loadPluginConfig(): T = this.loadPluginConfig(T::class)
|
Loading…
Reference in New Issue
Block a user