Commit 15a5e424 authored by jiahua.liu's avatar jiahua.liu

Super Smart Config

parent 240183d5
...@@ -8,12 +8,11 @@ ...@@ -8,12 +8,11 @@
*/ */
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.serialization.UnstableDefault
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.alsoLogin import net.mamoe.mirai.alsoLogin
import net.mamoe.mirai.api.http.generateSessionKey import net.mamoe.mirai.api.http.generateSessionKey
import net.mamoe.mirai.plugin.JsonConfig import net.mamoe.mirai.plugin.*
import net.mamoe.mirai.plugin.PluginBase
import net.mamoe.mirai.plugin.PluginManager
import java.io.File import java.io.File
import kotlin.concurrent.thread import kotlin.concurrent.thread
...@@ -112,24 +111,13 @@ object MiraiConsole { ...@@ -112,24 +111,13 @@ object MiraiConsole {
} }
} }
@UnstableDefault
object MiraiProperties { object MiraiProperties {
var HTTP_API_ENABLE: Boolean = true var config = Config.load("$path/mirai.json")
var HTTP_API_PORT: Short = 8080
var HTTP_API_AUTH_KEY: String = ""
private val file = File(path + "/mirai.json".replace("//", "/"))
private lateinit var config: JsonConfig
fun load() {
if (!file.exists()) {
HTTP_API_AUTH_KEY = "INITKEY" + generateSessionKey()
save()
return
}
config = PluginBase
}
fun save() { var HTTP_API_ENABLE: Boolean by config.withDefault { true }
var HTTP_API_PORT: Int by config.withDefault { 8080 }
} var HTTP_API_AUTH_KEY: String by config.withDefault { "INITKEY" + generateSessionKey() }
} }
} }
......
...@@ -9,20 +9,19 @@ ...@@ -9,20 +9,19 @@
package net.mamoe.mirai.plugin package net.mamoe.mirai.plugin
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.UnstableDefault import kotlinx.serialization.UnstableDefault
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.io.File import java.io.File
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.properties.Delegates
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.isSubclassOf
/** /**
* TODO: support all config types * TODO: support all config types
* only JSON is now supported
*
*/ */
interface Config { interface Config {
...@@ -42,9 +41,29 @@ interface Config { ...@@ -42,9 +41,29 @@ interface Config {
operator fun get(key: String): Any? operator fun get(key: String): Any?
fun exist(key: String): Boolean fun exist(key: String): Boolean
fun asMap(): Map<String, Any> fun asMap(): Map<String, Any>
fun save()
companion object {
fun load(fileName: String): Config {
return load(File(fileName.replace("//", "/")))
}
fun load(file: File): Config {
if (!file.exists()) {
file.createNewFile()
}
return when (file.extension.toLowerCase()) {
"json" -> JsonConfig(file)
else -> error("Unsupported file config type ${file.extension.toLowerCase()}")
}
}
}
} }
inline fun <reified T : Any> Config.withDefault(crossinline defaultValue: () -> T): ReadWriteProperty<Any, T> { inline fun <reified T : Any> Config.withDefault(
autoSave: Boolean = true,
crossinline defaultValue: () -> T
): ReadWriteProperty<Any, T> {
return object : ReadWriteProperty<Any, T> { return object : ReadWriteProperty<Any, T> {
override fun getValue(thisRef: Any, property: KProperty<*>): T { override fun getValue(thisRef: Any, property: KProperty<*>): T {
if (!this@withDefault.exist(property.name)) { if (!this@withDefault.exist(property.name)) {
...@@ -55,12 +74,13 @@ inline fun <reified T : Any> Config.withDefault(crossinline defaultValue: () -> ...@@ -55,12 +74,13 @@ inline fun <reified T : Any> Config.withDefault(crossinline defaultValue: () ->
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) { override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
this@withDefault[property.name] = value this@withDefault[property.name] = value
if (autoSave) save()
} }
} }
} }
@Suppress("IMPLICIT_CAST_TO_ANY") @Suppress("IMPLICIT_CAST_TO_ANY")
inline operator fun <reified T> ConfigSection.getValue(thisRef: Any?, property: KProperty<*>): T { inline operator fun <reified T> Config.getValue(thisRef: Any?, property: KProperty<*>): T {
return when (T::class) { return when (T::class) {
String::class -> this.getString(property.name) String::class -> this.getString(property.name)
Int::class -> this.getInt(property.name) Int::class -> this.getInt(property.name)
...@@ -93,7 +113,7 @@ inline operator fun <reified T> ConfigSection.getValue(thisRef: Any?, property: ...@@ -93,7 +113,7 @@ inline operator fun <reified T> ConfigSection.getValue(thisRef: Any?, property:
} as T } as T
} }
inline operator fun <reified T> ConfigSection.setValue(thisRef: Any?, property: KProperty<*>, value: T) { inline operator fun <reified T> Config.setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this[property.name] = value!! this[property.name] = value!!
} }
...@@ -147,14 +167,17 @@ interface ConfigSection : Config { ...@@ -147,14 +167,17 @@ interface ConfigSection : Config {
return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toLong() } return ((get(key) ?: error("ConfigSection does not contain $key ")) as List<*>).map { it.toString().toLong() }
} }
override operator fun set(key: String, value: Any) { override fun exist(key: String): Boolean {
this[key] = value return get(key) != null
} }
} }
@Serializable @Serializable
open class ConfigSectionImpl() : ConcurrentHashMap<String, Any>(), ConfigSection { open class ConfigSectionImpl() : ConcurrentHashMap<String, Any>(), ConfigSection {
override fun set(key: String, value: Any) {
this[key] = value
}
override operator fun get(key: String): Any? { override operator fun get(key: String): Any? {
return super.get(key) return super.get(key)
} }
...@@ -166,48 +189,66 @@ open class ConfigSectionImpl() : ConcurrentHashMap<String, Any>(), ConfigSection ...@@ -166,48 +189,66 @@ open class ConfigSectionImpl() : ConcurrentHashMap<String, Any>(), ConfigSection
override fun asMap(): Map<String, Any> { override fun asMap(): Map<String, Any> {
return this return this
} }
override fun save() {
}
} }
interface FileConfig { interface FileConfig : Config {
fun deserialize(content: String): ConfigSectionImpl
fun serialize(config: ConfigSectionImpl): String
} }
@Serializable
abstract class FileConfigImpl internal constructor() : ConfigSectionImpl(), FileConfig {
} abstract class FileConfigImpl internal constructor(
private val file: File
) : FileConfig, ConfigSection {
@Serializable private val content by lazy {
class JsonConfig internal constructor() : FileConfigImpl() { deserialize(file.readText())
}
companion object { override fun save() {
@UnstableDefault if (!file.exists()) {
fun load(file: File): Config { file.createNewFile()
require(file.extension.toLowerCase() == "json") }
val content = file.apply { file.writeText(serialize(content))
if (!this.exists()) this.createNewFile() }
}.readText()
if (content.isEmpty() || content.isBlank()) { override fun get(key: String): Any? {
return JsonConfig() return content[key]
} }
return Json.parse(
JsonConfig.serializer(), override fun set(key: String, value: Any) {
content content[key] = value
) }
override fun asMap(): Map<String, Any> {
return content
}
}
class JsonConfig internal constructor(file: File) : FileConfigImpl(file) {
@UnstableDefault
override fun deserialize(content: String): ConfigSectionImpl {
if (content.isEmpty() || content.isBlank() || content == "{}") {
return ConfigSectionImpl()
} }
return Json.parse(
ConfigSectionImpl.serializer(),
content
)
}
@UnstableDefault @UnstableDefault
fun save(file: File, config: JsonConfig) { override fun serialize(config: ConfigSectionImpl): String {
require(file.extension.toLowerCase() == "json") if (config.isEmpty()) {
val content = Json.stringify( return "{}"
JsonConfig.serializer(),
config
)
file.apply {
if (!this.exists()) this.createNewFile()
}.writeText(content)
} }
return Json.stringify(ConfigSectionImpl.serializer(), config)
} }
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment