SettingStorage infrastructure

This commit is contained in:
Him188 2020-05-25 17:51:01 +08:00
parent 3d53f7f7bc
commit f1b0bf7e68
9 changed files with 167 additions and 23 deletions

View File

@ -15,6 +15,8 @@ import kotlinx.io.charsets.Charset
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.plugin.PluginLoader
import net.mamoe.mirai.console.plugin.builtin.JarPluginLoader
import net.mamoe.mirai.console.plugin.builtin.JvmPlugin
import net.mamoe.mirai.console.setting.SettingStorage
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiLogger
@ -41,7 +43,11 @@ object MiraiConsole : CoroutineScope, IMiraiConsole {
override val mainLogger: MiraiLogger get() = instance.mainLogger
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
override val builtInPluginLoaders: List<PluginLoader<*, *>> = instance.builtInPluginLoaders
override val builtInPluginLoaders: List<PluginLoader<*, *>> get() = instance.builtInPluginLoaders
@Suppress("CANNOT_WEAKEN_ACCESS_PRIVILEGE")
internal override val jvmSettingStorage: SettingStorage
get() = instance.jvmSettingStorage
init {
DefaultLogger = { identity -> this.newLogger(identity) }
@ -79,6 +85,11 @@ internal interface IMiraiConsole : CoroutineScope {
* 内建加载器列表, 一般需要包含 [JarPluginLoader]
*/
val builtInPluginLoaders: List<PluginLoader<*, *>>
/**
* 内建的供 [JvmPlugin] 使用的 [SettingStorage]
*/
val jvmSettingStorage: SettingStorage
}
/**

View File

@ -10,6 +10,7 @@
package net.mamoe.mirai.console.plugin
import net.mamoe.mirai.console.plugin.builtin.JvmPlugin
import java.io.File
/**
* 表示一个 mirai-console 插件.
@ -22,4 +23,9 @@ interface Plugin {
* 所属插件加载器实例
*/
val loader: PluginLoader<*, *>
/**
* 插件数据目录
*/
val dataFolder: File
}

View File

@ -11,6 +11,7 @@
package net.mamoe.mirai.console.plugin
import net.mamoe.mirai.console.plugin.builtin.JarPluginLoader
import java.io.File
/**
@ -59,7 +60,7 @@ open class PluginLoadException : RuntimeException {
*/
interface FilePluginLoader<P : Plugin, D : PluginDescription> : PluginLoader<P, D> {
/**
* 所支持的插件文件后缀, '.'. [JarPluginLoader] "jar"
* 所支持的插件文件后缀, '.'. [JarPluginLoader] ".jar"
*/
val fileSuffix: String
}
@ -70,6 +71,9 @@ abstract class AbstractFilePluginLoader<P : Plugin, D : PluginDescription>(
private fun pluginsFilesSequence(): Sequence<File> =
PluginManager.pluginsDir.walk().filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) }
/**
* 读取扫描到的后缀与 [fileSuffix] 相同的文件中的 [PluginDescription]
*/
protected abstract fun Sequence<File>.mapToDescription(): List<D>
final override fun listPlugins(): List<D> = pluginsFilesSequence().mapToDescription()

View File

@ -24,6 +24,7 @@ inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoade
object PluginManager {
val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
val pluginsDataFolder = File(MiraiConsole.rootDir, "data").apply { mkdir() }
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
private val loadersLock: ReentrantLock = ReentrantLock()

View File

@ -14,6 +14,7 @@ import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader
import net.mamoe.mirai.console.plugin.PluginLoadException
import net.mamoe.mirai.console.plugin.PluginsLoader
import net.mamoe.mirai.console.setting.SettingStorage
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.yamlkt.Yaml
import java.io.File
@ -24,7 +25,7 @@ import kotlin.reflect.full.createInstance
/**
* 内建的 Jar (JVM) 插件加载器
*/
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>("jar"), CoroutineScope {
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>(".jar"), CoroutineScope {
private val logger: MiraiLogger by lazy {
MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!)
}
@ -46,6 +47,8 @@ object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescriptio
}
}
val settingStorage: SettingStorage = MiraiConsole.jvmSettingStorage
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {

View File

@ -12,18 +12,20 @@
package net.mamoe.mirai.console.plugin.builtin
import kotlinx.atomicfu.locks.withLock
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.*
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.plugin.PluginLoader
import net.mamoe.mirai.console.plugin.PluginManager
import net.mamoe.mirai.console.setting.*
import net.mamoe.mirai.console.utils.JavaPluginScheduler
import net.mamoe.mirai.utils.MiraiLogger
import java.io.File
import java.util.*
import java.util.concurrent.locks.ReentrantLock
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.reflect.KProperty
/**
@ -40,7 +42,7 @@ interface JvmPlugin : Plugin, CoroutineScope {
val description: JvmPluginDescription
/** 所属插件加载器实例 */
override val loader: PluginLoader<*, *> get() = JarPluginLoader
override val loader: JarPluginLoader get() = JarPluginLoader
@JvmDefault
fun onLoad() {
@ -72,36 +74,46 @@ abstract class JavaPlugin @JvmOverloads constructor(
abstract class KotlinPlugin @JvmOverloads constructor(
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, JvmPluginImpl(parentCoroutineContext) {
// that's it
abstract inner class PluginSetting : Setting() {
private val track =
@Suppress("LeakingThis")
loader.settingStorage.trackOn(this)
init {
this@KotlinPlugin.job.invokeOnCompletion {
track.close()
}
}
override fun onElementChanged(value: Value<*>) {
TODO()
}
}
}
internal val <T> T.job: Job where T : CoroutineScope, T : Plugin get() = this.coroutineContext[Job]!!
internal sealed class JvmPluginImpl(
parentCoroutineContext: CoroutineContext
) : JvmPlugin, CoroutineScope {
// region JvmPlugin
/**
* Initialized immediately after construction of [JvmPluginImpl] instance
*/
@Suppress("PropertyName")
internal lateinit var _description: JvmPluginDescription
// for future use
@Suppress("PropertyName")
@JvmField
internal var _intrinsicCoroutineContext: CoroutineContext = EmptyCoroutineContext
override val description: JvmPluginDescription get() = _description
final override val logger: MiraiLogger by lazy { MiraiConsole.newLogger(this._description.name) }
@JvmField
internal val coroutineContextInitializer = {
CoroutineExceptionHandler { _, throwable -> logger.error(throwable) }
.plus(parentCoroutineContext)
.plus(SupervisorJob(parentCoroutineContext[Job])) + _intrinsicCoroutineContext
}
private var firstRun = true
override val dataFolder: File by lazy {
File(PluginManager.pluginsDataFolder, description.name).apply { mkdir() }
}
internal fun internalOnDisable() {
firstRun = false
this.onDisable()
@ -116,6 +128,22 @@ internal sealed class JvmPluginImpl(
this.onEnable()
}
// endregion
// region CoroutineScope
// for future use
@Suppress("PropertyName")
@JvmField
internal var _intrinsicCoroutineContext: CoroutineContext = EmptyCoroutineContext
@JvmField
internal val coroutineContextInitializer = {
CoroutineExceptionHandler { _, throwable -> logger.error(throwable) }
.plus(parentCoroutineContext)
.plus(SupervisorJob(parentCoroutineContext[Job])) + _intrinsicCoroutineContext
}
private fun refreshCoroutineContext(): CoroutineContext {
return coroutineContextInitializer().also { _coroutineContext = it }
}
@ -126,4 +154,5 @@ internal sealed class JvmPluginImpl(
get() = _coroutineContext
?: contextUpdateLock.withLock { _coroutineContext ?: refreshCoroutineContext() }
// endregion
}

View File

@ -13,6 +13,7 @@ package net.mamoe.mirai.console.setting
import kotlinx.serialization.KSerializer
import net.mamoe.mirai.console.setting.internal.SettingImpl
import net.mamoe.mirai.console.setting.internal.serialNameOrPropertyName
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@ -33,6 +34,12 @@ typealias Comment = net.mamoe.yamlkt.Comment
*/
@Suppress("EXPOSED_SUPER_CLASS")
abstract class Setting : SettingImpl() {
data class PropertyInfo(
val serialName: String,
val annotations: List<Annotation>
)
/**
* 这个配置的名称, 仅对于顶层配置有效.
*/
@ -43,6 +50,16 @@ abstract class Setting : SettingImpl() {
?: error("Names should be assigned to anonymous classes manually by overriding serialName")
// for Java only
fun <T : Any> addProperty(
propertyInfo: PropertyInfo,
value: Value<*>
): Value<*> {
if (built) error("The Setting is already serialized so it's structure is immutable.")
valueList.add(value to propertyInfo)
return value
}
/**
* 提供属性委托, 并添加这个对象的自动保存跟踪.
*/
@ -52,10 +69,12 @@ abstract class Setting : SettingImpl() {
property: KProperty<*>
): ReadWriteProperty<Setting, T> {
if (built) error("The Setting is already serialized so it's structure is immutable.")
valueList.add(this to property)
valueList.add(this to PropertyInfo(property.serialNameOrPropertyName, property.annotations))
return this
}
abstract override fun onElementChanged(value: Value<*>)
override fun toString(): String = yamlForToString.stringify(this.serializer, this)
}

View File

@ -0,0 +1,65 @@
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
package net.mamoe.mirai.console.setting
import kotlinx.atomicfu.locks.withLock
import kotlinx.serialization.*
import kotlinx.serialization.internal.getChecked
import net.mamoe.mirai.console.setting.internal.SettingSerializerMark
import java.io.Closeable
import java.io.File
import java.util.concurrent.locks.ReentrantLock
/**
* [Setting] 存储方式
*/
interface SettingStorage {
interface TrackedSetting : Closeable {
fun save()
fun update()
override fun close()
}
fun trackOn(setting: Setting): TrackedSetting
fun saveAll()
fun updateAll()
}
class SingleFileSettingStorage(
val file: File
) : SettingStorage {
private val descriptor: MutableList<Setting> = ArrayList()
private val updaterSerializer: KSerializer<SettingSerializerMark> = object : KSerializer<SettingSerializerMark> {
override val descriptor: SerialDescriptor = SerialDescriptor("SingleFileSettingStorage") {
TODO()
}
override fun deserialize(decoder: Decoder): SettingSerializerMark {
TODO("Not yet implemented")
}
override fun serialize(encoder: Encoder, value: SettingSerializerMark) {
TODO("Not yet implemented")
}
}
init {
require(file.isFile) { "file $file is not a file" }
require(file.canRead()) { "file $file is not readable" }
}
override fun trackOn(setting: Setting): SettingStorage.TrackedSetting {
TODO("Not yet implemented")
}
override fun saveAll() {
TODO("Not yet implemented")
}
override fun updateAll() {
TODO("Not yet implemented")
}
}

View File

@ -13,13 +13,19 @@ package net.mamoe.mirai.console.command
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.plugin.builtin.KotlinPlugin
import net.mamoe.mirai.console.setting.value
import net.mamoe.mirai.message.data.*
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
val plugin: KotlinPlugin = object : KotlinPlugin() {
val plugin = MyPlugin()
class MyPlugin : KotlinPlugin() {
inner class MySetting : PluginSetting() {
val int by value(1)
}
}
/*