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

View File

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

View File

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

View File

@ -24,6 +24,7 @@ inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoade
object PluginManager { object PluginManager {
val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() } val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
val pluginsDataFolder = File(MiraiConsole.rootDir, "data").apply { mkdir() }
private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf() private val _pluginLoaders: MutableList<PluginLoader<*, *>> = mutableListOf()
private val loadersLock: ReentrantLock = ReentrantLock() 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.AbstractFilePluginLoader
import net.mamoe.mirai.console.plugin.PluginLoadException import net.mamoe.mirai.console.plugin.PluginLoadException
import net.mamoe.mirai.console.plugin.PluginsLoader import net.mamoe.mirai.console.plugin.PluginsLoader
import net.mamoe.mirai.console.setting.SettingStorage
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.yamlkt.Yaml import net.mamoe.yamlkt.Yaml
import java.io.File import java.io.File
@ -24,7 +25,7 @@ import kotlin.reflect.full.createInstance
/** /**
* 内建的 Jar (JVM) 插件加载器 * 内建的 Jar (JVM) 插件加载器
*/ */
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>("jar"), CoroutineScope { object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>(".jar"), CoroutineScope {
private val logger: MiraiLogger by lazy { private val logger: MiraiLogger by lazy {
MiraiConsole.newLogger(JarPluginLoader::class.simpleName!!) 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 getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> { override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {

View File

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

View File

@ -13,6 +13,7 @@ package net.mamoe.mirai.console.setting
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import net.mamoe.mirai.console.setting.internal.SettingImpl import net.mamoe.mirai.console.setting.internal.SettingImpl
import net.mamoe.mirai.console.setting.internal.serialNameOrPropertyName
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@ -33,6 +34,12 @@ typealias Comment = net.mamoe.yamlkt.Comment
*/ */
@Suppress("EXPOSED_SUPER_CLASS") @Suppress("EXPOSED_SUPER_CLASS")
abstract class Setting : SettingImpl() { 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") ?: 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<*> property: KProperty<*>
): ReadWriteProperty<Setting, T> { ): ReadWriteProperty<Setting, T> {
if (built) error("The Setting is already serialized so it's structure is immutable.") 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 return this
} }
abstract override fun onElementChanged(value: Value<*>)
override fun toString(): String = yamlForToString.stringify(this.serializer, this) 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.Bot
import net.mamoe.mirai.console.plugin.builtin.KotlinPlugin import net.mamoe.mirai.console.plugin.builtin.KotlinPlugin
import net.mamoe.mirai.console.setting.value
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.*
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
val plugin: KotlinPlugin = object : KotlinPlugin() { val plugin = MyPlugin()
class MyPlugin : KotlinPlugin() {
inner class MySetting : PluginSetting() {
val int by value(1)
}
} }
/* /*