From ef17e23a0377f74784c421546d00327541b2f815 Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 15 May 2020 14:44:39 +0800 Subject: [PATCH] Settings infrastructure: Primitive Value codegen, use-site codegen, value impl codegen; Settings serializers; Values; Settings property delegation. --- backend/codegen/README.MD | 87 +++ backend/codegen/build.gradle.kts | 41 ++ .../codegen/SettingValueUseSiteCodegen.kt | 76 ++ .../mirai/console/codegen/ValueImplCodegen.kt | 102 +++ .../mirai/console/codegen/ValuesCodegen.kt | 199 ++++++ backend/mirai-console/build.gradle.kts | 2 +- .../console/plugins/ConfigSectionFactory.java | 9 - .../mirai/console/center/CuiPluginCenter.kt | 58 +- .../mirai/console/plugins/ConfigSection.kt | 617 ----------------- .../mamoe/mirai/console/plugins/PluginBase.kt | 18 - .../mamoe/mirai/console/setting/Setting.kt | 123 ++++ .../net/mamoe/mirai/console/setting/Value.kt | 16 + .../mamoe/mirai/console/setting/_Setting.kt | 55 ++ .../net/mamoe/mirai/console/setting/_Value.kt | 101 +++ .../mamoe/mirai/console/setting/_ValueImpl.kt | 652 ++++++++++++++++++ .../mamoe/mirai/console/utils/BotHelper.kt | 26 +- settings.gradle | 3 + 17 files changed, 1488 insertions(+), 697 deletions(-) create mode 100644 backend/codegen/README.MD create mode 100644 backend/codegen/build.gradle.kts create mode 100644 backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/SettingValueUseSiteCodegen.kt create mode 100644 backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt create mode 100644 backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt delete mode 100644 backend/mirai-console/src/main/java/net/mamoe/mirai/console/plugins/ConfigSectionFactory.java delete mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Value.kt create mode 100644 backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt diff --git a/backend/codegen/README.MD b/backend/codegen/README.MD new file mode 100644 index 000000000..6d5db89bc --- /dev/null +++ b/backend/codegen/README.MD @@ -0,0 +1,87 @@ +# Mirai Console +你可以在全平台运行Mirai高效率机器人框架 +### Mirai Console提供了6个版本以满足各种需要 +#### 所有版本的Mirai Console API相同 插件系统相同 + +| 名字 | 介绍 | +|:------------------------|:------------------------------| +| Mirai-Console-Pure | 最纯净版, CLI环境, 通过标准输入与标准输出 交互 | +| Mirai-Console-Terminal | (UNIX)Terminal环境 提供简洁的富文本控制台 | +| Mirai-Console-Android | 安卓APP (TODO) | +| Mirai-Console-Graphical | JavaFX的图形化界面 (.jar/.exe/.dmg) | +| Mirai-Console-WebPanel | Web Panel操作(TODO) | +| Mirai-Console-Ios | IOS APP (TODO) | + + +### 如何选择版本 +1: Mirai-Console-Pure 兼容性最高, 在其他都表现不佳的时候请使用
+2: 以系统区分 +```kotlin + return when(operatingSystem){ + WINDOWS -> listOf("Graphical","WebPanel","Pure") + MAC_OS -> listOf("Graphical","Terminal","WebPanel","Pure") + LINUX -> listOf("Terminal","Pure") + ANDROID -> listOf("Android","Pure","WebPanel") + IOS -> listOf("Ios") + else -> listOf("Pure") + } +``` +3: 以策略区分 +```kotlin + return when(task){ + 体验 -> listOf("Graphical","Terminal","WebPanel","Android","Pure") + 测试插件 -> listOf("Pure") + 调试插件 -> byOperatingSystem() + 稳定挂机 -> listOf("Terminal","Pure") + else -> listOf("Pure") + } +``` + + +#### More Importantly, Mirai Console support Plugins, tells the bot what to do +#### Mirai Console 支持插件系统, 你可以自己开发或使用公开的插件来逻辑化机器人, 如群管 +
+ +#### download 下载 +#### how to get/write plugins 如何获取/写插件 +
+
+ +### how to use(如何使用) +#### how to run Mirai Console + + + + +#### how to add plugins + + + + + diff --git a/backend/codegen/build.gradle.kts b/backend/codegen/build.gradle.kts new file mode 100644 index 000000000..574abfe7f --- /dev/null +++ b/backend/codegen/build.gradle.kts @@ -0,0 +1,41 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("kotlin") + kotlin("plugin.serialization") + id("java") +} + +kotlin { + sourceSets { + all { + languageSettings.useExperimentalAnnotation("kotlin.Experimental") + languageSettings.useExperimentalAnnotation("kotlin.OptIn") + languageSettings.progressiveMode = true + languageSettings.useExperimentalAnnotation("net.mamoe.mirai.utils.MiraiInternalAPI") + languageSettings.useExperimentalAnnotation("kotlin.ExperimentalUnsignedTypes") + languageSettings.useExperimentalAnnotation("kotlin.experimental.ExperimentalTypeInference") + languageSettings.useExperimentalAnnotation("kotlin.contracts.ExperimentalContracts") + } + } +} + +dependencies { + api(kotlin("stdlib")) +} + +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" +} \ No newline at end of file diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/SettingValueUseSiteCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/SettingValueUseSiteCodegen.kt new file mode 100644 index 000000000..99269419a --- /dev/null +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/SettingValueUseSiteCodegen.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.console.codegen + +import java.io.File + + +fun main() { + println(File("").absolutePath) // default project base dir + + File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt").apply { + createNewFile() + }.writeText(buildString { + appendln(COPYRIGHT) + appendln() + appendln(PACKAGE) + appendln() + // appendln(IMPORTS) + // appendln() + // appendln() + appendln(DO_NOT_MODIFY) + appendln() + appendln() + appendln(genAllValueUseSite()) + }) +} + +private val DO_NOT_MODIFY = """ +/** + * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.SettingValueUseSiteCodegen.kt + * !!! DO NOT MODIFY THIS FILE MANUALLY + */ +""".trimIndent() + +private val PACKAGE = """ +package net.mamoe.mirai.console.setting +""".trimIndent() + +private val IMPORTS = """ +import kotlinx.serialization.builtins.* +""".trimIndent() + +fun genAllValueUseSite(): String = buildString { + // PRIMITIVE + for (number in NUMBERS + OTHER_PRIMITIVES) { + appendln(genValueUseSite(number, number)) + } + + // PRIMITIVE ARRAYS + for (number in NUMBERS + OTHER_PRIMITIVES.filterNot { it == "String" }) { + appendln(genValueUseSite("${number}Array", "${number}Array")) + } + + // TYPED ARRAYS + for (number in NUMBERS + OTHER_PRIMITIVES) { + appendln(genValueUseSite("Array<${number}>", "Typed${number}Array")) + } + + // PRIMITIVE LISTS + for (number in NUMBERS + OTHER_PRIMITIVES) { + appendln(genValueUseSite("List<${number}>", "${number}List")) + } +} + +fun genValueUseSite(kotlinTypeName: String, miraiValueName: String): String = + """ + fun Setting.value(default: $kotlinTypeName): ${miraiValueName}Value = valueImpl(default) + """.trimIndent() + diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt new file mode 100644 index 000000000..7d40123f1 --- /dev/null +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValueImplCodegen.kt @@ -0,0 +1,102 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.console.codegen + +import java.io.File + + +fun main() { + println(File("").absolutePath) // default project base dir + + File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt").apply { + createNewFile() + }.writeText(buildString { + appendln(COPYRIGHT) + appendln() + appendln(PACKAGE) + appendln() + appendln(IMPORTS) + appendln() + appendln() + appendln(DO_NOT_MODIFY) + appendln() + appendln() + appendln(genAllValueImpl()) + }) +} + +private val DO_NOT_MODIFY = """ +/** + * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.ValueImplCodegen.kt + * !!! DO NOT MODIFY THIS FILE MANUALLY + */ +""".trimIndent() + +private val PACKAGE = """ +package net.mamoe.mirai.console.setting +""".trimIndent() + +private val IMPORTS = """ +import kotlinx.serialization.builtins.* +""".trimIndent() + +fun genAllValueImpl(): String = buildString { + // PRIMITIVE + for (number in NUMBERS + OTHER_PRIMITIVES) { + appendln(genValueImpl(number, number, "$number.serializer()", false)) + } + + // PRIMITIVE ARRAYS + for (number in NUMBERS + OTHER_PRIMITIVES.filterNot { it == "String" }) { + appendln(genValueImpl("${number}Array", "${number}Array", "${number}ArraySerializer()", true)) + } + + // TYPED ARRAYS + for (number in NUMBERS + OTHER_PRIMITIVES) { + appendln(genValueImpl("Array<${number}>", "Typed${number}Array", "ArraySerializer(${number}.serializer())", true)) + } + + // PRIMITIVE LISTS + for (number in NUMBERS + OTHER_PRIMITIVES) { + appendln(genValueImpl("List<${number}>", "${number}List", "ListSerializer(${number}.serializer())", false)) + } +} + +fun genValueImpl(kotlinTypeName: String, miraiValueName: String, serializer: String, isArray: Boolean): String = + """ + internal fun Setting.valueImpl(default: ${kotlinTypeName}): ${miraiValueName}Value { + return object : ${miraiValueName}Value() { + private var internalValue: $kotlinTypeName = default + override var value: $kotlinTypeName + get() = internalValue + set(new) { + ${ + if (isArray) """ + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + """.trim() + else """ + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + """.trim() + } + } + override val serializer = ${serializer}.bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } + } + """.trimIndent() + "\n" + diff --git a/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt new file mode 100644 index 000000000..4d75b33d2 --- /dev/null +++ b/backend/codegen/src/main/kotlin/net/mamoe/mirai/console/codegen/ValuesCodegen.kt @@ -0,0 +1,199 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ +@file:Suppress("ClassName") + +package net.mamoe.mirai.console.codegen + +import org.intellij.lang.annotations.Language +import java.io.File + +fun main() { + println(File("").absolutePath) // default project base dir + + File("backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Value.kt").apply { + createNewFile() + }.writeText(genPublicApi()) +} + +internal const val COPYRIGHT = """ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ +""" + +internal val NUMBERS = listOf( + "Int", + "Short", + "Byte", + "Long", + "Float", + "Double" +) + +internal val UNSIGNED_NUMBERS = listOf( + "UInt", + "UShort", + "UByte", + "ULong" +) + +internal val OTHER_PRIMITIVES = listOf( + "Boolean", + "Char", + "String" +) + +fun genPublicApi() = buildString { + fun appendln(@Language("kt") code: String){ + this.appendln(code.trimIndent()) + } + + appendln(COPYRIGHT.trim()) + appendln() + appendln( + """ + package net.mamoe.mirai.console.setting + + import kotlinx.serialization.KSerializer + import kotlin.properties.ReadWriteProperty + import kotlin.reflect.KProperty + """ + ) + appendln() + appendln( + """ + /** + * !!! These primitive types are auto-generated by backend/codegen/src/main/kotlin/net.mamoe.mirai.console.codegen.ValuesCodegen.kt + * !!! for better performance + * !!! DO NOT MODIFY THIS FILE MANUALLY + */ + """ + ) + appendln() + + appendln( + """ +sealed class Value : ReadWriteProperty { + abstract var value: T + + abstract val serializer: KSerializer + override fun getValue(thisRef: Setting, property: KProperty<*>): T = value + override fun setValue(thisRef: Setting, property: KProperty<*>, value: T) { + this.value = value + } +} + """ + ) + appendln() + + // PRIMITIVES + + appendln( + """ + sealed class PrimitiveValue : Value() + + sealed class NumberValue : Value() + """ + ) + + for (number in NUMBERS) { + val template = """ + abstract class ${number}Value internal constructor() : NumberValue<${number}>() + """ + + appendln(template) + } + + appendln() + + for (number in OTHER_PRIMITIVES) { + val template = """ + abstract class ${number}Value internal constructor() : PrimitiveValue<${number}>() + """ + + appendln(template) + } + + appendln() + + // ARRAYS + + appendln( + """ + // T can be primitive array or typed Array + sealed class ArrayValue : Value() + """ + ) + + // PRIMITIVE ARRAYS + appendln( + """ + sealed class PrimitiveArrayValue : ArrayValue() + """ + ) + appendln() + + for (number in (NUMBERS + OTHER_PRIMITIVES).filterNot { it == "String" }) { + val template = """ + abstract class ${number}ArrayValue internal constructor() : PrimitiveArrayValue<${number}Array>(), Iterable<${number}> { + override fun iterator(): Iterator<${number}> = this.value.iterator() + } + """ + + appendln(template) + } + + appendln() + + // TYPED ARRAYS + + appendln( + """ + sealed class TypedPrimitiveArrayValue : ArrayValue>() , Iterable{ + override fun iterator() = this.value.iterator() + } + """ + ) + appendln() + + for (number in (NUMBERS + OTHER_PRIMITIVES)) { + val template = """ + abstract class Typed${number}ArrayValue internal constructor() : TypedPrimitiveArrayValue<${number}>() + """ + + appendln(template) + } + + appendln() + + // TYPED LISTS + + appendln( + """ + sealed class ListValue : Value>(), Iterable{ + override fun iterator() = this.value.iterator() + } + """ + ) + + for (number in (NUMBERS + OTHER_PRIMITIVES)) { + val template = """ + abstract class ${number}ListValue internal constructor() : ListValue<${number}>() + """ + + appendln(template) + } + + appendln() +} \ No newline at end of file diff --git a/backend/mirai-console/build.gradle.kts b/backend/mirai-console/build.gradle.kts index 124050c15..60572b5fd 100644 --- a/backend/mirai-console/build.gradle.kts +++ b/backend/mirai-console/build.gradle.kts @@ -32,7 +32,7 @@ dependencies { compileAndRuntime("net.mamoe:mirai-core:${Versions.Mirai.core}") compileAndRuntime(kotlin("stdlib")) - api("moe.him188.yamlkt:yamlkt:0.2.0") + api("net.mamoe.yamlkt:yamlkt:0.2.0") api("org.jsoup:jsoup:1.12.1") diff --git a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/plugins/ConfigSectionFactory.java b/backend/mirai-console/src/main/java/net/mamoe/mirai/console/plugins/ConfigSectionFactory.java deleted file mode 100644 index 0f1877e06..000000000 --- a/backend/mirai-console/src/main/java/net/mamoe/mirai/console/plugins/ConfigSectionFactory.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.mamoe.mirai.console.plugins; - -public class ConfigSectionFactory { - - public static ConfigSection create(){ - return ConfigSection.Companion.create(); - } - -} diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt index faced21a0..60b041c17 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/center/CuiPluginCenter.kt @@ -1,12 +1,10 @@ package net.mamoe.mirai.console.center -import com.google.gson.JsonArray -import com.google.gson.JsonObject -import com.google.gson.JsonParser import io.ktor.client.HttpClient import io.ktor.client.engine.cio.CIO import io.ktor.client.request.get import io.ktor.util.KtorExperimentalAPI +import kotlinx.serialization.json.* import net.mamoe.mirai.console.utils.retryCatching import java.io.File @@ -26,20 +24,20 @@ internal object CuiPluginCenter : PluginCenter { if (plugins == null) { refresh() } - if (it >= plugins!!.size()) { + if (it >= plugins!!.size) { return@forEach } val info = plugins!![it] - with(info.asJsonObject) { - map[this.get("name").asString] = PluginCenter.PluginInsight( - this.get("name")?.asString ?: "", - this.get("version")?.asString ?: "", - this.get("core")?.asString ?: "", - this.get("console")?.asString ?: "", - this.get("author")?.asString ?: "", - this.get("description")?.asString ?: "", - this.get("tags")?.asJsonArray?.map { it.asString } ?: arrayListOf(), - this.get("commands")?.asJsonArray?.map { it.asString } ?: arrayListOf() + with(info.jsonObject) { + map[this["name"]!!.toString()] = PluginCenter.PluginInsight( + this["name"]?.primitive?.content ?: "", + this["version"]?.primitive?.content ?: "", + this["core"]?.primitive?.content ?: "", + this["console"]?.primitive?.content ?: "", + this["author"]?.primitive?.content ?: "", + this["description"]?.primitive?.content ?: "", + this["tags"]?.jsonArray?.map { it.primitive.content } ?: arrayListOf(), + this["commands"]?.jsonArray?.map { it.primitive.content } ?: arrayListOf() ) } } @@ -62,18 +60,18 @@ internal object CuiPluginCenter : PluginCenter { return result.asJson().run { PluginCenter.PluginInfo( - this.get("name")?.asString ?: "", - this.get("version")?.asString ?: "", - this.get("core")?.asString ?: "", - this.get("console")?.asString ?: "", - this.get("tags")?.asJsonArray?.map { it.asString } ?: arrayListOf(), - this.get("author")?.asString ?: "", - this.get("contact")?.asString ?: "", - this.get("description")?.asString ?: "", - this.get("usage")?.asString ?: "", - this.get("vsc")?.asString ?: "", - this.get("commands")?.asJsonArray?.map { it.asString } ?: arrayListOf(), - this.get("changeLog")?.asJsonArray?.map { it.asString } ?: arrayListOf() + this["name"]?.primitive?.content ?: "", + this["version"]?.primitive?.content ?: "", + this["core"]?.primitive?.content ?: "", + this["console"]?.primitive?.content ?: "", + this["tags"]?.jsonArray?.map { it.primitive.content } ?: arrayListOf(), + this["author"]?.primitive?.content ?: "", + this["contact"]?.primitive?.content ?: "", + this["description"]?.primitive?.content ?: "", + this["usage"]?.primitive?.content ?: "", + this["vsc"]?.primitive?.content ?: "", + this["commands"]?.jsonArray?.map { it.primitive.content } ?: arrayListOf(), + this["changeLog"]?.jsonArray?.map { it.primitive.content } ?: arrayListOf() ) } @@ -82,10 +80,10 @@ internal object CuiPluginCenter : PluginCenter { override suspend fun refresh() { val results = Http.get("https://miraiapi.jasonczc.cn/getPluginList").asJson() - if (!(results.has("success") && results["success"].asBoolean)) { + if (!(results.containsKey("success") && results["success"]?.boolean == true)) { error("Failed to fetch plugin list from Cui Cloud") } - plugins = results.get("result").asJsonArray//先不解析 + plugins = results["result"]?.jsonArray//先不解析 } override suspend fun T.downloadPlugin(name: String, progressListener: T.(Float) -> Unit): File { @@ -118,8 +116,10 @@ internal object CuiPluginCenter : PluginCenter { get() = "崔云" + private val json = Json(JsonConfiguration.Stable) + private fun String.asJson(): JsonObject { - return JsonParser.parseString(this).asJsonObject + return json.parseJson(this).jsonObject } } diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt deleted file mode 100644 index ba8225913..000000000 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt +++ /dev/null @@ -1,617 +0,0 @@ -/* - * Copyright 2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -@file:Suppress("MemberVisibilityCanBePrivate") - -package net.mamoe.mirai.console.plugins - -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken -import com.moandjiezana.toml.Toml -import com.moandjiezana.toml.TomlWriter -import kotlinx.serialization.Serializable -import kotlinx.serialization.UnstableDefault -import net.mamoe.mirai.console.encodeToString -import net.mamoe.mirai.utils.MiraiInternalAPI -import org.yaml.snakeyaml.Yaml -import java.io.File -import java.io.InputStream -import java.util.* -import java.util.concurrent.ConcurrentHashMap -import kotlin.NoSuchElementException -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KClass -import kotlin.reflect.KProperty -import kotlin.reflect.full.isSubclassOf - - -/** - * 标注一个即将在将来版本删除的 API - * - * 目前 [Config] 的读写设计语义不明, list 和 map 读取效率低, 属性委托写入语义不明 - * 将在未来重写并变为 `ReadOnlyConfig` 和 `ReadWriteConfig`. - * 并计划添加图形端配置绑定, 和带注释的序列化, 自动保存的配置文件, 与指令绑定的属性. - */ -@RequiresOptIn("将在未来进行不兼容的修改", RequiresOptIn.Level.WARNING) -annotation class ToBeRemoved - -/** - * 可读可写配置文件. - * - * @suppress 注意: 配置文件正在进行不兼容的修改 - */ -interface Config { - @ToBeRemoved - fun getConfigSection(key: String): ConfigSection - - fun getString(key: String): String - fun getInt(key: String): Int - fun getFloat(key: String): Float - fun getDouble(key: String): Double - fun getLong(key: String): Long - fun getBoolean(key: String): Boolean - - @ToBeRemoved - fun getList(key: String): List<*> - - - @ToBeRemoved - fun getStringList(key: String): List - - @ToBeRemoved - fun getIntList(key: String): List - - @ToBeRemoved - fun getFloatList(key: String): List - - @ToBeRemoved - fun getDoubleList(key: String): List - - @ToBeRemoved - fun getLongList(key: String): List - - @ToBeRemoved - fun getConfigSectionList(key: String): List - - operator fun set(key: String, value: Any) - operator fun get(key: String): Any? - operator fun contains(key: String): Boolean - - @ToBeRemoved - fun exist(key: String): Boolean - - /** - * 设置 key = value (如果value不存在则valueInitializer会被调用) - * 之后返回当前key对应的值 - * */ - @ToBeRemoved - fun setIfAbsent(key: String, value: T) - - @ToBeRemoved - fun setIfAbsent(key: String, valueInitializer: Config.() -> T) - - @ToBeRemoved - fun asMap(): Map - - @ToBeRemoved - fun save() - - companion object { - @ToBeRemoved - fun load(fileName: String): Config { - return load( - File( - fileName.replace( - "//", - "/" - ) - ) - ) - } - - /** - * create a read-write config - * */ - @ToBeRemoved - fun load(file: File): Config { - if (!file.exists()) { - file.createNewFile() - } - return when (file.extension.toLowerCase()) { - "json" -> JsonConfig(file) - "yml" -> YamlConfig(file) - "yaml" -> YamlConfig(file) - "mirai" -> YamlConfig(file) - "ini" -> TomlConfig(file) - "toml" -> TomlConfig(file) - "properties" -> TomlConfig(file) - "property" -> TomlConfig(file) - "data" -> TomlConfig(file) - else -> error("Unsupported file config type ${file.extension.toLowerCase()}") - } - } - - /** - * create a read-only config - */ - @ToBeRemoved - fun load(content: String, type: String): Config { - return when (type.toLowerCase()) { - "json" -> JsonConfig(content) - "yml" -> YamlConfig(content) - "yaml" -> YamlConfig(content) - "mirai" -> YamlConfig(content) - "ini" -> TomlConfig(content) - "toml" -> TomlConfig(content) - "properties" -> TomlConfig(content) - "property" -> TomlConfig(content) - "data" -> TomlConfig(content) - else -> error("Unsupported file config type $content") - } - } - - /** - * create a read-only config - */ - @ToBeRemoved - fun load(inputStream: InputStream, type: String): Config { - return load(inputStream.readBytes().encodeToString(), type) - } - - } -} - - -@ToBeRemoved -fun File.loadAsConfig(): Config { - return Config.load(this) -} - -/* 最简单的代理 */ -@ToBeRemoved -inline operator fun Config.getValue(thisRef: Any?, property: KProperty<*>): T { - return smartCast(property) -} - -@ToBeRemoved -inline operator fun Config.setValue(thisRef: Any?, property: KProperty<*>, value: T) { - this[property.name] = value -} - -/* 带有默认值的代理 */ -@Suppress("unused") -@ToBeRemoved -inline fun Config.withDefault( - crossinline defaultValue: () -> T -): ReadWriteProperty { - return object : ReadWriteProperty { - override fun getValue(thisRef: Any, property: KProperty<*>): T { - if (this@withDefault.exist(property.name)) {//unsafe - return this@withDefault.smartCast(property) - } - return defaultValue() - } - - override fun setValue(thisRef: Any, property: KProperty<*>, value: T) { - this@withDefault[property.name] = value - } - } -} - -/* 带有默认值且如果为空会写入的代理 */ -@Suppress("unused") -@ToBeRemoved -inline fun Config.withDefaultWrite( - noinline defaultValue: () -> T -): WithDefaultWriteLoader { - return WithDefaultWriteLoader( - T::class, - this, - defaultValue, - false - ) -} - -/* 带有默认值且如果为空会写入保存的代理 */ -@ToBeRemoved -inline fun Config.withDefaultWriteSave( - noinline defaultValue: () -> T -): WithDefaultWriteLoader { - return WithDefaultWriteLoader(T::class, this, defaultValue, true) -} - -@ToBeRemoved -class WithDefaultWriteLoader( - private val _class: KClass, - private val config: Config, - private val defaultValue: () -> T, - private val save: Boolean -) { - operator fun provideDelegate( - thisRef: Any, - prop: KProperty<*> - ): ReadWriteProperty { - val defaultValue by lazy { defaultValue.invoke() } - if (!config.contains(prop.name)) { - config[prop.name] = defaultValue - if (save) { - config.save() - } - } - return object : ReadWriteProperty { - override fun getValue(thisRef: Any, property: KProperty<*>): T { - if (config.exist(property.name)) {//unsafe - return config.smartCastInternal(property.name, _class) - } - return defaultValue - } - - override fun setValue(thisRef: Any, property: KProperty<*>, value: T) { - config[property.name] = value - } - } - } -} - -@PublishedApi -internal inline fun Config.smartCast(property: KProperty<*>): T { - return smartCastInternal(property.name, T::class) -} - -@OptIn(ToBeRemoved::class) -@PublishedApi -@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST") -internal fun Config.smartCastInternal(propertyName: String, _class: KClass): T { - return when (_class) { - String::class -> this.getString(propertyName) - Int::class -> this.getInt(propertyName) - Float::class -> this.getFloat(propertyName) - Double::class -> this.getDouble(propertyName) - Long::class -> this.getLong(propertyName) - Boolean::class -> this.getBoolean(propertyName) - else -> when { - _class.isSubclassOf(ConfigSection::class) -> this.getConfigSection(propertyName) - _class == List::class || _class == MutableList::class -> { - val list = this.getList(propertyName) - return if (list.isEmpty()) { - list - } else { - when (list[0]!!::class) { - String::class -> getStringList(propertyName) - Int::class -> getIntList(propertyName) - Float::class -> getFloatList(propertyName) - Double::class -> getDoubleList(propertyName) - Long::class -> getLongList(propertyName) - //不去支持getConfigSectionList(propertyName) - // LinkedHashMap::class -> getConfigSectionList(propertyName)//faster approach - else -> { - //if(list[0]!! is ConfigSection || list[0]!! is Map<*,*>){ - // getConfigSectionList(propertyName) - //}else { - error("unsupported type" + list[0]!!::class) - //} - } - } - } as T - } - else -> { - error("unsupported type") - } - } - } as T -} - - -@ToBeRemoved -interface ConfigSection : Config, MutableMap { - companion object { - fun create(): ConfigSection { - return ConfigSectionImpl() - } - - fun new(): ConfigSection { - return this.create() - } - } - - override fun getConfigSection(key: String): ConfigSection { - val content = get(key) ?: throw NoSuchElementException(key) - if (content is ConfigSection) { - return content - } - @Suppress("UNCHECKED_CAST") - return ConfigSectionDelegation( - Collections.synchronizedMap( - (get(key) ?: throw NoSuchElementException(key)) as LinkedHashMap - ) - ) - } - - override fun getString(key: String): String { - return (get(key) ?: throw NoSuchElementException(key)).toString() - } - - override fun getInt(key: String): Int { - return (get(key) ?: throw NoSuchElementException(key)).toString().toInt() - } - - override fun getFloat(key: String): Float { - return (get(key) ?: throw NoSuchElementException(key)).toString().toFloat() - } - - override fun getBoolean(key: String): Boolean { - return (get(key) ?: throw NoSuchElementException(key)).toString().toBoolean() - } - - override fun getDouble(key: String): Double { - return (get(key) ?: throw NoSuchElementException(key)).toString().toDouble() - } - - override fun getLong(key: String): Long { - return (get(key) ?: throw NoSuchElementException(key)).toString().toLong() - } - - override fun getList(key: String): List<*> { - return ((get(key) ?: throw NoSuchElementException(key)) as List<*>) - } - - override fun getStringList(key: String): List { - return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).filterNotNull().map { it.toString() } - } - - override fun getIntList(key: String): List { - return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map { it.toString().toInt() } - } - - override fun getFloatList(key: String): List { - return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map { it.toString().toFloat() } - } - - override fun getDoubleList(key: String): List { - return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map { it.toString().toDouble() } - } - - override fun getLongList(key: String): List { - return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map { it.toString().toLong() } - } - - override fun getConfigSectionList(key: String): List { - return ((get(key) ?: throw NoSuchElementException(key)) as List<*>).map { - if (it is ConfigSection) { - it - } else { - @Suppress("UNCHECKED_CAST") - ConfigSectionDelegation( - Collections.synchronizedMap( - it as MutableMap - ) - ) - } - } - } - - override fun exist(key: String): Boolean { - return get(key) != null - } - - override fun setIfAbsent(key: String, value: T) { - putIfAbsent(key, value) - } - - override fun setIfAbsent(key: String, valueInitializer: Config.() -> T) { - if (this.exist(key)) { - put(key, valueInitializer.invoke(this)) - } - } -} - -@OptIn(ToBeRemoved::class) -internal inline fun ConfigSection.smartGet(key: String): T { - return this.smartCastInternal(key, T::class) -} - -@Serializable -@ToBeRemoved -open class ConfigSectionImpl : ConcurrentHashMap(), - - ConfigSection { - override fun set(key: String, value: Any) { - super.put(key, value) - } - - override operator fun get(key: String): Any? { - return super.get(key) - } - - @Suppress("RedundantOverride") - override fun contains(key: String): Boolean { - return super.contains(key) - } - - override fun exist(key: String): Boolean { - return containsKey(key) - } - - override fun asMap(): Map { - return this - } - - override fun save() { - - } -} - -@ToBeRemoved -open class ConfigSectionDelegation( - private val delegate: MutableMap -) : ConfigSection, MutableMap by delegate { - override fun set(key: String, value: Any) { - delegate[key] = value - } - - override fun contains(key: String): Boolean { - return delegate.containsKey(key) - } - - override fun asMap(): Map { - return delegate - } - - override fun save() { - - } -} - - -@ToBeRemoved -interface FileConfig : Config { - fun deserialize(content: String): ConfigSection - - fun serialize(config: ConfigSection): String -} - - -@MiraiInternalAPI -@ToBeRemoved -abstract class FileConfigImpl internal constructor( - private val rawContent: String -) : FileConfig, - ConfigSection { - - internal var file: File? = null - - - @Suppress("unused") - constructor(file: File) : this(file.readText()) { - this.file = file - } - - - private val content by lazy { - deserialize(rawContent) - } - - - override val size: Int get() = content.size - override val entries: MutableSet> get() = content.entries - override val keys: MutableSet get() = content.keys - override val values: MutableCollection get() = content.values - override fun containsKey(key: String): Boolean = content.containsKey(key) - override fun containsValue(value: Any): Boolean = content.containsValue(value) - override fun put(key: String, value: Any): Any? = content.put(key, value) - override fun isEmpty(): Boolean = content.isEmpty() - override fun putAll(from: Map) = content.putAll(from) - override fun clear() = content.clear() - override fun remove(key: String): Any? = content.remove(key) - - override fun save() { - if (isReadOnly) { - error("Config is readonly") - } - if (!((file?.exists())!!)) { - file?.createNewFile() - } - file?.writeText(serialize(content)) - } - - val isReadOnly: Boolean get() = file == null - - override fun contains(key: String): Boolean { - return content.contains(key) - } - - override fun get(key: String): Any? { - return content[key] - } - - override fun set(key: String, value: Any) { - content[key] = value - } - - override fun asMap(): Map { - return content.asMap() - } - -} - -@ToBeRemoved -@OptIn(MiraiInternalAPI::class) -class JsonConfig internal constructor( - content: String -) : FileConfigImpl(content) { - constructor(file: File) : this(file.readText()) { - this.file = file - } - - @UnstableDefault - override fun deserialize(content: String): ConfigSection { - if (content.isEmpty() || content.isBlank() || content == "{}") { - return ConfigSectionImpl() - } - val gson = Gson() - val typeRef = object : TypeToken>() {}.type - return ConfigSectionDelegation( - gson.fromJson(content, typeRef) - ) - } - - @UnstableDefault - override fun serialize(config: ConfigSection): String { - val gson = Gson() - return gson.toJson(config.toMap()) - } -} - -@ToBeRemoved -@OptIn(MiraiInternalAPI::class) -class YamlConfig internal constructor(content: String) : FileConfigImpl(content) { - constructor(file: File) : this(file.readText()) { - this.file = file - } - - override fun deserialize(content: String): ConfigSection { - if (content.isEmpty() || content.isBlank()) { - return ConfigSectionImpl() - } - return ConfigSectionDelegation( - Collections.synchronizedMap( - Yaml().load(content) as LinkedHashMap - ) - ) - } - - override fun serialize(config: ConfigSection): String { - return Yaml().dumpAsMap(config) - } - -} - -@ToBeRemoved -@OptIn(MiraiInternalAPI::class) -class TomlConfig internal constructor(content: String) : FileConfigImpl(content) { - constructor(file: File) : this(file.readText()) { - this.file = file - } - - override fun deserialize(content: String): ConfigSection { - if (content.isEmpty() || content.isBlank()) { - return ConfigSectionImpl() - } - return ConfigSectionDelegation( - Collections.synchronizedMap( - Toml().read(content).toMap() - ) - ) - - } - - override fun serialize(config: ConfigSection): String { - return TomlWriter().write(config) - } -} \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt index f97eb092b..61c11d4b4 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt @@ -70,14 +70,6 @@ abstract class PluginBase } - /** - * 加载一个 [dataFolder] 中的 [Config] - */ - fun loadConfig(fileName: String): Config { - @OptIn(ToBeRemoved::class) - return Config.load(dataFolder.absolutePath + "/" + fileName) - } - /** * 插件的日志 */ @@ -110,16 +102,6 @@ abstract class PluginBase } } - /** - * 加载 resource 中的 [Config] - * 这个 [Config] 是只读的 - */ - @ToBeRemoved - fun getResourcesConfig(fileName: String): Config { - require(fileName.contains(".")) { "Unknown Config Type" } - @OptIn(ToBeRemoved::class) - return Config.load(getResources(fileName) ?: error("No such file: $fileName"), fileName.substringAfter('.')) - } /** * Java API Scheduler diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt new file mode 100644 index 000000000..121fa563f --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Setting.kt @@ -0,0 +1,123 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +@file:Suppress("NOTHING_TO_INLINE") + +package net.mamoe.mirai.console.setting + +import kotlinx.serialization.* +import net.mamoe.yamlkt.Yaml +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty +import kotlin.reflect.full.findAnnotation + +internal inline fun KSerializer.bind( + crossinline setter: (E) -> Unit, + crossinline getter: () -> E +): KSerializer { + return object : KSerializer { + 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()) + } +} + +typealias SerialName = kotlinx.serialization.SerialName + +/** + * 配置的基类 + */ +abstract class Setting { + open val serialName: String + get() = this::class.findAnnotation()?.value + ?: this::class.qualifiedName + ?: error("Names should be assigned to anonymous classes manually by overriding serialName") + + + @JvmSynthetic + operator fun Value.provideDelegate( + thisRef: Setting, + property: KProperty<*> + ): ReadWriteProperty { + if (built) error("The Setting is already serialized so it's structure is immutable.") + valueList.add(this to property) + return this + } + + @JvmField + internal var valueList: MutableList, KProperty<*>>> = mutableListOf() + private var built: Boolean = false + + internal val serializer: KSerializer by lazy { + built = true + SettingSerializer(this) + } + + internal fun onElementChanged(value: Value<*>) { + println("my value changed!") + } +} + +internal class SettingSerializer( + private val instance: Setting +) : KSerializer { + override val descriptor: SerialDescriptor by lazy { + SerialDescriptor(instance.serialName) { + for ((value, property) in instance.valueList) { + element(property.serialNameOrPropertyName, value.serializer.descriptor, annotations, true) + } + } + } + + @Suppress("UNCHECKED_CAST") // erased, no problem. + override fun deserialize(decoder: Decoder): SettingSerializerMark = decoder.decodeStructure(descriptor) { + if (this.decodeSequentially()) { + instance.valueList.forEachIndexed { index, (value, _) -> + this.decodeSerializableElement( + value.serializer.descriptor, + index, + value.serializer as KSerializer + ) + } + } else { + while (true) { + val index = this.decodeElementIndex(descriptor) + if (index == CompositeDecoder.READ_DONE) return@decodeStructure SettingSerializerMark + val value = instance.valueList[index].first + + this.decodeSerializableElement( + descriptor, + index, + value.serializer + ) + } + } + SettingSerializerMark + } + + override fun serialize(encoder: Encoder, value: SettingSerializerMark) = encoder.encodeStructure(descriptor) { + instance.valueList.forEachIndexed { index, (value, _) -> + @Suppress("UNCHECKED_CAST") // erased, no problem. + this.encodeSerializableElement( + descriptor, + index, + value.serializer as KSerializer, + value.value + ) + } + } + +} + +internal object SettingSerializerMark + +internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation()?.value ?: this.name \ No newline at end of file diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt new file mode 100644 index 000000000..989ece08b --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/Value.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.console.setting + +object ValueSerializerMark + +/* + * More generic ones + */ diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt new file mode 100644 index 000000000..83dbe7345 --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Setting.kt @@ -0,0 +1,55 @@ + +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + + +package net.mamoe.mirai.console.setting + +/** + * !!! This file is auto-generated by backend/codegen/src/kotlin/net.mamoe.mirai.console.codegen.SettingValueUseSiteCodegen.kt + * !!! DO NOT MODIFY THIS FILE MANUALLY + */ + + +fun Setting.value(default: Int): IntValue = valueImpl(default) +fun Setting.value(default: Short): ShortValue = valueImpl(default) +fun Setting.value(default: Byte): ByteValue = valueImpl(default) +fun Setting.value(default: Long): LongValue = valueImpl(default) +fun Setting.value(default: Float): FloatValue = valueImpl(default) +fun Setting.value(default: Double): DoubleValue = valueImpl(default) +fun Setting.value(default: Boolean): BooleanValue = valueImpl(default) +fun Setting.value(default: Char): CharValue = valueImpl(default) +fun Setting.value(default: String): StringValue = valueImpl(default) +fun Setting.value(default: IntArray): IntArrayValue = valueImpl(default) +fun Setting.value(default: ShortArray): ShortArrayValue = valueImpl(default) +fun Setting.value(default: ByteArray): ByteArrayValue = valueImpl(default) +fun Setting.value(default: LongArray): LongArrayValue = valueImpl(default) +fun Setting.value(default: FloatArray): FloatArrayValue = valueImpl(default) +fun Setting.value(default: DoubleArray): DoubleArrayValue = valueImpl(default) +fun Setting.value(default: BooleanArray): BooleanArrayValue = valueImpl(default) +fun Setting.value(default: CharArray): CharArrayValue = valueImpl(default) +fun Setting.value(default: Array): TypedIntArrayValue = valueImpl(default) +fun Setting.value(default: Array): TypedShortArrayValue = valueImpl(default) +fun Setting.value(default: Array): TypedByteArrayValue = valueImpl(default) +fun Setting.value(default: Array): TypedLongArrayValue = valueImpl(default) +fun Setting.value(default: Array): TypedFloatArrayValue = valueImpl(default) +fun Setting.value(default: Array): TypedDoubleArrayValue = valueImpl(default) +fun Setting.value(default: Array): TypedBooleanArrayValue = valueImpl(default) +fun Setting.value(default: Array): TypedCharArrayValue = valueImpl(default) +fun Setting.value(default: Array): TypedStringArrayValue = valueImpl(default) +fun Setting.value(default: List): IntListValue = valueImpl(default) +fun Setting.value(default: List): ShortListValue = valueImpl(default) +fun Setting.value(default: List): ByteListValue = valueImpl(default) +fun Setting.value(default: List): LongListValue = valueImpl(default) +fun Setting.value(default: List): FloatListValue = valueImpl(default) +fun Setting.value(default: List): DoubleListValue = valueImpl(default) +fun Setting.value(default: List): BooleanListValue = valueImpl(default) +fun Setting.value(default: List): CharListValue = valueImpl(default) +fun Setting.value(default: List): StringListValue = valueImpl(default) + diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Value.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Value.kt new file mode 100644 index 000000000..3e052566a --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_Value.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.console.setting + +import kotlinx.serialization.KSerializer +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +/** + * !!! These primitive types are auto-generated by backend/codegen/src/main/kotlin/net.mamoe.mirai.console.ValuesCodegen.kt + * !!! for better performance + * !!! DO NOT MODIFY THIS FILE MANUALLY + */ + +sealed class Value : ReadWriteProperty { + abstract var value: T + + abstract val serializer: KSerializer + override fun getValue(thisRef: Setting, property: KProperty<*>): T = value + override fun setValue(thisRef: Setting, property: KProperty<*>, value: T) { + this.value = value + } +} + +sealed class PrimitiveValue : Value() + +sealed class NumberValue : Value() +abstract class IntValue internal constructor() : NumberValue() +abstract class ShortValue internal constructor() : NumberValue() +abstract class ByteValue internal constructor() : NumberValue() +abstract class LongValue internal constructor() : NumberValue() +abstract class FloatValue internal constructor() : NumberValue() +abstract class DoubleValue internal constructor() : NumberValue() + +abstract class BooleanValue internal constructor() : PrimitiveValue() +abstract class CharValue internal constructor() : PrimitiveValue() +abstract class StringValue internal constructor() : PrimitiveValue() + +// T can be primitive array or typed Array +sealed class ArrayValue : Value() +sealed class PrimitiveArrayValue : ArrayValue() + +abstract class IntArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { + override fun iterator(): Iterator = this.value.iterator() +} +abstract class ShortArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { + override fun iterator(): Iterator = this.value.iterator() +} +abstract class ByteArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { + override fun iterator(): Iterator = this.value.iterator() +} +abstract class LongArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { + override fun iterator(): Iterator = this.value.iterator() +} +abstract class FloatArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { + override fun iterator(): Iterator = this.value.iterator() +} +abstract class DoubleArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { + override fun iterator(): Iterator = this.value.iterator() +} +abstract class BooleanArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { + override fun iterator(): Iterator = this.value.iterator() +} +abstract class CharArrayValue internal constructor() : PrimitiveArrayValue(), Iterable { + override fun iterator(): Iterator = this.value.iterator() +} + +sealed class TypedPrimitiveArrayValue : ArrayValue>() , Iterable{ + override fun iterator() = this.value.iterator() +} + +abstract class TypedIntArrayValue internal constructor() : TypedPrimitiveArrayValue() +abstract class TypedShortArrayValue internal constructor() : TypedPrimitiveArrayValue() +abstract class TypedByteArrayValue internal constructor() : TypedPrimitiveArrayValue() +abstract class TypedLongArrayValue internal constructor() : TypedPrimitiveArrayValue() +abstract class TypedFloatArrayValue internal constructor() : TypedPrimitiveArrayValue() +abstract class TypedDoubleArrayValue internal constructor() : TypedPrimitiveArrayValue() +abstract class TypedBooleanArrayValue internal constructor() : TypedPrimitiveArrayValue() +abstract class TypedCharArrayValue internal constructor() : TypedPrimitiveArrayValue() +abstract class TypedStringArrayValue internal constructor() : TypedPrimitiveArrayValue() + +sealed class ListValue : Value>(), Iterable{ + override fun iterator() = this.value.iterator() +} +abstract class IntListValue internal constructor() : ListValue() +abstract class ShortListValue internal constructor() : ListValue() +abstract class ByteListValue internal constructor() : ListValue() +abstract class LongListValue internal constructor() : ListValue() +abstract class FloatListValue internal constructor() : ListValue() +abstract class DoubleListValue internal constructor() : ListValue() +abstract class BooleanListValue internal constructor() : ListValue() +abstract class CharListValue internal constructor() : ListValue() +abstract class StringListValue internal constructor() : ListValue() + diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt new file mode 100644 index 000000000..cc8b445aa --- /dev/null +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/setting/_ValueImpl.kt @@ -0,0 +1,652 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + + +package net.mamoe.mirai.console.setting + +import kotlinx.serialization.builtins.* + + +/** + * !!! This file is auto-generated by test/kotlin/net.mamoe.mirai.console.SettingsCodegen.kt + * !!! DO NOT MODIFY THIS FILE MANUALLY + */ + + +internal fun Setting.valueImpl(default: Int): IntValue { + return object : IntValue() { + private var internalValue: Int = default + override var value: Int + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = Int.serializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Short): ShortValue { + return object : ShortValue() { + private var internalValue: Short = default + override var value: Short + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = Short.serializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Byte): ByteValue { + return object : ByteValue() { + private var internalValue: Byte = default + override var value: Byte + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = Byte.serializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Long): LongValue { + return object : LongValue() { + private var internalValue: Long = default + override var value: Long + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = Long.serializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Float): FloatValue { + return object : FloatValue() { + private var internalValue: Float = default + override var value: Float + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = Float.serializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Double): DoubleValue { + return object : DoubleValue() { + private var internalValue: Double = default + override var value: Double + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = Double.serializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Boolean): BooleanValue { + return object : BooleanValue() { + private var internalValue: Boolean = default + override var value: Boolean + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = Boolean.serializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Char): CharValue { + return object : CharValue() { + private var internalValue: Char = default + override var value: Char + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = Char.serializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: String): StringValue { + return object : StringValue() { + private var internalValue: String = default + override var value: String + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = String.serializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: IntArray): IntArrayValue { + return object : IntArrayValue() { + private var internalValue: IntArray = default + override var value: IntArray + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = IntArraySerializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: ShortArray): ShortArrayValue { + return object : ShortArrayValue() { + private var internalValue: ShortArray = default + override var value: ShortArray + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ShortArraySerializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: ByteArray): ByteArrayValue { + return object : ByteArrayValue() { + private var internalValue: ByteArray = default + override var value: ByteArray + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ByteArraySerializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: LongArray): LongArrayValue { + return object : LongArrayValue() { + private var internalValue: LongArray = default + override var value: LongArray + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = LongArraySerializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: FloatArray): FloatArrayValue { + return object : FloatArrayValue() { + private var internalValue: FloatArray = default + override var value: FloatArray + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = FloatArraySerializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: DoubleArray): DoubleArrayValue { + return object : DoubleArrayValue() { + private var internalValue: DoubleArray = default + override var value: DoubleArray + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = DoubleArraySerializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: BooleanArray): BooleanArrayValue { + return object : BooleanArrayValue() { + private var internalValue: BooleanArray = default + override var value: BooleanArray + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = BooleanArraySerializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: CharArray): CharArrayValue { + return object : CharArrayValue() { + private var internalValue: CharArray = default + override var value: CharArray + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = CharArraySerializer().bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Array): TypedIntArrayValue { + return object : TypedIntArrayValue() { + private var internalValue: Array = default + override var value: Array + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ArraySerializer(Int.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Array): TypedShortArrayValue { + return object : TypedShortArrayValue() { + private var internalValue: Array = default + override var value: Array + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ArraySerializer(Short.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Array): TypedByteArrayValue { + return object : TypedByteArrayValue() { + private var internalValue: Array = default + override var value: Array + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ArraySerializer(Byte.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Array): TypedLongArrayValue { + return object : TypedLongArrayValue() { + private var internalValue: Array = default + override var value: Array + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ArraySerializer(Long.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Array): TypedFloatArrayValue { + return object : TypedFloatArrayValue() { + private var internalValue: Array = default + override var value: Array + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ArraySerializer(Float.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Array): TypedDoubleArrayValue { + return object : TypedDoubleArrayValue() { + private var internalValue: Array = default + override var value: Array + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ArraySerializer(Double.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Array): TypedBooleanArrayValue { + return object : TypedBooleanArrayValue() { + private var internalValue: Array = default + override var value: Array + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ArraySerializer(Boolean.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Array): TypedCharArrayValue { + return object : TypedCharArrayValue() { + private var internalValue: Array = default + override var value: Array + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ArraySerializer(Char.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: Array): TypedStringArrayValue { + return object : TypedStringArrayValue() { + private var internalValue: Array = default + override var value: Array + get() = internalValue + set(new) { + if (!new.contentEquals(internalValue)) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ArraySerializer(String.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: List): IntListValue { + return object : IntListValue() { + private var internalValue: List = default + override var value: List + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ListSerializer(Int.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: List): ShortListValue { + return object : ShortListValue() { + private var internalValue: List = default + override var value: List + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ListSerializer(Short.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: List): ByteListValue { + return object : ByteListValue() { + private var internalValue: List = default + override var value: List + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ListSerializer(Byte.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: List): LongListValue { + return object : LongListValue() { + private var internalValue: List = default + override var value: List + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ListSerializer(Long.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: List): FloatListValue { + return object : FloatListValue() { + private var internalValue: List = default + override var value: List + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ListSerializer(Float.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: List): DoubleListValue { + return object : DoubleListValue() { + private var internalValue: List = default + override var value: List + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ListSerializer(Double.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: List): BooleanListValue { + return object : BooleanListValue() { + private var internalValue: List = default + override var value: List + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ListSerializer(Boolean.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: List): CharListValue { + return object : CharListValue() { + private var internalValue: List = default + override var value: List + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ListSerializer(Char.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + +internal fun Setting.valueImpl(default: List): StringListValue { + return object : StringListValue() { + private var internalValue: List = default + override var value: List + get() = internalValue + set(new) { + if (new != internalValue) { + internalValue = new + onElementChanged(this) + } + } + override val serializer = ListSerializer(String.serializer()).bind( + getter = { internalValue }, + setter = { internalValue = it } + ) + } +} + + diff --git a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt index 1c01500c2..2746a1672 100644 --- a/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt +++ b/backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/utils/BotHelper.kt @@ -12,7 +12,6 @@ package net.mamoe.mirai.console.utils import net.mamoe.mirai.Bot import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.plugins.* -import net.mamoe.mirai.console.utils.BotManagers.BOT_MANAGERS import net.mamoe.mirai.contact.User import java.io.File @@ -23,12 +22,6 @@ import java.io.File val User.isManager: Boolean get() = this.bot.managers.contains(this.id) -@OptIn(ToBeRemoved::class) -internal object BotManagers { - val config = File("${MiraiConsole.path}/bot.yml").loadAsConfig() - val BOT_MANAGERS: ConfigSection by config.withDefaultWriteSave { ConfigSectionImpl() } -} - @JvmName("addManager") @JvmSynthetic @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) @@ -36,31 +29,18 @@ fun Bot.addManagerDeprecated(long: Long) { addManager(long) } -@OptIn(ToBeRemoved::class) internal fun Bot.addManager(long: Long): Boolean { - BOT_MANAGERS.putIfAbsent(this.id.toString(), mutableListOf()) - BOT_MANAGERS[this.id.toString()] = - (BOT_MANAGERS.getLongList(this.id.toString()) as MutableList).apply { - if (contains(long)) return@addManager false - add(long) - } - BotManagers.config.save() + TODO() return true } -@OptIn(ToBeRemoved::class) fun Bot.removeManager(long: Long) { - BOT_MANAGERS.putIfAbsent(this.id.toString(), mutableListOf()) - BOT_MANAGERS[this.id.toString()] = - (BOT_MANAGERS.getLongList(this.id.toString()) as MutableList).apply { remove(long) } - BotManagers.config.save() + TODO() } val Bot.managers: List - @OptIn(ToBeRemoved::class) get() { - BOT_MANAGERS.putIfAbsent(this.id.toString(), mutableListOf()) - return BOT_MANAGERS.getLongList(this.id.toString()) + TODO() } fun Bot.checkManager(long: Long): Boolean { diff --git a/settings.gradle b/settings.gradle index 8055fef21..d63876b77 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,6 +28,9 @@ def onlyBackEnd = true include(':mirai-console') project(':mirai-console').dir = file("backend/mirai-console") +include(':codegen') +project(':codegen').dir = file("backend/codegen") + if (!onlyBackEnd) {