Cleanup, remove old settings

This commit is contained in:
Him188 2020-06-17 13:25:36 +08:00
parent 11cb8d5c68
commit 687729df4e
14 changed files with 10 additions and 2544 deletions

View File

@ -16,8 +16,6 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.plugin.PluginLoader import net.mamoe.mirai.console.plugin.PluginLoader
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
import net.mamoe.mirai.console.setting.SettingStorage
import net.mamoe.mirai.console.setting.internal.ConsoleBuiltInSetting
import net.mamoe.mirai.utils.DefaultLogger import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
@ -72,14 +70,6 @@ object MiraiConsole : CoroutineScope, IMiraiConsole {
override val builtInPluginLoaders: List<PluginLoader<*, *>> get() = instance.builtInPluginLoaders override val builtInPluginLoaders: List<PluginLoader<*, *>> get() = instance.builtInPluginLoaders
@Suppress("CANNOT_WEAKEN_ACCESS_PRIVILEGE")
internal override val jvmSettingStorage: SettingStorage
get() = instance.jvmSettingStorage
@Suppress("CANNOT_WEAKEN_ACCESS_PRIVILEGE")
override val consoleBuiltIntSettingStorage: SettingStorage
get() = instance.consoleBuiltIntSettingStorage
init { init {
DefaultLogger = { identity -> this.newLogger(identity) } DefaultLogger = { identity -> this.newLogger(identity) }
this.coroutineContext[Job]!!.invokeOnCompletion { this.coroutineContext[Job]!!.invokeOnCompletion {
@ -116,16 +106,6 @@ internal interface IMiraiConsole : CoroutineScope {
* 内建加载器列表, 一般需要包含 [JarPluginLoader] * 内建加载器列表, 一般需要包含 [JarPluginLoader]
*/ */
val builtInPluginLoaders: List<PluginLoader<*, *>> val builtInPluginLoaders: List<PluginLoader<*, *>>
/**
* 内建的供 [JvmPlugin] 使用的 [SettingStorage]
*/
val jvmSettingStorage: SettingStorage
/**
* 内建的供 [ConsoleBuiltInSetting] 使用的 [SettingStorage]
*/
val consoleBuiltIntSettingStorage: SettingStorage
} }
/** /**

View File

@ -15,7 +15,6 @@ import net.mamoe.mirai.console.plugin.AbstractFilePluginLoader
import net.mamoe.mirai.console.plugin.PluginLoadException import net.mamoe.mirai.console.plugin.PluginLoadException
import net.mamoe.mirai.console.plugin.internal.JvmPluginImpl import net.mamoe.mirai.console.plugin.internal.JvmPluginImpl
import net.mamoe.mirai.console.plugin.internal.PluginsLoader import net.mamoe.mirai.console.plugin.internal.PluginsLoader
import net.mamoe.mirai.console.setting.SettingStorage
import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.yamlkt.Yaml import net.mamoe.yamlkt.Yaml
import java.io.File import java.io.File
@ -49,8 +48,6 @@ object JarPluginLoader : AbstractFilePluginLoader<JvmPlugin, JvmPluginDescriptio
} }
} }
val settingStorage: SettingStorage = MiraiConsole.jvmSettingStorage
override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description override fun getPluginDescription(plugin: JvmPlugin): JvmPluginDescription = plugin.description
override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> { override fun Sequence<File>.mapToDescription(): List<JvmPluginDescription> {

View File

@ -12,9 +12,6 @@
package net.mamoe.mirai.console.plugin.jvm package net.mamoe.mirai.console.plugin.jvm
import net.mamoe.mirai.console.plugin.internal.JvmPluginImpl import net.mamoe.mirai.console.plugin.internal.JvmPluginImpl
import net.mamoe.mirai.console.plugin.internal.job
import net.mamoe.mirai.console.setting.Setting
import net.mamoe.mirai.console.setting.Value
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.EmptyCoroutineContext
@ -23,20 +20,4 @@ import kotlin.coroutines.EmptyCoroutineContext
*/ */
abstract class KotlinPlugin @JvmOverloads constructor( abstract class KotlinPlugin @JvmOverloads constructor(
parentCoroutineContext: CoroutineContext = EmptyCoroutineContext parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
) : JvmPlugin, JvmPluginImpl(parentCoroutineContext) { ) : JvmPlugin, JvmPluginImpl(parentCoroutineContext)
abstract inner class PluginSetting : Setting() {
private val track =
@Suppress("LeakingThis")
JarPluginLoader.settingStorage.trackOn(this)
init {
this@KotlinPlugin.job.invokeOnCompletion {
track.close()
}
}
override fun onElementChanged(value: Value<*>) {
TODO()
}
}
}

View File

@ -1,115 +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("NOTHING_TO_INLINE", "unused")
package net.mamoe.mirai.console.setting
import kotlinx.serialization.KSerializer
import net.mamoe.mirai.console.setting.internal.SettingImpl
import net.mamoe.mirai.console.setting.internal.serialNameOrPropertyName
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.full.findAnnotation
/**
* 在配置文件和图像界面中保存的名称.
*/
typealias SerialName = kotlinx.serialization.SerialName
/**
* 在配置文件和图像界面中显示的说明.
*/
typealias Comment = net.mamoe.yamlkt.Comment
/**
* 配置的基类. 所有配置必须拥有一个无参构造器, 以用于在 [MutableList] [MutableMap] 中动态识别类型
*/
@Suppress("EXPOSED_SUPER_CLASS")
abstract class Setting : SettingImpl() {
/**
* 表示这个配置的嵌套对象, 自动绑定数据更新.
*/
abstract inner class Inner : Setting() {
internal lateinit var attachedValue: Value<*>
override fun onElementChanged(value: Value<*>) {
this@Setting.onElementChanged(attachedValue)
}
}
data class PropertyInfo(
val serialName: String,
val annotations: List<Annotation>,
val propertyOriginalName: String?
)
/**
* 这个配置的名称, 仅对于顶层配置有效.
*/
@MiraiExperimentalAPI
open val serialName: String
get() = this::class.findAnnotation<SerialName>()?.value
?: this::class.qualifiedName
?: error("Names should be assigned to anonymous classes manually by overriding serialName")
// for Java only
fun <T : Any> addProperty(
propertyInfo: PropertyInfo,
value: Value<*>
): Value<*> {
if (built) error("The Setting is already serialized so it's structure is immutable.")
valueList.add(value to propertyInfo)
return value
}
/**
* 获取这个属性的真实 [Value] 委托
*/
@get:JvmSynthetic
val <R : Any> KProperty0<R>.correspondingValue: Value<R>
@Suppress("UNCHECKED_CAST")
get() = findCorrespondingValue(this)
?: throw NoSuchElementException("No corresponding Value found for property $this")
/**
* 获取这个属性的真实 [Value] 委托
*/
fun <R : Any> findCorrespondingValue(property: KProperty0<R>): Value<R>? {
@Suppress("UNCHECKED_CAST")
return this@Setting.valueList.firstOrNull { it.second.propertyOriginalName == property.name }?.first as Value<R>?
}
/**
* 提供属性委托, 并添加这个对象的自动保存跟踪.
*/
@JvmSynthetic
operator fun <T : Any> Value<T>.provideDelegate(
thisRef: Setting,
property: KProperty<*>
): ReadWriteProperty<Setting, T> {
if (built) error("The Setting is already serialized so it's structure is immutable.")
valueList.add(this to PropertyInfo(property.serialNameOrPropertyName, property.annotations, property.name))
return this
}
abstract override fun onElementChanged(value: Value<*>)
override fun toString(): String = yamlForToString.stringify(this.serializer, this)
}
/**
* 用于更新或保存这个 [Value] 的序列化器.
*/
@Suppress("UNCHECKED_CAST")
val <T : Setting> T.serializer: KSerializer<T>
get() = kotlinSerializer as KSerializer<T>

View File

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

View File

@ -1,167 +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("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "unused")
package net.mamoe.mirai.console.setting
import net.mamoe.mirai.console.setting.internal.valueImpl
import kotlin.internal.LowPriorityInOverloadResolution
/**
* !!! 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<Int>): TypedIntArrayValue = valueImpl(default)
fun Setting.value(default: Array<Short>): TypedShortArrayValue = valueImpl(default)
fun Setting.value(default: Array<Byte>): TypedByteArrayValue = valueImpl(default)
fun Setting.value(default: Array<Long>): TypedLongArrayValue = valueImpl(default)
fun Setting.value(default: Array<Float>): TypedFloatArrayValue = valueImpl(default)
fun Setting.value(default: Array<Double>): TypedDoubleArrayValue = valueImpl(default)
fun Setting.value(default: Array<Boolean>): TypedBooleanArrayValue = valueImpl(default)
fun Setting.value(default: Array<Char>): TypedCharArrayValue = valueImpl(default)
fun Setting.value(default: Array<String>): TypedStringArrayValue = valueImpl(default)
fun Setting.value(default: List<Int>): IntListValue = valueImpl(default)
fun Setting.value(default: List<Short>): ShortListValue = valueImpl(default)
fun Setting.value(default: List<Byte>): ByteListValue = valueImpl(default)
fun Setting.value(default: List<Long>): LongListValue = valueImpl(default)
fun Setting.value(default: List<Float>): FloatListValue = valueImpl(default)
fun Setting.value(default: List<Double>): DoubleListValue = valueImpl(default)
fun Setting.value(default: List<Boolean>): BooleanListValue = valueImpl(default)
fun Setting.value(default: List<Char>): CharListValue = valueImpl(default)
fun Setting.value(default: List<String>): StringListValue = valueImpl(default)
fun Setting.value(default: Set<Int>): IntSetValue = valueImpl(default)
fun Setting.value(default: Set<Short>): ShortSetValue = valueImpl(default)
fun Setting.value(default: Set<Byte>): ByteSetValue = valueImpl(default)
fun Setting.value(default: Set<Long>): LongSetValue = valueImpl(default)
fun Setting.value(default: Set<Float>): FloatSetValue = valueImpl(default)
fun Setting.value(default: Set<Double>): DoubleSetValue = valueImpl(default)
fun Setting.value(default: Set<Boolean>): BooleanSetValue = valueImpl(default)
fun Setting.value(default: Set<Char>): CharSetValue = valueImpl(default)
fun Setting.value(default: Set<String>): StringSetValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableList<Int>): MutableIntListValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableList<Short>): MutableShortListValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableList<Byte>): MutableByteListValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableList<Long>): MutableLongListValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableList<Float>): MutableFloatListValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableList<Double>): MutableDoubleListValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableList<Boolean>): MutableBooleanListValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableList<Char>): MutableCharListValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableList<String>): MutableStringListValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableSet<Int>): MutableIntSetValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableSet<Short>): MutableShortSetValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableSet<Byte>): MutableByteSetValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableSet<Long>): MutableLongSetValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableSet<Float>): MutableFloatSetValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableSet<Double>): MutableDoubleSetValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableSet<Boolean>): MutableBooleanSetValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableSet<Char>): MutableCharSetValue = valueImpl(default)
@JvmName("valueMutable")
fun Setting.value(default: MutableSet<String>): MutableStringSetValue = valueImpl(default)
fun <T : Setting> Setting.value(default: T): Value<T> {
require(this::class != default::class) {
"Recursive nesting is prohibited"
}
return valueImpl(default).also {
if (default is Setting.Inner) {
default.attachedValue = it
}
}
}
inline fun <T : Setting> Setting.value(default: T, crossinline initializer: T.() -> Unit): Value<T> =
value(default).also { it.value.apply(initializer) }
inline fun <reified T : Setting> Setting.value(default: List<T>): SettingListValue<T> = valueImpl(default)
@JvmName("valueMutable")
inline fun <reified T : Setting> Setting.value(default: MutableList<T>): MutableSettingListValue<T> = valueImpl(default)
inline fun <reified T : Setting> Setting.value(default: Set<T>): SettingSetValue<T> = valueImpl(default)
@JvmName("valueMutable")
inline fun <reified T : Setting> Setting.value(default: MutableSet<T>): MutableSettingSetValue<T> = valueImpl(default)
/**
* 创建一个只引用对象而不跟踪其属性的值.
*
* @param T 类型. 必须拥有 [kotlinx.serialization.Serializable] 注解 (因此编译器会自动生成序列化器)
*/
@DangerousReferenceOnlyValue
@JvmName("valueDynamic")
@LowPriorityInOverloadResolution
inline fun <reified T : Any> Setting.value(default: T): Value<T> = valueImpl(default)
@RequiresOptIn(
"""
这种只保存引用的 Value 可能会导致意料之外的结果, 在使用时须保持谨慎.
对值的改变不会触发自动保存, 也不会同步到 UI . UI 中只能编辑序列化之后的值.
""", level = RequiresOptIn.Level.WARNING
)
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.FUNCTION)
annotation class DangerousReferenceOnlyValue

View File

@ -1,170 +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
*/
package net.mamoe.mirai.console.setting
import kotlinx.serialization.KSerializer
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* !!! This file is auto-generated by backend/codegen/src/main/kotlin/net.mamoe.mirai.console.codegen.ValuesCodegen.kt
* !!! for better performance
* !!! DO NOT MODIFY THIS FILE MANUALLY
*/
sealed class Value<T : Any> : ReadWriteProperty<Setting, T> {
abstract var value: T
/**
* 用于更新 [value] 的序列化器
*/
abstract val serializer: KSerializer<T>
override fun getValue(thisRef: Setting, property: KProperty<*>): T = value
override fun setValue(thisRef: Setting, property: KProperty<*>, value: T) {
this.value = value
}
override fun equals(other: Any?): Boolean {
if (other == null) return false
if (other::class != this::class) return false
other as Value<*>
return other.value == this.value
}
override fun hashCode(): Int = value.hashCode()
}
sealed class PrimitiveValue<T : Any> : Value<T>()
sealed class NumberValue<T : Number> : Value<T>()
abstract class IntValue internal constructor() : NumberValue<Int>()
abstract class ShortValue internal constructor() : NumberValue<Short>()
abstract class ByteValue internal constructor() : NumberValue<Byte>()
abstract class LongValue internal constructor() : NumberValue<Long>()
abstract class FloatValue internal constructor() : NumberValue<Float>()
abstract class DoubleValue internal constructor() : NumberValue<Double>()
abstract class BooleanValue internal constructor() : PrimitiveValue<Boolean>()
abstract class CharValue internal constructor() : PrimitiveValue<Char>()
abstract class StringValue internal constructor() : PrimitiveValue<String>()
// T can be primitive array or typed Array
sealed class ArrayValue<T : Any> : Value<T>()
sealed class PrimitiveArrayValue<T : Any> : ArrayValue<T>()
abstract class IntArrayValue internal constructor() : PrimitiveArrayValue<IntArray>(), Iterable<Int> {
override fun iterator(): Iterator<Int> = this.value.iterator()
}
abstract class ShortArrayValue internal constructor() : PrimitiveArrayValue<ShortArray>(), Iterable<Short> {
override fun iterator(): Iterator<Short> = this.value.iterator()
}
abstract class ByteArrayValue internal constructor() : PrimitiveArrayValue<ByteArray>(), Iterable<Byte> {
override fun iterator(): Iterator<Byte> = this.value.iterator()
}
abstract class LongArrayValue internal constructor() : PrimitiveArrayValue<LongArray>(), Iterable<Long> {
override fun iterator(): Iterator<Long> = this.value.iterator()
}
abstract class FloatArrayValue internal constructor() : PrimitiveArrayValue<FloatArray>(), Iterable<Float> {
override fun iterator(): Iterator<Float> = this.value.iterator()
}
abstract class DoubleArrayValue internal constructor() : PrimitiveArrayValue<DoubleArray>(), Iterable<Double> {
override fun iterator(): Iterator<Double> = this.value.iterator()
}
abstract class BooleanArrayValue internal constructor() : PrimitiveArrayValue<BooleanArray>(), Iterable<Boolean> {
override fun iterator(): Iterator<Boolean> = this.value.iterator()
}
abstract class CharArrayValue internal constructor() : PrimitiveArrayValue<CharArray>(), Iterable<Char> {
override fun iterator(): Iterator<Char> = this.value.iterator()
}
sealed class TypedPrimitiveArrayValue<E> : ArrayValue<Array<E>>(), Iterable<E> {
override fun iterator() = this.value.iterator()
}
abstract class TypedIntArrayValue internal constructor() : TypedPrimitiveArrayValue<Int>()
abstract class TypedShortArrayValue internal constructor() : TypedPrimitiveArrayValue<Short>()
abstract class TypedByteArrayValue internal constructor() : TypedPrimitiveArrayValue<Byte>()
abstract class TypedLongArrayValue internal constructor() : TypedPrimitiveArrayValue<Long>()
abstract class TypedFloatArrayValue internal constructor() : TypedPrimitiveArrayValue<Float>()
abstract class TypedDoubleArrayValue internal constructor() : TypedPrimitiveArrayValue<Double>()
abstract class TypedBooleanArrayValue internal constructor() : TypedPrimitiveArrayValue<Boolean>()
abstract class TypedCharArrayValue internal constructor() : TypedPrimitiveArrayValue<Char>()
abstract class TypedStringArrayValue internal constructor() : TypedPrimitiveArrayValue<String>()
sealed class ListValue<E> : Value<List<E>>(), List<E>
abstract class IntListValue internal constructor() : ListValue<Int>()
abstract class ShortListValue internal constructor() : ListValue<Short>()
abstract class ByteListValue internal constructor() : ListValue<Byte>()
abstract class LongListValue internal constructor() : ListValue<Long>()
abstract class FloatListValue internal constructor() : ListValue<Float>()
abstract class DoubleListValue internal constructor() : ListValue<Double>()
abstract class BooleanListValue internal constructor() : ListValue<Boolean>()
abstract class CharListValue internal constructor() : ListValue<Char>()
abstract class StringListValue internal constructor() : ListValue<String>()
abstract class SettingListValue<T : Setting> internal constructor() : Value<List<T>>(), List<T>
sealed class SetValue<E> : Value<Set<E>>(), Set<E>
abstract class IntSetValue internal constructor() : SetValue<Int>()
abstract class ShortSetValue internal constructor() : SetValue<Short>()
abstract class ByteSetValue internal constructor() : SetValue<Byte>()
abstract class LongSetValue internal constructor() : SetValue<Long>()
abstract class FloatSetValue internal constructor() : SetValue<Float>()
abstract class DoubleSetValue internal constructor() : SetValue<Double>()
abstract class BooleanSetValue internal constructor() : SetValue<Boolean>()
abstract class CharSetValue internal constructor() : SetValue<Char>()
abstract class StringSetValue internal constructor() : SetValue<String>()
abstract class SettingSetValue<T : Setting> internal constructor() : Value<Set<T>>(), Set<T>
abstract class SettingValue<T : Setting> internal constructor() : Value<T>()
abstract class MutableListValue<T : Any> internal constructor() : Value<MutableList<Value<T>>>(), MutableList<T>
abstract class MutableIntListValue internal constructor() : Value<MutableList<Int>>(), MutableList<Int>
abstract class MutableShortListValue internal constructor() : Value<MutableList<Short>>(), MutableList<Short>
abstract class MutableByteListValue internal constructor() : Value<MutableList<Byte>>(), MutableList<Byte>
abstract class MutableLongListValue internal constructor() : Value<MutableList<Long>>(), MutableList<Long>
abstract class MutableFloatListValue internal constructor() : Value<MutableList<Float>>(), MutableList<Float>
abstract class MutableDoubleListValue internal constructor() : Value<MutableList<Double>>(), MutableList<Double>
abstract class MutableBooleanListValue internal constructor() : Value<MutableList<Boolean>>(), MutableList<Boolean>
abstract class MutableCharListValue internal constructor() : Value<MutableList<Char>>(), MutableList<Char>
abstract class MutableStringListValue internal constructor() : Value<MutableList<String>>(), MutableList<String>
abstract class MutableSettingListValue<T : Setting> internal constructor() : Value<MutableList<T>>(), MutableList<T>
abstract class MutableSetValue<T : Any> internal constructor() : Value<MutableSet<Value<T>>>(), MutableSet<T>
abstract class MutableIntSetValue internal constructor() : Value<MutableSet<Int>>(), MutableSet<Int>
abstract class MutableShortSetValue internal constructor() : Value<MutableSet<Short>>(), MutableSet<Short>
abstract class MutableByteSetValue internal constructor() : Value<MutableSet<Byte>>(), MutableSet<Byte>
abstract class MutableLongSetValue internal constructor() : Value<MutableSet<Long>>(), MutableSet<Long>
abstract class MutableFloatSetValue internal constructor() : Value<MutableSet<Float>>(), MutableSet<Float>
abstract class MutableDoubleSetValue internal constructor() : Value<MutableSet<Double>>(), MutableSet<Double>
abstract class MutableBooleanSetValue internal constructor() : Value<MutableSet<Boolean>>(), MutableSet<Boolean>
abstract class MutableCharSetValue internal constructor() : Value<MutableSet<Char>>(), MutableSet<Char>
abstract class MutableStringSetValue internal constructor() : Value<MutableSet<String>>(), MutableSet<String>
abstract class MutableSettingSetValue<T : Setting> internal constructor() : Value<MutableSet<T>>(), MutableSet<T>
/**
* 只引用这个对象, 而不跟踪其成员.
* 仅适用于基础类型, 用于 mutable list/map 等情况; 或标注了 [Serializable] 的类.
*/
abstract class DynamicReferenceValue<T : Any> internal constructor() : Value<T>()

View File

@ -1,32 +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
*/
package net.mamoe.mirai.console.setting.internal
import kotlinx.coroutines.Job
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.setting.Setting
import net.mamoe.mirai.console.setting.Value
internal abstract class ConsoleBuiltInSetting : Setting() {
private val track =
@Suppress("LeakingThis")
MiraiConsole.jvmSettingStorage.trackOn(this)
init {
MiraiConsole.coroutineContext[Job]!!.invokeOnCompletion {
track.close()
}
}
override fun onElementChanged(value: Value<*>) {
TODO()
}
}

View File

@ -12,140 +12,12 @@ package net.mamoe.mirai.console.setting.internal
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.serializer import kotlinx.serialization.builtins.serializer
import net.mamoe.mirai.console.setting.SerialName
import net.mamoe.mirai.console.setting.Setting
import net.mamoe.mirai.console.setting.Value
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.yamlkt.Yaml import net.mamoe.yamlkt.Yaml
import net.mamoe.yamlkt.YamlConfiguration import net.mamoe.yamlkt.YamlConfiguration
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.full.findAnnotation import kotlin.reflect.full.findAnnotation
internal abstract class SettingImpl {
@JvmField
internal var valueList: MutableList<Pair<Value<*>, Setting.PropertyInfo>> = mutableListOf()
@JvmField
internal var built: Boolean = false
internal val updaterSerializer: KSerializer<SettingSerializerMark> by lazy {
built = true
SettingUpdaterSerializer(this as Setting)
}
internal val kotlinSerializer: KSerializer<Setting> by lazy {
object : KSerializer<Setting> {
override val descriptor: SerialDescriptor
get() = this@SettingImpl.updaterSerializer.descriptor
override fun deserialize(decoder: Decoder): Setting {
this@SettingImpl.updaterSerializer.deserialize(decoder)
return this@SettingImpl as Setting
}
override fun serialize(encoder: Encoder, value: Setting) {
this@SettingImpl.updaterSerializer.serialize(
encoder,
SettingSerializerMark
)
}
}
}
abstract fun onElementChanged(value: Value<*>)
companion object {
@JvmStatic
internal val yamlForToString =
Yaml(
configuration = YamlConfiguration(
nonStrictNullability = true,
nonStrictNumber = true,
stringSerialization = YamlConfiguration.StringSerialization.NONE,
classSerialization = YamlConfiguration.MapSerialization.FLOW_MAP,
listSerialization = YamlConfiguration.ListSerialization.FLOW_SEQUENCE
)
)
}
}
internal class SettingUpdaterSerializer(
private val instance: Setting
) : KSerializer<SettingSerializerMark> {
override val descriptor: SerialDescriptor by lazy {
@OptIn(MiraiExperimentalAPI::class)
SerialDescriptor(instance.serialName) {
for ((value, prop) in instance.valueList) {
val (serialName, annotations) = prop
element(serialName, 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, _) ->
val v = value as Value<Any>
v.value = this.decodeSerializableElement(
value.serializer.descriptor,
index,
v.serializer
)
}
} else {
while (true) {
val index = this.decodeElementIndex(descriptor)
if (index == CompositeDecoder.READ_DONE) return@decodeStructure SettingSerializerMark
val value = instance.valueList[index].first as Value<Any>
value.value = this.decodeSerializableElement(
descriptor,
index,
value.serializer
)
}
}
SettingSerializerMark
}
private val emptyList = emptyList<String>()
private val emptyListSerializer = ListSerializer(String.serializer())
override fun serialize(encoder: Encoder, value: SettingSerializerMark) {
if (instance.valueList.isEmpty()) {
emptyListSerializer.serialize(encoder, emptyList)
} else encoder.encodeStructure(descriptor) {
instance.valueList.forEachIndexed { index, (value, _) ->
@Suppress("UNCHECKED_CAST") // erased, no problem.
this.encodeElementSmart(descriptor, index, value)
}
}
}
}
// until https://github.com/Him188/yamlkt/issues/2 fixed
internal fun <T : Any> CompositeEncoder.encodeElementSmart(
descriptor: SerialDescriptor,
index: Int,
value: Value<T>
) {
when (value.value::class) {
String::class -> this.encodeStringElement(descriptor, index, value.value as String)
Int::class -> this.encodeIntElement(descriptor, index, value.value as Int)
Byte::class -> this.encodeByteElement(descriptor, index, value.value as Byte)
Char::class -> this.encodeCharElement(descriptor, index, value.value as Char)
Long::class -> this.encodeLongElement(descriptor, index, value.value as Long)
Float::class -> this.encodeFloatElement(descriptor, index, value.value as Float)
Double::class -> this.encodeDoubleElement(descriptor, index, value.value as Double)
Boolean::class -> this.encodeBooleanElement(descriptor, index, value.value as Boolean)
else ->
@Suppress("UNCHECKED_CAST")
this.encodeSerializableElement(descriptor, index, value.serializer as KSerializer<Any>, value.value)
}
}
internal object SettingSerializerMark internal object SettingSerializerMark
internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation<SerialName>()?.value ?: this.name internal val KProperty<*>.serialNameOrPropertyName: String get() = this.findAnnotation<SerialName>()?.value ?: this.name

View File

@ -1,324 +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("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
package net.mamoe.mirai.console.setting.internal
import kotlinx.serialization.*
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.SetSerializer
import net.mamoe.mirai.console.setting.*
import net.mamoe.yamlkt.YamlDynamicSerializer
import kotlin.internal.LowPriorityInOverloadResolution
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.full.createInstance
import kotlin.reflect.typeOf
/// region MUTABLE LIST
@PublishedApi
@JvmName("valueImplSetting")
@Suppress("UNCHECKED_CAST")
internal inline fun <reified T : Setting> Setting.valueImpl(
default: List<T>
): SettingListValue<T> = valueImpl(default, T::class.createInstance().serializer)
@PublishedApi
@JvmName("valueImplSettingMutable")
@Suppress("UNCHECKED_CAST")
internal inline fun <reified T : Setting> Setting.valueImpl(
default: MutableList<T>
): MutableSettingListValue<T> = valueImpl(default, T::class.createInstance().serializer)
/*
@PublishedApi
@JvmName("valueImpl1")
internal fun <T : Any> Setting.valueImpl(
default: MutableList<T>,
valueMapper: (T) -> Value<T>,
elementSerializer: KSerializer<T>
): MutableListValue<T> = valueImpl(default.mapTo(mutableListOf(), valueMapper), valueMapper, elementSerializer)
*/
internal fun <T : Any> Setting.valueImpl(
default: MutableList<Value<T>>,
valueMapper: (T) -> Value<T>,
elementSerializer: KSerializer<T>
): MutableListValue<T> {
var internalValue: MutableList<Value<T>> = default
fun updateShadow(): MutableList<T> =
internalValue.shadowMap(transform = { it.value }, transformBack = { valueMapper(it) })
var shadowed: MutableList<T> = updateShadow()
val delegt = dynamicMutableList { shadowed }
return object : MutableListValue<T>(), MutableList<T> by delegt {
override var value: MutableList<Value<T>>
get() = internalValue
set(new) {
if (new != internalValue) {
internalValue = new
shadowed = updateShadow()
onElementChanged(this)
}
}
override val serializer: KSerializer<MutableList<Value<T>>> = object : KSerializer<MutableList<Value<T>>> {
private val delegate = ListSerializer(elementSerializer)
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableList<Value<T>> {
return delegate.deserialize(decoder).mapTo(mutableListOf(), valueMapper)
}
override fun serialize(encoder: Encoder, value: MutableList<Value<T>>) {
delegate.serialize(encoder, value.map { it.value })
}
}
}
}
@PublishedApi
internal fun <T : Setting> Setting.valueImpl(
default: MutableList<T>,
elementSerializer: KSerializer<T>
): MutableSettingListValue<T> {
var internalValue: MutableList<T> = default
val delegt = dynamicMutableList { internalValue }
return object : MutableSettingListValue<T>(), MutableList<T> by delegt {
override var value: MutableList<T>
get() = internalValue
set(new) {
if (new != internalValue) {
internalValue = new
onElementChanged(this)
}
}
override val serializer: KSerializer<MutableList<T>> = object : KSerializer<MutableList<T>> {
private val delegate = ListSerializer(elementSerializer)
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableList<T> {
return delegate.deserialize(decoder).toMutableList() // TODO: 2020/5/17 ATTACH OBSERVER
}
override fun serialize(encoder: Encoder, value: MutableList<T>) {
delegate.serialize(encoder, value)
}
}
}
}
@PublishedApi
internal fun <T : Setting> Setting.valueImpl(
default: List<T>,
elementSerializer: KSerializer<T>
): SettingListValue<T> {
var internalValue: List<T> = default
val delegt = dynamicList { internalValue }
return object : SettingListValue<T>(), List<T> by delegt {
override var value: List<T>
get() = internalValue
set(new) {
if (new != internalValue) {
internalValue = new
onElementChanged(this)
}
}
override val serializer: KSerializer<List<T>> = object : KSerializer<List<T>> {
private val delegate = ListSerializer(elementSerializer)
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): List<T> {
return delegate.deserialize(decoder) // TODO: 2020/5/17 ATTACH OBSERVER
}
override fun serialize(encoder: Encoder, value: List<T>) {
delegate.serialize(encoder, value)
}
}
}
}
@PublishedApi
internal fun <T : Setting> Setting.valueImpl(
default: Set<T>,
elementSerializer: KSerializer<T>
): SettingSetValue<T> {
var internalValue: Set<T> = default
val delegt = dynamicSet { internalValue }
return object : SettingSetValue<T>(), Set<T> by delegt {
override var value: Set<T>
get() = internalValue
set(new) {
if (new != internalValue) {
internalValue = new
onElementChanged(this)
}
}
override val serializer: KSerializer<Set<T>> = object : KSerializer<Set<T>> {
private val delegate = SetSerializer(elementSerializer)
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): Set<T> {
return delegate.deserialize(decoder) // TODO: 2020/5/17 ATTACH OBSERVER
}
override fun serialize(encoder: Encoder, value: Set<T>) {
delegate.serialize(encoder, value)
}
}
}
}
@PublishedApi
internal fun <T : Setting> Setting.valueImpl(
default: MutableSet<T>,
elementSerializer: KSerializer<T>
): MutableSettingSetValue<T> {
var internalValue: MutableSet<T> = default
val delegt = dynamicMutableSet { internalValue }
return object : MutableSettingSetValue<T>(), MutableSet<T> by delegt {
override var value: MutableSet<T>
get() = internalValue
set(new) {
if (new != internalValue) {
internalValue = new
onElementChanged(this)
}
}
override val serializer: KSerializer<MutableSet<T>> = object : KSerializer<MutableSet<T>> {
private val delegate = SetSerializer(elementSerializer)
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableSet<T> {
return delegate.deserialize(decoder).toMutableSet() // TODO: 2020/5/17 ATTACH OBSERVER
}
override fun serialize(encoder: Encoder, value: MutableSet<T>) {
delegate.serialize(encoder, value)
}
}
}
}
// endregion
// region MUTABLE SET
@PublishedApi
@JvmName("valueImplSetting")
@Suppress("UNCHECKED_CAST")
internal inline fun <reified T : Setting> Setting.valueImpl(
default: Set<T>
): SettingSetValue<T> = valueImpl(default, T::class.createInstance().serializer)
@PublishedApi
@JvmName("valueImplSettingMutable")
@Suppress("UNCHECKED_CAST")
internal inline fun <reified T : Setting> Setting.valueImpl(
default: MutableSet<T>
): MutableSettingSetValue<T> = valueImpl(default, T::class.createInstance().serializer)
/*
@JvmName("valueImpl1")
@PublishedApi
internal fun <T : Any> Setting.valueImpl(
default: MutableSet<T>,
valueMapper: (T) -> Value<T>,
elementSerializer: KSerializer<T>
): MutableSetValue<T> = valueImpl(default.mapTo(mutableSetOf(), valueMapper), valueMapper, elementSerializer)
*/
@JvmName("valueImplMutable")
internal fun <T : Any> Setting.valueImpl(
default: MutableSet<Value<T>>,
valueMapper: (T) -> Value<T>,
elementSerializer: KSerializer<T>
): MutableSetValue<T> {
var internalValue: MutableSet<Value<T>> = default
fun updateShadow(): MutableSet<T> =
internalValue.shadowMap(transform = { it.value }, transformBack = { valueMapper(it) })
var shadowed: MutableSet<T> = updateShadow()
val delegt = dynamicMutableSet { shadowed }
return object : MutableSetValue<T>(), MutableSet<T> by delegt {
override var value: MutableSet<Value<T>>
get() = internalValue
set(new) {
if (new != internalValue) {
internalValue = new
shadowed = updateShadow()
onElementChanged(this)
}
}
override val serializer: KSerializer<MutableSet<Value<T>>> = object : KSerializer<MutableSet<Value<T>>> {
private val delegate = SetSerializer(elementSerializer)
override val descriptor: SerialDescriptor = delegate.descriptor
override fun deserialize(decoder: Decoder): MutableSet<Value<T>> {
return delegate.deserialize(decoder).mapTo(mutableSetOf(), valueMapper)
}
override fun serialize(encoder: Encoder, value: MutableSet<Value<T>>) {
delegate.serialize(encoder, value.mapTo(mutableSetOf()) { it.value })
}
}
}
}
// endregion
// region DYNAMIC PRIMITIVES AND SERIALIZABLE
/**
* For primitives and serializable only
*/
@OptIn(ExperimentalStdlibApi::class)
@PublishedApi
@LowPriorityInOverloadResolution
internal inline fun <reified T : Any> Setting.valueImpl(default: T): Value<T> =
valueImpl(default, T::class)
@PublishedApi
internal fun <T : Any> Setting.valueImpl(default: T, clazz: KClass<out T>): Value<T> {
if (default is Setting) @Suppress("UNCHECKED_CAST") return valueImpl(default as Setting) as Value<T>
@OptIn(ImplicitReflectionSerializer::class)
requireNotNull(clazz.serializerOrNull()) {
"${clazz.qualifiedName} is not serializable"
}
return object : DynamicReferenceValue<T>() {
override var value: T = default
override val serializer: KSerializer<T> = object : KSerializer<T> {
override val descriptor: SerialDescriptor
get() = YamlDynamicSerializer.descriptor
override fun deserialize(decoder: Decoder): T =
YamlDynamicSerializer.deserialize(decoder).smartCastPrimitive(clazz)
override fun serialize(encoder: Encoder, value: T) = YamlDynamicSerializer.serialize(encoder, value)
}
}
}
// endregion

View File

@ -10,17 +10,8 @@
package net.mamoe.mirai.console.utils package net.mamoe.mirai.console.utils
import kotlinx.atomicfu.locks.withLock
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.setting.Value
import net.mamoe.mirai.console.setting.internal.ConsoleBuiltInSetting
import net.mamoe.mirai.console.setting.value
import net.mamoe.mirai.contact.User import net.mamoe.mirai.contact.User
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.Condition
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import kotlin.properties.ReadWriteProperty
/** /**

View File

@ -1,106 +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("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.console.command
import net.mamoe.mirai.Bot
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
import net.mamoe.mirai.console.setting.value
import net.mamoe.mirai.message.data.*
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
val plugin = MyPlugin()
class MyPlugin : KotlinPlugin() {
inner class MySetting : PluginSetting() {
val int by value(1)
}
}
/*
internal object TestCommand : PluginCommand(
plugin,
CommandDescriptor("test") {
param<String>()
}
) {
override suspend fun CommandSender.onCommand(args: CommandArgs): Boolean {
val s = args.getReified<String>()
sendMessage(s)
return true
}
}*/
internal class TestCommands {
@Test
fun testFlatten() {
assertEquals(listOf("test", "v1"), "test v1".flattenCommandComponents().toList())
assertEquals(listOf("test", "v1"), PlainText("test v1").flattenCommandComponents().toList())
assertEquals(listOf("test", "v1"), arrayOf("test ", "v1", " ").flattenCommandComponents().toList())
assertEquals(
listOf("test", "v1"),
messageChainOf("test v1".toMessage(), " ".toMessage()).flattenCommandComponents().toList()
)
}
/*
@Test
fun testRegister() {
assertTrue(TestCommand.register())
assertEquals(listOf("test"), TestCommand.allNames.single().toList())
assertFalse(TestCommand.register())
assertFalse(
object : PluginCommand(
plugin,
CommandDescriptor("test") {
param<String>()
}
) {
override suspend fun CommandSender.onCommand(args: CommandArgs): Boolean {
val s = args.getReified<String>()
sendMessage(s)
return true
}
}.register()
)
}
@Test
fun testExecute() = runBlocking {
TestCommand.register()
assertEquals(
"ok",
withSender {
execute("test", "arg")
}.contentToString()
)
}*/
}
internal inline fun withSender(block: CommandSender.() -> Unit): MessageChain {
val result = MessageChainBuilder()
val sender: CommandSender = object : CommandSender {
override val bot: Bot?
get() = null
override suspend fun sendMessage(message: Message) {
result.add(message)
}
}
sender.let(block)
return result.asMessageChain()
}

View File

@ -9,15 +9,19 @@
package net.mamoe.mirai.console.command package net.mamoe.mirai.console.command
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.message.data.Image
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
object TestCompositeCommand : CompositeCommand( object TestCompositeCommand : CompositeCommand(
TestCommandOwner, TestCommandOwner,
"name1", "name2", "groupManagement", "grpMgn"
description = """ ) {
desc @SubCommand
""".trimIndent() suspend fun CommandSender.mute(image: Image, target: Member, seconds: Int) {
) target.mute(seconds)
}
}
internal class TestComposite { internal class TestComposite {