Implement PluginLoaders and PluginManager

This commit is contained in:
Him188 2020-05-23 17:44:34 +08:00
parent 4de8a6b01c
commit 9c18c04466
16 changed files with 708 additions and 449 deletions

View File

@ -224,7 +224,8 @@ fun genCollectionValueImpl(
"""
internal fun Setting.valueImpl(default: ${kotlinTypeName}): ${miraiValueName}Value {
var internalValue: $kotlinTypeName = default
return object : ${miraiValueName}Value(), $kotlinTypeName by dynamic$collectionName({ internalValue }) {
val delegt = dynamic$collectionName { internalValue }
return object : ${miraiValueName}Value(), $kotlinTypeName by delegt {
override var value: $kotlinTypeName
get() = internalValue
set(new) {

View File

@ -1,4 +1,3 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import upload.Bintray
import java.util.*
@ -12,19 +11,46 @@ plugins {
apply(plugin = "com.github.johnrengelman.shadow")
kotlin {
sourceSets {
all {
languageSettings.enableLanguageFeature("InlineClasses")
version = Versions.Mirai.console
description = "Console backend for mirai"
languageSettings.useExperimentalAnnotation("kotlin.Experimental")
languageSettings.useExperimentalAnnotation("kotlin.OptIn")
languageSettings.progressiveMode = true
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiExperimentalAPI")
languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
languageSettings.useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference")
languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks.withType(JavaCompile::class.java) {
options.encoding = "UTF8"
}
kotlin {
sourceSets.all {
target.compilations.all {
kotlinOptions {
freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=enable"
jvmTarget = "1.8"
}
}
languageSettings.apply {
enableLanguageFeature("InlineClasses")
progressiveMode = true
useExperimentalAnnotation("kotlin.Experimental")
useExperimentalAnnotation("kotlin.OptIn")
useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI")
useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiExperimentalAPI")
useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes")
useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference")
useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts")
}
}
sourceSets {
getByName("test") {
languageSettings.apply {
languageVersion = "1.4"
}
}
}
}
@ -34,36 +60,15 @@ dependencies {
compileAndRuntime(kotlin("stdlib"))
api("net.mamoe.yamlkt:yamlkt:0.3.1")
api("org.jetbrains:annotations:19.0.0")
testApi("net.mamoe:mirai-core-qqandroid:${Versions.Mirai.core}")
testApi(kotlin("stdlib"))
testApi(kotlin("stdlib-jdk8"))
testApi(kotlin("test"))
testApi(kotlin("test-junit5"))
}
version = Versions.Mirai.console
description = "Console backend for mirai"
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "1.8"
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "1.8"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks.withType(JavaCompile::class.java) {
options.encoding = "UTF8"
}
// region PUBLISHING
tasks.register("ensureBintrayAvailable") {
doLast {
@ -128,4 +133,6 @@ if (Bintray.isBintrayAvailable(project)) {
}
}
}
} else println("bintray isn't available. NO PUBLICATIONS WILL BE SET")
} else println("bintray isn't available. NO PUBLICATIONS WILL BE SET")
// endregion

View File

@ -13,33 +13,40 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.io.charsets.Charset
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.utils.MiraiConsoleFrontEnd
import net.mamoe.mirai.console.plugins.JarPluginLoader
import net.mamoe.mirai.console.plugins.PluginLoader
import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiLogger
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintStream
import kotlin.coroutines.CoroutineContext
// 前端使用
interface IMiraiConsole : CoroutineScope {
internal interface IMiraiConsole : CoroutineScope {
val build: String
val version: String
/**
* Console运行路径
* Console 运行路径
*/
val path: String
val rootDir: File
/**
* Console前端接口
* Console 前端接口
*/
val frontEnd: MiraiConsoleFrontEnd
/**
* 与前端交互所使用的Logger
* 与前端交互所使用的 Logger
*/
val mainLogger: MiraiLogger
/**
* 内建加载器列表, 一般需要包含 [JarPluginLoader]
*/
val builtInPluginLoaders: List<PluginLoader<*, *>>
}
object MiraiConsole : CoroutineScope, IMiraiConsole {
@ -52,19 +59,19 @@ object MiraiConsole : CoroutineScope, IMiraiConsole {
override val build: String get() = instance.build
override val version: String get() = instance.version
override val path: String get() = instance.path
override val rootDir: File get() = instance.rootDir
override val frontEnd: MiraiConsoleFrontEnd get() = instance.frontEnd
override val mainLogger: MiraiLogger get() = instance.mainLogger
override val coroutineContext: CoroutineContext get() = instance.coroutineContext
override val builtInPluginLoaders: List<PluginLoader<*, *>> = instance.builtInPluginLoaders
init {
DefaultLogger = {
this.newLogger(it)
DefaultLogger = { identity ->
this.newLogger(identity)
}
this.coroutineContext[Job]!!.invokeOnCompletion {
Bot.botInstances.forEach {
it.close()
}
Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
}
}
@ -72,6 +79,9 @@ object MiraiConsole : CoroutineScope, IMiraiConsole {
fun newLogger(identity: String?): MiraiLogger = frontEnd.loggerFor(identity)
}
/**
* Included in kotlin stdlib 1.4
*/
internal val Throwable.stacktraceString: String
get() =
ByteArrayOutputStream().apply {

View File

@ -7,7 +7,7 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.console.utils
package net.mamoe.mirai.console
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.center.CuiPluginCenter

View File

@ -10,11 +10,7 @@
package net.mamoe.mirai.console.command.description
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.command.BotAwareCommandSender
import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.console.command.MemberCommandSender
import net.mamoe.mirai.console.command.UserCommandSender
import net.mamoe.mirai.console.utils.fuzzySearchMember
import net.mamoe.mirai.console.command.*
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member

View File

@ -9,6 +9,8 @@
package net.mamoe.mirai.console.command
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import java.util.concurrent.locks.ReentrantLock
@ -61,4 +63,97 @@ internal infix fun <T> Array<out T>.intersects(other: Array<out T>): Boolean {
if (this[i] == other[i]) return true
}
return false
}
internal fun String.fuzzyCompare(target: String): Double {
var step = 0
if (this == target) {
return 1.0
}
if (target.length > this.length) {
return 0.0
}
for (i in this.indices) {
if (target.length == i) {
step--
} else {
if (this[i] != target[i]) {
break
}
step++
}
}
if (step == this.length - 1) {
return 1.0
}
return step.toDouble() / this.length
}
/**
* 模糊搜索一个List中index最接近target的东西
*/
internal inline fun <T : Any> Collection<T>.fuzzySearch(
target: String,
index: (T) -> String
): T? {
if (this.isEmpty()) {
return null
}
var potential: T? = null
var rate = 0.0
this.forEach {
val thisIndex = index(it)
if (thisIndex == target) {
return it
}
with(thisIndex.fuzzyCompare(target)) {
if (this > rate) {
rate = this
potential = it
}
}
}
return potential
}
/**
* 模糊搜索一个List中index最接近target的东西
* 并且确保target是唯一的
* 如搜索index为XXXXYY list中同时存在XXXXYYY XXXXYYYY 将返回null
*/
internal inline fun <T : Any> Collection<T>.fuzzySearchOnly(
target: String,
index: (T) -> String
): T? {
if (this.isEmpty()) {
return null
}
var potential: T? = null
var rate = 0.0
var collide = 0
this.forEach {
with(index(it).fuzzyCompare(target)) {
if (this > rate) {
rate = this
potential = it
}
if (this == 1.0) {
collide++
}
if (collide > 1) {
return null//collide
}
}
}
return potential
}
internal fun Group.fuzzySearchMember(nameCardTarget: String): Member? {
return this.members.fuzzySearchOnly(nameCardTarget) {
it.nameCard
}
}

View File

@ -6,50 +6,133 @@
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "EXPOSED_SUPER_CLASS")
package net.mamoe.mirai.console.plugins
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.utils.MiraiLogger
import java.io.File
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
sealed class JarPlugin : Plugin(), CoroutineScope {
internal lateinit var _description: JarPluginDescription
interface JvmPlugin : Plugin, CoroutineScope {
val logger: MiraiLogger
val description: JvmPluginDescription
final override val description: PluginDescription get() = _description
final override val loader: JarPluginLoader get() = JarPluginLoader
@JvmDefault
fun onLoad() {
}
@JvmDefault
fun onEnable() {
}
@JvmDefault
fun onDisable() {
}
}
abstract class JavaPlugin @JvmOverloads constructor(
coroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, JvmPluginImpl(coroutineContext) {
// TODO: 2020/5/23 scheduler, event listener(?)
}
abstract class KotlinPlugin @JvmOverloads constructor(
coroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, JvmPluginImpl(coroutineContext) {
// that's it
}
@Serializable
internal class JarPluginDescription(
data class JvmPluginDescription internal constructor( // serializer 可以用这个构造器
override val kind: PluginKind,
override val name: String,
override val author: String,
override val version: String,
override val info: String,
override val depends: List<String>
) : PluginDescription
override val loadBefore: List<String>,
override val dependencies: List<PluginDependency>
) : PluginDescription, FilePluginDescription {
/**
* 在手动实现时使用这个构造器.
*/
@Suppress("unused")
constructor(
kind: PluginKind,
name: String,
author: String,
version: String,
info: String,
loadBefore: List<String>,
depends: List<PluginDependency>,
file: File
) : this(kind, name, author, version, info, loadBefore, depends) {
this._file = file
}
abstract class JavaPlugin : JarPlugin()
@Suppress("PropertyName")
@Transient
internal var _file: File? = null
abstract class KotlinPlugin : JarPlugin()
override val file: File
get() = _file ?: error("Internal error: JvmPluginDescription(name=$name)._file == null")
}
internal abstract class JvmPluginImpl(
parentCoroutineContext: CoroutineContext
) : JvmPlugin, CoroutineScope {
/**
* Initialized immediately after construction of [JvmPluginImpl] instance
*/
@Suppress("PropertyName")
internal lateinit var _description: JvmPluginDescription
override val description: JvmPluginDescription get() = _description
final override val logger: MiraiLogger by lazy { MiraiConsole.newLogger(this._description.name) }
final override val coroutineContext: CoroutineContext by lazy {
SupervisorJob(parentCoroutineContext[Job]) + CoroutineExceptionHandler { _, throwable ->
logger.error(throwable)
}
}
}
/**
* 内建的 Jar (JVM) 插件加载器
*/
object JarPluginLoader : PluginLoader<JarPlugin> {
override val list: List<JarPlugin>
get() = TODO("Not yet implemented")
object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescription>("jar") {
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
override fun load(plugin: JarPlugin) {
TODO("Not yet implemented")
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {
TODO(
"""
CHECK IS JAR FILE AND CAN BE READ
READ JAR FILE, EXTRACT PLUGIN DESCRIPTION
SET JvmPluginDescription._file
RETURN PLUGIN
""".trimIndent()
)
}
override fun enable(plugin: JarPlugin) {
TODO("Not yet implemented")
@Throws(PluginLoadException::class)
override fun load(description: JvmPluginDescription): JvmPlugin {
TODO("FIND PLUGIN MAIN, THEN LOAD")
// no need to check dependencies
}
override fun enable(plugin: JvmPlugin) = plugin.onEnable()
override fun disable(plugin: JvmPlugin) = plugin.onDisable()
}
/*

View File

@ -9,45 +9,82 @@
package net.mamoe.mirai.console.plugins
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiLogger
import kotlin.coroutines.CoroutineContext
import kotlinx.serialization.Serializable
import java.io.File
/** 插件类型 */
enum class PluginKind {
/** 表示此插件提供一个 [PluginLoader], 应在加载其他 [NORMAL] 类型插件前加载 */
LOADER,
/** 表示此插件为一个通常的插件, 按照正常的依赖关系加载. */
NORMAL
}
/**
* 插件信息
* 插件描述
*/
interface PluginDescription {
val kind: PluginKind
val name: String
val author: String
val version: String
val info: String
val depends: List<String>
/** 指定此插件需要在这些插件之前加载 */
val loadBefore: List<String>
/** 此插件依赖的其他插件, 将会在这些插件加载之后加载此插件 */
val dependencies: List<PluginDependency>
}
/** 插件的一个依赖的信息 */
@Serializable
data class PluginDependency(
/** 依赖插件名 */
val name: String,
/**
* 依赖版本号
* @see versionKind 版本号类型
*/
val version: String,
/** 版本号类型 */
val versionKind: VersionKind
) {
enum class VersionKind {
/** 要求依赖精确的版本 */
EXACT,
/** 要求依赖最低版本 */
AT_LEAST,
/** 要求依赖最高版本 */
AT_MOST
}
override fun toString(): String {
return "$name ${versionKind.toEnglishString()}v$version"
}
}
internal fun PluginDependency.VersionKind.toEnglishString(): String = when (this) {
PluginDependency.VersionKind.EXACT -> ""
PluginDependency.VersionKind.AT_LEAST -> "at least "
PluginDependency.VersionKind.AT_MOST -> "at most "
}
/**
* 插件基类.
*
* 内建的插件类型:
* - [JarPlugin]
* 基于文件的插件的描述
*/
abstract class Plugin : CoroutineScope {
abstract val description: PluginDescription
abstract val loader: PluginLoader<*>
interface FilePluginDescription : PluginDescription {
val file: File
}
@OptIn(MiraiExperimentalAPI::class)
val logger: MiraiLogger by lazy { MiraiConsole.newLogger(description.name) }
override val coroutineContext: CoroutineContext
get() = SupervisorJob(MiraiConsole.coroutineContext[Job]) + CoroutineExceptionHandler { _, throwable ->
logger.error(throwable)
}
open fun onLoaded() {}
open fun onDisabled() {}
open fun onEnabled() {}
}
/**
* 表示一个 mirai-console 插件.
*
* @see JvmPlugin
*/
interface Plugin

View File

@ -1,29 +1,68 @@
@file:Suppress("unused")
package net.mamoe.mirai.console.plugins
import net.mamoe.mirai.console.MiraiConsole
import java.io.File
/**
* 插件加载器
* 插件加载器.
*
* 插件加载器只实现寻找插件列表, 加载插件, 启用插件, 关闭插件这四个功能.
*
* 有关插件的依赖和已加载的插件列表由 [PluginManager] 维护.
*/
interface PluginLoader<P : Plugin, D : PluginDescription> {
/**
* 扫描并返回可以被加载的插件的 [描述][PluginDescription] 列表. 此函数只会被调用一次
*/
fun listPlugins(): List<D>
/**
* 获取此插件的描述
*/
@Throws(PluginLoadException::class)
fun getPluginDescription(plugin: P): D
/**
* 加载一个插件 (实例), 但不 [启用][enable] . 返回加载成功的实例
*
* @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如找不到主类等).
*/
@Throws(PluginLoadException::class)
fun load(description: D): P
fun enable(plugin: P)
fun disable(plugin: P)
}
open class PluginLoadException : RuntimeException {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
}
/**
* '/plugins' 目录中的插件的加载器. 每个加载器需绑定一个后缀.
*
* @see AbstractFilePluginLoader
* @see JarPluginLoader 内建的 Jar (JVM) 插件加载器.
*/
interface PluginLoader<P : Plugin> {
val list: List<P>
fun loadAll() = list.forEach(::load)
fun enableAll() = list.forEach(::enable)
fun unloadAll() = list.forEach(::unload)
fun reloadAll() = list.forEach(::reload)
val isUnloadSupported: Boolean
get() = false
fun load(plugin: P)
fun enable(plugin: P)
fun unload(plugin: P) {
error("NotImplemented")
}
fun reload(plugin: P) {
unload(plugin)
load(plugin)
}
interface FilePluginLoader<P : Plugin, D : PluginDescription> : PluginLoader<P, D> {
/**
* 所支持的插件文件后缀, 不含 '.'. [JarPluginLoader] "jar"
*/
val fileSuffix: String
}
abstract class AbstractFilePluginLoader<P : Plugin, D : PluginDescription>(
override val fileSuffix: String
) : FilePluginLoader<P, D> {
private fun pluginsFilesSequence(): Sequence<File> =
PluginManager.pluginsDir.walk().filter { it.isFile && it.name.endsWith(fileSuffix, ignoreCase = true) }
protected abstract fun Sequence<File>.mapToDescription(): List<D>
final override fun listPlugins(): List<D> = pluginsFilesSequence().mapToDescription()
}

View File

@ -1,24 +1,180 @@
@file:Suppress("NOTHING_TO_INLINE")
package net.mamoe.mirai.console.plugins
import kotlinx.atomicfu.locks.withLock
import net.mamoe.mirai.console.MiraiConsole
import java.io.File
import java.util.*
import java.util.concurrent.locks.ReentrantLock
import kotlin.collections.ArrayList
val Plugin.description: PluginDescription get() = TODO()
val <P : Plugin> P.loader: PluginLoader<P, *> get() = TODO()
inline fun PluginLoader<*, *>.register() = PluginManager.registerPluginLoader(this)
inline fun PluginLoader<*, *>.unregister() = PluginManager.unregisterPluginLoader(this)
object PluginManager {
private val _loaders: MutableSet<PluginLoader<*>> = mutableSetOf()
val pluginsDir = File(MiraiConsole.rootDir, "plugins").apply { mkdir() }
val loaders: Set<PluginLoader<*>> get() = _loaders
class LoaderNode<P : Plugin, D : PluginDescription>(
val loader: PluginLoader<P, D>,
val loadedPlugins: MutableList<P> = mutableListOf()
)
fun registerPluginLoader(loader: PluginLoader<*>) {
_loaders.add(loader)
private val _pluginLoaders: MutableSet<LoaderNode<*, *>> = mutableSetOf()
private val loadersLock: ReentrantLock = ReentrantLock()
private val resolvedPlugins: LinkedList<Plugin> = LinkedList()
@JvmStatic
val plugins: List<Plugin>
get() = _pluginLoaders.flatMap { it.loadedPlugins }
/**
* 内建的插件加载器列表. [MiraiConsole] 初始化
*/
@JvmStatic
val builtInLoaders: List<PluginLoader<*, *>>
get() = MiraiConsole.builtInPluginLoaders
/**
* 由插件创建的 [PluginLoader]
*/
val pluginLoaders: List<PluginLoader<*, *>> get() = _pluginLoaders.map { it.loader }
@JvmStatic
fun registerPluginLoader(loader: PluginLoader<*, *>): Boolean = loadersLock.withLock {
_pluginLoaders.add(LoaderNode(loader))
}
fun unregisterPluginLoader(loader: PluginLoader<*>) {
_loaders.remove(loader)
@JvmStatic
fun unregisterPluginLoader(loader: PluginLoader<*, *>) = loadersLock.withLock {
_pluginLoaders.removeAll { it.loader == loader }
}
fun loadPlugins() {
loaders.forEach(PluginLoader<*>::loadAll)
// region LOADING
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.loadPluginNoEnable(description: D): P {
return this.load(description).also { resolvedPlugins.add(it) }
}
fun enablePlugins() {
loaders.forEach(PluginLoader<*>::enableAll)
private fun <P : Plugin, D : PluginDescription> PluginLoader<P, D>.loadPluginAndEnable(description: D) {
@Suppress("UNCHECKED_CAST")
return this.enable(loadPluginNoEnable(description.unwrap()))
}
/**
* STEPS:
* 1. 遍历插件列表, 使用 [builtInLoaders] 加载 [PluginKind.LOADER] 类型的插件
* 2. [启动][PluginLoader.enable] 所有 [PluginKind.LOADER] 的插件
* 3. 使用内建和所有插件提供的 [PluginLoader] 加载全部除 [PluginKind.LOADER] 外的插件列表.
* 4. 解决依赖并排序
* 5. 依次 [PluginLoader.load]
* 但不 [PluginLoader.enable]
*
* @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable]
*/
@Suppress("UNCHECKED_CAST")
@Throws(PluginMissingDependencyException::class)
internal fun loadEnablePlugins() {
val all = loadAndEnableLoaderProviders() + pluginLoaders.listAllPlugins().flatMap { it.second }
for ((loader, desc) in all.sortByDependencies()) {
loader.loadPluginAndEnable(desc)
}
}
/**
* @return [builtInLoaders] 可以加载的插件. 已经完成了 [PluginLoader.load], 但没有 [PluginLoader.enable]
*/
@Suppress("UNCHECKED_CAST")
@Throws(PluginMissingDependencyException::class)
private fun loadAndEnableLoaderProviders(): List<PluginDescriptionWithLoader> {
val allDescriptions =
this.builtInLoaders.listAllPlugins()
.asSequence()
.onEach { (loader, descriptions) ->
loader as PluginLoader<Plugin, PluginDescription>
for (it in descriptions.filter { it.kind == PluginKind.LOADER }.sortByDependencies()) {
loader.loadPluginAndEnable(it)
}
}
.flatMap { it.second.asSequence() }
return allDescriptions.toList()
}
private fun List<PluginLoader<*, *>>.listAllPlugins(): List<Pair<PluginLoader<*, *>, List<PluginDescriptionWithLoader>>> {
return associateWith { loader -> loader.listPlugins().map { desc -> desc.wrapWith(loader) } }.toList()
}
@Throws(PluginMissingDependencyException::class)
private fun <D : PluginDescription> List<D>.sortByDependencies(): List<D> {
val resolved = ArrayList<D>(this.size)
fun D.canBeLoad(): Boolean = this.dependencies.all { it in resolved }
fun List<D>.consumeLoadable(): List<D> {
val (canBeLoad, cannotBeLoad) = this.partition { it.canBeLoad() }
resolved.addAll(canBeLoad)
return cannotBeLoad
}
fun List<PluginDependency>.filterIsMissing(): List<PluginDependency> = this.filterNot { it in resolved }
tailrec fun List<D>.doSort() {
if (this.isEmpty()) return
val beforeSize = this.size
this.consumeLoadable().also { resultPlugins ->
check(resultPlugins.size < beforeSize) {
throw PluginMissingDependencyException(resultPlugins.joinToString("\n") { badPlugin ->
"Cannot load plugin ${badPlugin.name}, missing dependencies: ${badPlugin.dependencies.filterIsMissing()
.joinToString()}"
})
}
}.doSort()
}
this.doSort()
return resolved
}
// endregion
}
internal data class PluginDescriptionWithLoader(
@JvmField val loader: PluginLoader<*, PluginDescription>, // easier type
@JvmField val delegate: PluginDescription
) : PluginDescription by delegate
@Suppress("UNCHECKED_CAST")
internal fun <D : PluginDescription> PluginDescription.unwrap(): D =
if (this is PluginDescriptionWithLoader) this.delegate as D else this as D
@Suppress("UNCHECKED_CAST")
internal fun PluginDescription.wrapWith(loader: PluginLoader<*, *>): PluginDescriptionWithLoader =
PluginDescriptionWithLoader(
loader as PluginLoader<*, PluginDescription>, this
)
internal operator fun List<PluginDescription>.contains(dependency: PluginDependency): Boolean =
any { it.name == dependency.name }
class PluginMissingDependencyException : Exception {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
}
open class PluginResolutionException : Exception {
constructor() : super()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
}

View File

@ -9,10 +9,7 @@
package net.mamoe.mirai.console.setting.internal
import kotlinx.serialization.Decoder
import kotlinx.serialization.Encoder
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialDescriptor
import kotlinx.serialization.*
import kotlinx.serialization.builtins.*
import net.mamoe.mirai.console.setting.*
@ -415,7 +412,8 @@ internal fun Setting.valueImpl(default: Array<String>): TypedStringArrayValue {
internal fun Setting.valueImpl(default: List<Int>): IntListValue {
var internalValue: List<Int> = default
return object : IntListValue(), List<Int> by dynamicList({ internalValue }) {
val delegt = dynamicList { internalValue }
return object : IntListValue(), List<Int> by delegt {
override var value: List<Int>
get() = internalValue
set(new) {
@ -430,7 +428,8 @@ internal fun Setting.valueImpl(default: List<Int>): IntListValue {
internal fun Setting.valueImpl(default: List<Short>): ShortListValue {
var internalValue: List<Short> = default
return object : ShortListValue(), List<Short> by dynamicList({ internalValue }) {
val delegt = dynamicList { internalValue }
return object : ShortListValue(), List<Short> by delegt {
override var value: List<Short>
get() = internalValue
set(new) {
@ -445,7 +444,8 @@ internal fun Setting.valueImpl(default: List<Short>): ShortListValue {
internal fun Setting.valueImpl(default: List<Byte>): ByteListValue {
var internalValue: List<Byte> = default
return object : ByteListValue(), List<Byte> by dynamicList({ internalValue }) {
val delegt = dynamicList { internalValue }
return object : ByteListValue(), List<Byte> by delegt {
override var value: List<Byte>
get() = internalValue
set(new) {
@ -460,7 +460,8 @@ internal fun Setting.valueImpl(default: List<Byte>): ByteListValue {
internal fun Setting.valueImpl(default: List<Long>): LongListValue {
var internalValue: List<Long> = default
return object : LongListValue(), List<Long> by dynamicList({ internalValue }) {
val delegt = dynamicList { internalValue }
return object : LongListValue(), List<Long> by delegt {
override var value: List<Long>
get() = internalValue
set(new) {
@ -475,7 +476,8 @@ internal fun Setting.valueImpl(default: List<Long>): LongListValue {
internal fun Setting.valueImpl(default: List<Float>): FloatListValue {
var internalValue: List<Float> = default
return object : FloatListValue(), List<Float> by dynamicList({ internalValue }) {
val delegt = dynamicList { internalValue }
return object : FloatListValue(), List<Float> by delegt {
override var value: List<Float>
get() = internalValue
set(new) {
@ -490,7 +492,8 @@ internal fun Setting.valueImpl(default: List<Float>): FloatListValue {
internal fun Setting.valueImpl(default: List<Double>): DoubleListValue {
var internalValue: List<Double> = default
return object : DoubleListValue(), List<Double> by dynamicList({ internalValue }) {
val delegt = dynamicList { internalValue }
return object : DoubleListValue(), List<Double> by delegt {
override var value: List<Double>
get() = internalValue
set(new) {
@ -505,7 +508,8 @@ internal fun Setting.valueImpl(default: List<Double>): DoubleListValue {
internal fun Setting.valueImpl(default: List<Boolean>): BooleanListValue {
var internalValue: List<Boolean> = default
return object : BooleanListValue(), List<Boolean> by dynamicList({ internalValue }) {
val delegt = dynamicList { internalValue }
return object : BooleanListValue(), List<Boolean> by delegt {
override var value: List<Boolean>
get() = internalValue
set(new) {
@ -520,7 +524,8 @@ internal fun Setting.valueImpl(default: List<Boolean>): BooleanListValue {
internal fun Setting.valueImpl(default: List<Char>): CharListValue {
var internalValue: List<Char> = default
return object : CharListValue(), List<Char> by dynamicList({ internalValue }) {
val delegt = dynamicList { internalValue }
return object : CharListValue(), List<Char> by delegt {
override var value: List<Char>
get() = internalValue
set(new) {
@ -535,7 +540,8 @@ internal fun Setting.valueImpl(default: List<Char>): CharListValue {
internal fun Setting.valueImpl(default: List<String>): StringListValue {
var internalValue: List<String> = default
return object : StringListValue(), List<String> by dynamicList({ internalValue }) {
val delegt = dynamicList { internalValue }
return object : StringListValue(), List<String> by delegt {
override var value: List<String>
get() = internalValue
set(new) {
@ -550,7 +556,8 @@ internal fun Setting.valueImpl(default: List<String>): StringListValue {
internal fun Setting.valueImpl(default: Set<Int>): IntSetValue {
var internalValue: Set<Int> = default
return object : IntSetValue(), Set<Int> by dynamicSet({ internalValue }) {
val delegt = dynamicSet { internalValue }
return object : IntSetValue(), Set<Int> by delegt {
override var value: Set<Int>
get() = internalValue
set(new) {
@ -565,7 +572,8 @@ internal fun Setting.valueImpl(default: Set<Int>): IntSetValue {
internal fun Setting.valueImpl(default: Set<Short>): ShortSetValue {
var internalValue: Set<Short> = default
return object : ShortSetValue(), Set<Short> by dynamicSet({ internalValue }) {
val delegt = dynamicSet { internalValue }
return object : ShortSetValue(), Set<Short> by delegt {
override var value: Set<Short>
get() = internalValue
set(new) {
@ -580,7 +588,8 @@ internal fun Setting.valueImpl(default: Set<Short>): ShortSetValue {
internal fun Setting.valueImpl(default: Set<Byte>): ByteSetValue {
var internalValue: Set<Byte> = default
return object : ByteSetValue(), Set<Byte> by dynamicSet({ internalValue }) {
val delegt = dynamicSet { internalValue }
return object : ByteSetValue(), Set<Byte> by delegt {
override var value: Set<Byte>
get() = internalValue
set(new) {
@ -595,7 +604,8 @@ internal fun Setting.valueImpl(default: Set<Byte>): ByteSetValue {
internal fun Setting.valueImpl(default: Set<Long>): LongSetValue {
var internalValue: Set<Long> = default
return object : LongSetValue(), Set<Long> by dynamicSet({ internalValue }) {
val delegt = dynamicSet { internalValue }
return object : LongSetValue(), Set<Long> by delegt {
override var value: Set<Long>
get() = internalValue
set(new) {
@ -610,7 +620,8 @@ internal fun Setting.valueImpl(default: Set<Long>): LongSetValue {
internal fun Setting.valueImpl(default: Set<Float>): FloatSetValue {
var internalValue: Set<Float> = default
return object : FloatSetValue(), Set<Float> by dynamicSet({ internalValue }) {
val delegt = dynamicSet { internalValue }
return object : FloatSetValue(), Set<Float> by delegt {
override var value: Set<Float>
get() = internalValue
set(new) {
@ -625,7 +636,8 @@ internal fun Setting.valueImpl(default: Set<Float>): FloatSetValue {
internal fun Setting.valueImpl(default: Set<Double>): DoubleSetValue {
var internalValue: Set<Double> = default
return object : DoubleSetValue(), Set<Double> by dynamicSet({ internalValue }) {
val delegt = dynamicSet { internalValue }
return object : DoubleSetValue(), Set<Double> by delegt {
override var value: Set<Double>
get() = internalValue
set(new) {
@ -640,7 +652,8 @@ internal fun Setting.valueImpl(default: Set<Double>): DoubleSetValue {
internal fun Setting.valueImpl(default: Set<Boolean>): BooleanSetValue {
var internalValue: Set<Boolean> = default
return object : BooleanSetValue(), Set<Boolean> by dynamicSet({ internalValue }) {
val delegt = dynamicSet { internalValue }
return object : BooleanSetValue(), Set<Boolean> by delegt {
override var value: Set<Boolean>
get() = internalValue
set(new) {
@ -655,7 +668,8 @@ internal fun Setting.valueImpl(default: Set<Boolean>): BooleanSetValue {
internal fun Setting.valueImpl(default: Set<Char>): CharSetValue {
var internalValue: Set<Char> = default
return object : CharSetValue(), Set<Char> by dynamicSet({ internalValue }) {
val delegt = dynamicSet { internalValue }
return object : CharSetValue(), Set<Char> by delegt {
override var value: Set<Char>
get() = internalValue
set(new) {
@ -670,7 +684,8 @@ internal fun Setting.valueImpl(default: Set<Char>): CharSetValue {
internal fun Setting.valueImpl(default: Set<String>): StringSetValue {
var internalValue: Set<String> = default
return object : StringSetValue(), Set<String> by dynamicSet({ internalValue }) {
val delegt = dynamicSet { internalValue }
return object : StringSetValue(), Set<String> by delegt {
override var value: Set<String>
get() = internalValue
set(new) {
@ -690,7 +705,7 @@ internal fun Setting.valueImpl(
): MutableIntListValue {
var internalValue: MutableList<Int> = default
val delegt = dynamicMutableList { internalValue }
val delegt = dynamicMutableList{ internalValue }
return object : MutableIntListValue(), MutableList<Int> by delegt {
override var value: MutableList<Int>
get() = internalValue
@ -700,15 +715,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableList<Int>> = object : KSerializer<MutableList<Int>> {
private val delegate = ListSerializer(Int.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableList<Int> {
return delegate.deserialize(decoder).toMutableList().observable {
return delegate.deserialize(decoder).toMutableList().observable {
onElementChanged(outerThis)
}
}
@ -726,7 +741,7 @@ internal fun Setting.valueImpl(
): MutableShortListValue {
var internalValue: MutableList<Short> = default
val delegt = dynamicMutableList { internalValue }
val delegt = dynamicMutableList{ internalValue }
return object : MutableShortListValue(), MutableList<Short> by delegt {
override var value: MutableList<Short>
get() = internalValue
@ -736,15 +751,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableList<Short>> = object : KSerializer<MutableList<Short>> {
private val delegate = ListSerializer(Short.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableList<Short> {
return delegate.deserialize(decoder).toMutableList().observable {
return delegate.deserialize(decoder).toMutableList().observable {
onElementChanged(outerThis)
}
}
@ -762,7 +777,7 @@ internal fun Setting.valueImpl(
): MutableByteListValue {
var internalValue: MutableList<Byte> = default
val delegt = dynamicMutableList { internalValue }
val delegt = dynamicMutableList{ internalValue }
return object : MutableByteListValue(), MutableList<Byte> by delegt {
override var value: MutableList<Byte>
get() = internalValue
@ -772,15 +787,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableList<Byte>> = object : KSerializer<MutableList<Byte>> {
private val delegate = ListSerializer(Byte.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableList<Byte> {
return delegate.deserialize(decoder).toMutableList().observable {
return delegate.deserialize(decoder).toMutableList().observable {
onElementChanged(outerThis)
}
}
@ -798,7 +813,7 @@ internal fun Setting.valueImpl(
): MutableLongListValue {
var internalValue: MutableList<Long> = default
val delegt = dynamicMutableList { internalValue }
val delegt = dynamicMutableList{ internalValue }
return object : MutableLongListValue(), MutableList<Long> by delegt {
override var value: MutableList<Long>
get() = internalValue
@ -808,15 +823,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableList<Long>> = object : KSerializer<MutableList<Long>> {
private val delegate = ListSerializer(Long.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableList<Long> {
return delegate.deserialize(decoder).toMutableList().observable {
return delegate.deserialize(decoder).toMutableList().observable {
onElementChanged(outerThis)
}
}
@ -834,7 +849,7 @@ internal fun Setting.valueImpl(
): MutableFloatListValue {
var internalValue: MutableList<Float> = default
val delegt = dynamicMutableList { internalValue }
val delegt = dynamicMutableList{ internalValue }
return object : MutableFloatListValue(), MutableList<Float> by delegt {
override var value: MutableList<Float>
get() = internalValue
@ -844,15 +859,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableList<Float>> = object : KSerializer<MutableList<Float>> {
private val delegate = ListSerializer(Float.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableList<Float> {
return delegate.deserialize(decoder).toMutableList().observable {
return delegate.deserialize(decoder).toMutableList().observable {
onElementChanged(outerThis)
}
}
@ -870,7 +885,7 @@ internal fun Setting.valueImpl(
): MutableDoubleListValue {
var internalValue: MutableList<Double> = default
val delegt = dynamicMutableList { internalValue }
val delegt = dynamicMutableList{ internalValue }
return object : MutableDoubleListValue(), MutableList<Double> by delegt {
override var value: MutableList<Double>
get() = internalValue
@ -880,15 +895,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableList<Double>> = object : KSerializer<MutableList<Double>> {
private val delegate = ListSerializer(Double.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableList<Double> {
return delegate.deserialize(decoder).toMutableList().observable {
return delegate.deserialize(decoder).toMutableList().observable {
onElementChanged(outerThis)
}
}
@ -906,7 +921,7 @@ internal fun Setting.valueImpl(
): MutableBooleanListValue {
var internalValue: MutableList<Boolean> = default
val delegt = dynamicMutableList { internalValue }
val delegt = dynamicMutableList{ internalValue }
return object : MutableBooleanListValue(), MutableList<Boolean> by delegt {
override var value: MutableList<Boolean>
get() = internalValue
@ -916,15 +931,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableList<Boolean>> = object : KSerializer<MutableList<Boolean>> {
private val delegate = ListSerializer(Boolean.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableList<Boolean> {
return delegate.deserialize(decoder).toMutableList().observable {
return delegate.deserialize(decoder).toMutableList().observable {
onElementChanged(outerThis)
}
}
@ -942,7 +957,7 @@ internal fun Setting.valueImpl(
): MutableCharListValue {
var internalValue: MutableList<Char> = default
val delegt = dynamicMutableList { internalValue }
val delegt = dynamicMutableList{ internalValue }
return object : MutableCharListValue(), MutableList<Char> by delegt {
override var value: MutableList<Char>
get() = internalValue
@ -952,15 +967,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableList<Char>> = object : KSerializer<MutableList<Char>> {
private val delegate = ListSerializer(Char.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableList<Char> {
return delegate.deserialize(decoder).toMutableList().observable {
return delegate.deserialize(decoder).toMutableList().observable {
onElementChanged(outerThis)
}
}
@ -978,7 +993,7 @@ internal fun Setting.valueImpl(
): MutableStringListValue {
var internalValue: MutableList<String> = default
val delegt = dynamicMutableList { internalValue }
val delegt = dynamicMutableList{ internalValue }
return object : MutableStringListValue(), MutableList<String> by delegt {
override var value: MutableList<String>
get() = internalValue
@ -988,15 +1003,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableList<String>> = object : KSerializer<MutableList<String>> {
private val delegate = ListSerializer(String.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableList<String> {
return delegate.deserialize(decoder).toMutableList().observable {
return delegate.deserialize(decoder).toMutableList().observable {
onElementChanged(outerThis)
}
}
@ -1014,7 +1029,7 @@ internal fun Setting.valueImpl(
): MutableIntSetValue {
var internalValue: MutableSet<Int> = default
val delegt = dynamicMutableSet { internalValue }
val delegt = dynamicMutableSet{ internalValue }
return object : MutableIntSetValue(), MutableSet<Int> by delegt {
override var value: MutableSet<Int>
get() = internalValue
@ -1024,15 +1039,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableSet<Int>> = object : KSerializer<MutableSet<Int>> {
private val delegate = SetSerializer(Int.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableSet<Int> {
return delegate.deserialize(decoder).toMutableSet().observable {
return delegate.deserialize(decoder).toMutableSet().observable {
onElementChanged(outerThis)
}
}
@ -1050,7 +1065,7 @@ internal fun Setting.valueImpl(
): MutableShortSetValue {
var internalValue: MutableSet<Short> = default
val delegt = dynamicMutableSet { internalValue }
val delegt = dynamicMutableSet{ internalValue }
return object : MutableShortSetValue(), MutableSet<Short> by delegt {
override var value: MutableSet<Short>
get() = internalValue
@ -1060,15 +1075,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableSet<Short>> = object : KSerializer<MutableSet<Short>> {
private val delegate = SetSerializer(Short.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableSet<Short> {
return delegate.deserialize(decoder).toMutableSet().observable {
return delegate.deserialize(decoder).toMutableSet().observable {
onElementChanged(outerThis)
}
}
@ -1086,7 +1101,7 @@ internal fun Setting.valueImpl(
): MutableByteSetValue {
var internalValue: MutableSet<Byte> = default
val delegt = dynamicMutableSet { internalValue }
val delegt = dynamicMutableSet{ internalValue }
return object : MutableByteSetValue(), MutableSet<Byte> by delegt {
override var value: MutableSet<Byte>
get() = internalValue
@ -1096,15 +1111,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableSet<Byte>> = object : KSerializer<MutableSet<Byte>> {
private val delegate = SetSerializer(Byte.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableSet<Byte> {
return delegate.deserialize(decoder).toMutableSet().observable {
return delegate.deserialize(decoder).toMutableSet().observable {
onElementChanged(outerThis)
}
}
@ -1122,7 +1137,7 @@ internal fun Setting.valueImpl(
): MutableLongSetValue {
var internalValue: MutableSet<Long> = default
val delegt = dynamicMutableSet { internalValue }
val delegt = dynamicMutableSet{ internalValue }
return object : MutableLongSetValue(), MutableSet<Long> by delegt {
override var value: MutableSet<Long>
get() = internalValue
@ -1132,15 +1147,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableSet<Long>> = object : KSerializer<MutableSet<Long>> {
private val delegate = SetSerializer(Long.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableSet<Long> {
return delegate.deserialize(decoder).toMutableSet().observable {
return delegate.deserialize(decoder).toMutableSet().observable {
onElementChanged(outerThis)
}
}
@ -1158,7 +1173,7 @@ internal fun Setting.valueImpl(
): MutableFloatSetValue {
var internalValue: MutableSet<Float> = default
val delegt = dynamicMutableSet { internalValue }
val delegt = dynamicMutableSet{ internalValue }
return object : MutableFloatSetValue(), MutableSet<Float> by delegt {
override var value: MutableSet<Float>
get() = internalValue
@ -1168,15 +1183,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableSet<Float>> = object : KSerializer<MutableSet<Float>> {
private val delegate = SetSerializer(Float.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableSet<Float> {
return delegate.deserialize(decoder).toMutableSet().observable {
return delegate.deserialize(decoder).toMutableSet().observable {
onElementChanged(outerThis)
}
}
@ -1194,7 +1209,7 @@ internal fun Setting.valueImpl(
): MutableDoubleSetValue {
var internalValue: MutableSet<Double> = default
val delegt = dynamicMutableSet { internalValue }
val delegt = dynamicMutableSet{ internalValue }
return object : MutableDoubleSetValue(), MutableSet<Double> by delegt {
override var value: MutableSet<Double>
get() = internalValue
@ -1204,15 +1219,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableSet<Double>> = object : KSerializer<MutableSet<Double>> {
private val delegate = SetSerializer(Double.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableSet<Double> {
return delegate.deserialize(decoder).toMutableSet().observable {
return delegate.deserialize(decoder).toMutableSet().observable {
onElementChanged(outerThis)
}
}
@ -1230,7 +1245,7 @@ internal fun Setting.valueImpl(
): MutableBooleanSetValue {
var internalValue: MutableSet<Boolean> = default
val delegt = dynamicMutableSet { internalValue }
val delegt = dynamicMutableSet{ internalValue }
return object : MutableBooleanSetValue(), MutableSet<Boolean> by delegt {
override var value: MutableSet<Boolean>
get() = internalValue
@ -1240,15 +1255,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableSet<Boolean>> = object : KSerializer<MutableSet<Boolean>> {
private val delegate = SetSerializer(Boolean.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableSet<Boolean> {
return delegate.deserialize(decoder).toMutableSet().observable {
return delegate.deserialize(decoder).toMutableSet().observable {
onElementChanged(outerThis)
}
}
@ -1266,7 +1281,7 @@ internal fun Setting.valueImpl(
): MutableCharSetValue {
var internalValue: MutableSet<Char> = default
val delegt = dynamicMutableSet { internalValue }
val delegt = dynamicMutableSet{ internalValue }
return object : MutableCharSetValue(), MutableSet<Char> by delegt {
override var value: MutableSet<Char>
get() = internalValue
@ -1276,15 +1291,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableSet<Char>> = object : KSerializer<MutableSet<Char>> {
private val delegate = SetSerializer(Char.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableSet<Char> {
return delegate.deserialize(decoder).toMutableSet().observable {
return delegate.deserialize(decoder).toMutableSet().observable {
onElementChanged(outerThis)
}
}
@ -1302,7 +1317,7 @@ internal fun Setting.valueImpl(
): MutableStringSetValue {
var internalValue: MutableSet<String> = default
val delegt = dynamicMutableSet { internalValue }
val delegt = dynamicMutableSet{ internalValue }
return object : MutableStringSetValue(), MutableSet<String> by delegt {
override var value: MutableSet<String>
get() = internalValue
@ -1312,15 +1327,15 @@ internal fun Setting.valueImpl(
onElementChanged(this)
}
}
private val outerThis get() = this
override val serializer: KSerializer<MutableSet<String>> = object : KSerializer<MutableSet<String>> {
private val delegate = SetSerializer(String.serializer())
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableSet<String> {
return delegate.deserialize(decoder).toMutableSet().observable {
return delegate.deserialize(decoder).toMutableSet().observable {
onElementChanged(outerThis)
}
}
@ -1344,7 +1359,7 @@ internal fun <T : Setting> Setting.valueImpl(default: T): Value<T> {
onElementChanged(this)
}
}
override val serializer = object : KSerializer<T> {
override val serializer = object : KSerializer<T>{
override val descriptor: SerialDescriptor
get() = internalValue.updaterSerializer.descriptor

View File

@ -22,13 +22,6 @@ import java.io.File
val User.isManager: Boolean
get() = this.bot.managers.contains(this.id)
@JvmName("addManager")
@JvmSynthetic
@Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
fun Bot.addManagerDeprecated(long: Long) {
addManager(long)
}
internal fun Bot.addManager(long: Long): Boolean {
TODO()
return true

View File

@ -1,161 +0,0 @@
package net.mamoe.mirai.console.utils
import net.mamoe.mirai.contact.Group
import net.mamoe.mirai.contact.Member
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* 执行N次 builder
* 成功一次就会结束
* 否则就会throw
*/
@OptIn(ExperimentalContracts::class)
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
@kotlin.internal.InlineOnly
inline fun <R> retryCatching(n: Int, block: () -> R): Result<R> {
contract {
callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
}
require(n >= 0) { "param n for retryCatching must not be negative" }
var exception: Throwable? = null
repeat(n){
try {
return Result.success(block())
} catch (e: Throwable) {
exception?.addSuppressedMirai(e)
exception = e
}
}
return Result.failure(exception!!)
}
@OptIn(ExperimentalContracts::class)
inline fun <T> tryNTimes(n: Int = 2, block: () -> T):T {
contract {
callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
}
require(n >= 0) { "param n for tryNTimes must not be negative" }
var last:Exception? = null
repeat(n){
try {
return block.invoke()
}catch (e:Exception){
last = e
}
}
//给我编译
throw last!!
}
@PublishedApi
internal fun Throwable.addSuppressedMirai(e: Throwable) {
if (e === this) {
return
}
kotlin.runCatching {
this.addSuppressed(e)
}
}
/**
* 两个字符串的近似值
* 要求前面完全一致
*
* XXXXXYYYYY.fuzzyCompare(XXXXXYYY) = 0.8
* XXXXXYYYYY.fuzzyCompare(XXXXXYYYZZ) = 0.8
*/
internal fun String.fuzzyCompare(target: String): Double {
var step = 0
if (this == target) {
return 1.0
}
if (target.length > this.length) {
return 0.0
}
for (i in this.indices) {
if (target.length == i) {
step--
}else {
if (this[i] != target[i]) {
break
}
step++
}
}
if(step == this.length-1){
return 1.0
}
return step.toDouble()/this.length
}
/**
* 模糊搜索一个List中index最接近target的东西
*/
internal inline fun <T : Any> Collection<T>.fuzzySearch(
target: String,
index: (T) -> String
): T? {
if (this.isEmpty()) {
return null
}
var potential: T? = null
var rate = 0.0
this.forEach {
val thisIndex = index(it)
if(thisIndex == target){
return it
}
with(thisIndex.fuzzyCompare(target)) {
if (this > rate) {
rate = this
potential = it
}
}
}
return potential
}
/**
* 模糊搜索一个List中index最接近target的东西
* 并且确保target是唯一的
* 如搜索index为XXXXYY list中同时存在XXXXYYY XXXXYYYY 将返回null
*/
internal inline fun <T : Any> Collection<T>.fuzzySearchOnly(
target: String,
index: (T) -> String
): T? {
if (this.isEmpty()) {
return null
}
var potential: T? = null
var rate = 0.0
var collide = 0
this.forEach {
with(index(it).fuzzyCompare(target)) {
if (this > rate) {
rate = this
potential = it
}
if(this == 1.0){
collide++
}
if(collide > 1){
return null//collide
}
}
}
return potential
}
internal fun Group.fuzzySearchMember(nameCardTarget: String): Member? {
return this.members.fuzzySearchOnly(nameCardTarget) {
it.nameCard
}
}

View File

@ -1,50 +0,0 @@
package net.mamoe.mirai.console.utils
import net.mamoe.mirai.utils.MiraiExperimentalAPI
/**
* A Value
* the input type of this Value is T while the output is V
*/
@MiraiExperimentalAPI
abstract class Value<T, V> {
operator fun invoke(): V = get()
abstract fun get(): V
abstract fun set(t: T)
}
/**
* This value can be used as a Config Value
*/
@MiraiExperimentalAPI
interface ConfigValue
/**
* A simple value
* the input type is same as output value
*/
@MiraiExperimentalAPI
open class SimpleValue<T>(
var value: T
) : Value<T, T>() {
override fun get() = this.value
override fun set(t: T) {
this.value = t
}
}
@MiraiExperimentalAPI
open class NullableSimpleValue<T>(
value: T? = null
) : SimpleValue<T?>(
value
) {
fun isNull() = value == null
}

View File

@ -0,0 +1,38 @@
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "RESULT_CLASS_IN_RETURN_TYPE")
package net.mamoe.mirai.console.utils
import org.jetbrains.annotations.Range
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
/**
* 执行 [n] [block], 在第一次成功时返回执行结果, 在捕获到异常时返回异常.
*/
@kotlin.internal.InlineOnly
inline fun <R> retryCatching(n: @Range(from = 1, to = Long.MAX_VALUE) Int, block: () -> R): Result<R> {
contract {
callsInPlace(block, InvocationKind.AT_LEAST_ONCE)
}
require(n >= 0) { "param n for retryCatching must not be negative" }
var exception: Throwable? = null
repeat(n) {
try {
return Result.success(block())
} catch (e: Throwable) {
exception?.addSuppressedMirai(e)
exception = e
}
}
return Result.failure(exception!!)
}
@PublishedApi
internal fun Throwable.addSuppressedMirai(e: Throwable) {
if (e === this) {
return
}
kotlin.runCatching {
this.addSuppressed(e)
}
}

View File

@ -1,5 +1,5 @@
import net.mamoe.mirai.console.utils.fuzzySearch
import net.mamoe.mirai.console.utils.fuzzySearchOnly
import net.mamoe.mirai.console.command.fuzzySearch
import net.mamoe.mirai.console.command.fuzzySearchOnly
class Him188(val name:String){
override fun toString(): String {