Merge branch 'master' into command

# Conflicts:
#	backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/description/CommandArgumentContext.kt
This commit is contained in:
Him188 2020-09-20 21:57:10 +08:00
commit 2104cc2192
32 changed files with 421 additions and 505 deletions

View File

@ -0,0 +1,6 @@
# Mirai Console - Backend.codegen
后端代码生成模块,用于最小化重复代码的人工成本。
- `MessageScope` 代码生成: [MessageScopeCodegen.kt: Line 33](src/main/kotlin/net/mamoe/mirai/console/codegen/MessageScopeCodegen.kt#L33)
- `Value``PluginData` 相关代码生成: [ValueSettingCodegen.kt: Line 18](src/main/kotlin/net/mamoe/mirai/console/codegen/ValuePluginDataCodegen.kt#L18)

View File

@ -0,0 +1,3 @@
# Mirai Console - Backend
Mirai Console 后端模块. 发布为 `net.mamoe:mirai-console`.

View File

@ -65,11 +65,9 @@ dependencies {
compileAndTestRuntime(kotlinx("serialization-core", Versions.serialization))
compileAndTestRuntime(kotlin("reflect"))
implementation("org.jetbrains:annotations:19.0.0")
smartImplementation("net.mamoe.yamlkt:yamlkt:${Versions.yamlkt}")
smartImplementation("org.jetbrains:annotations:19.0.0")
smartApi(kotlinx("coroutines-jdk8", Versions.coroutines))
smartApi("net.mamoe.yamlkt:yamlkt:${Versions.yamlkt}")
smartApi("com.vdurmont:semver4j:3.1.0")
testApi("net.mamoe:mirai-core-qqandroid:${Versions.core}")
testApi(kotlin("stdlib-jdk8"))
@ -102,8 +100,8 @@ tasks {
})"""
}
.replace(
Regex("""val version: Semver = Semver\(".*", Semver.SemverType.LOOSE\)""")
) { """val version: Semver = Semver("${project.version}", Semver.SemverType.LOOSE)""" }
Regex("""val version: SemVersion = SemVersion.parse\(".*"\)""")
) { """val version: SemVersion = SemVersion.parse("${project.version}")""" }
)
}
}

View File

@ -12,7 +12,6 @@
package net.mamoe.mirai.console
import com.vdurmont.semver4j.Semver
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import net.mamoe.mirai.Bot
@ -29,6 +28,7 @@ import net.mamoe.mirai.console.plugin.loader.PluginLoader
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.console.util.ConsoleInternalApi
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScopeContext
import net.mamoe.mirai.console.util.SemVersion
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.MiraiLogger
import java.io.File
@ -79,7 +79,7 @@ public interface MiraiConsole : CoroutineScope {
/**
* Console 后端版本号
*/
public val version: Semver
public val version: SemVersion
@ConsoleExperimentalApi

View File

@ -9,7 +9,8 @@
package net.mamoe.mirai.console
import com.vdurmont.semver4j.Semver
import net.mamoe.mirai.console.util.SemVersion
/**
* 有关前端实现的信息
@ -28,7 +29,7 @@ public interface MiraiConsoleFrontEndDescription {
/**
* 此前端实现的名称
*/
public val version: Semver
public val version: SemVersion
/**
* 兼容的 [MiraiConsole] 后端版本号
@ -37,7 +38,7 @@ public interface MiraiConsoleFrontEndDescription {
*
* 返回 `null` 表示禁止 [MiraiConsole] 后端检查版本兼容性.
*/
public val compatibleBackendVersion: Semver? get() = null
public val compatibleBackendVersion: SemVersion? get() = null
/**
* 返回显示在 [MiraiConsole] 启动时的信息

View File

@ -21,6 +21,8 @@ import net.mamoe.mirai.console.permission.PermitteeId
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.data.MessageContent
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.message.data.PlainText
import kotlin.internal.LowPriorityInOverloadResolution
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
@ -76,6 +78,9 @@ public interface CommandArgumentContext {
Double::class with DoubleArgumentParser
Float::class with FloatArgumentParser
Image::class with ImageArgumentParser
PlainText::class with PlainTextArgumentParser
Contact::class with ExistingContactArgumentParser
User::class with ExistingUserArgumentParser
Member::class with ExistingMemberArgumentParser

View File

@ -77,6 +77,35 @@ public object StringArgumentParser : InternalCommandArgumentParserExtensions<Str
public override fun parse(raw: String, sender: CommandSender): String = raw
}
/**
* 解析 [String] 通过 [Image].
*/
public object ImageArgumentParser : InternalCommandArgumentParserExtensions<Image> {
public override fun parse(raw: String, sender: CommandSender): Image {
return kotlin.runCatching {
Image(raw)
}.getOrElse {
illegalArgument("无法解析 $raw 为图片.")
}
}
override fun parse(raw: MessageContent, sender: CommandSender): Image {
if (raw is Image) return raw
return super.parse(raw, sender)
}
}
public object PlainTextArgumentParser : InternalCommandArgumentParserExtensions<PlainText> {
public override fun parse(raw: String, sender: CommandSender): PlainText {
return PlainText(raw)
}
override fun parse(raw: MessageContent, sender: CommandSender): PlainText {
if (raw is PlainText) return raw
return super.parse(raw, sender)
}
}
/**
* 当字符串内容为(不区分大小写) "true", "yes", "enabled"
*/

View File

@ -24,7 +24,7 @@ import kotlinx.coroutines.Job
* @see AutoSavePluginData
*/
public open class AutoSavePluginConfig : AutoSavePluginData, PluginConfig {
@Deprecated("请手动指定保存名称. 此构造器将在 1.0.0 删除", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("AutoSavePluginConfig(\"改成保存名称\")"))
@Deprecated("请手动指定保存名称. 此构造器将在 1.0.0 删除", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("AutoSavePluginConfig(\"把我改成保存名称\")"))
@Suppress("DEPRECATION_ERROR")
public constructor() : super()

View File

@ -30,7 +30,8 @@ import kotlin.reflect.full.findAnnotation
* @see PluginData
*/
public open class AutoSavePluginData private constructor(
@Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any?
// KEEP THIS PRIMARY CONSTRUCTOR FOR FUTURE USE: WE'LL SUPPORT SERIALIZERS_MODULE FOR POLYMORPHISM
@Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any?,
) : AbstractPluginData() {
private lateinit var owner_: AutoSavePluginDataHolder
private val autoSaveIntervalMillis_: LongRange get() = owner_.autoSaveIntervalMillis
@ -45,7 +46,7 @@ public open class AutoSavePluginData private constructor(
_saveName = saveName
}
@Deprecated("请手动指定保存名称. 此构造器将在 1.0.0 删除", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("AutoSavePluginConfig"))
@Deprecated("请手动指定保存名称. 此构造器将在 1.0.0 删除", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("AutoSavePluginData(\"把我改成保存名称\")"))
public constructor() : this(null) {
val clazz = this::class
_saveName = clazz.findAnnotation<ValueName>()?.value

View File

@ -9,14 +9,12 @@
package net.mamoe.mirai.console.data
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
/**
* 序列化之后的名称.
*
* :
* ```
* object AccountPluginData : PluginData by ... {
* object AccountPluginData : AutoSavePluginData() {
* @ValueName("info")
* val map: Map<String, String> by value("a" to "b")
* }
@ -28,8 +26,10 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi
* map:
* a: b
* ```
*
* @see PluginData
* @see Value
*/
@ConsoleExperimentalApi
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
public annotation class ValueName(val value: String)

View File

@ -38,7 +38,7 @@ import net.mamoe.mirai.console.data.PluginData
* @see PluginConfig
*/
public abstract class JAutoSavePluginConfig : AutoSavePluginConfig, PluginConfig {
@Deprecated("请手动指定保存名称. 此构造器将在 1.0.0 删除", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("AutoSavePluginConfig(\"改成保存名称\")"))
@Deprecated("请手动指定保存名称. 此构造器将在 1.0.0 删除", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("JAutoSavePluginConfig(\"把我改成保存名称\")"))
@Suppress("DEPRECATION_ERROR")
public constructor() : super()

View File

@ -67,7 +67,7 @@ import kotlin.reflect.full.createType
* @see PluginData
*/
public abstract class JAutoSavePluginData : AutoSavePluginData, PluginConfig {
@Deprecated("请手动指定保存名称. 此构造器将在 1.0.0 删除", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("AutoSavePluginConfig(\"改成保存的名称\")"))
@Deprecated("请手动指定保存名称. 此构造器将在 1.0.0 删除", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("JAutoSavePluginData(\"把我改成保存名称\")"))
@Suppress("DEPRECATION_ERROR")
public constructor() : super()

View File

@ -9,13 +9,13 @@
package net.mamoe.mirai.console.internal
import com.vdurmont.semver4j.Semver
import net.mamoe.mirai.console.util.SemVersion
import java.time.Instant
internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants)
@JvmStatic
val buildDate: Instant = Instant.ofEpochSecond(1600522812)
val buildDate: Instant = Instant.ofEpochSecond(1600596035)
@JvmStatic
val version: Semver = Semver("1.0-RC-dev-28", Semver.SemverType.LOOSE)
val version: SemVersion = SemVersion("1.0-RC-dev-28")
}

View File

@ -11,7 +11,6 @@
package net.mamoe.mirai.console.internal
import com.vdurmont.semver4j.Semver
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@ -47,6 +46,7 @@ import net.mamoe.mirai.console.plugin.jvm.AbstractJvmPlugin
import net.mamoe.mirai.console.plugin.loader.PluginLoader
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.console.util.ConsoleInput
import net.mamoe.mirai.console.util.SemVersion
import net.mamoe.mirai.utils.*
import java.nio.file.Path
import java.time.Instant
@ -66,7 +66,7 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
private val instance: MiraiConsoleImplementation by MiraiConsoleImplementation.Companion::instance
override val buildDate: Instant by MiraiConsoleBuildConstants::buildDate
override val version: Semver by MiraiConsoleBuildConstants::version
override val version: SemVersion by MiraiConsoleBuildConstants::version
override val rootPath: Path by instance::rootPath
override val frontEndDescription: MiraiConsoleFrontEndDescription by instance::frontEndDescription

View File

@ -9,6 +9,8 @@
package net.mamoe.mirai.console.internal.data
import kotlinx.serialization.json.Json
import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.data.*
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
@ -59,23 +61,28 @@ internal open class MultiFilePluginDataStorageImpl(
return file.toFile().also { it.createNewFile() }
}
@ConsoleExperimentalApi
public override fun store(holder: PluginDataHolder, instance: PluginData) {
val yaml =/* if (instance.saveName == "PermissionService") Json {
private val json = Json {
prettyPrint = true
ignoreUnknownKeys = true
isLenient = true
allowStructuredMapKeys = true
} /*Yaml(
configuration = YamlConfiguration(
mapSerialization = YamlConfiguration.MapSerialization.FLOW_MAP,
listSerialization = YamlConfiguration.ListSerialization.FLOW_SEQUENCE,
classSerialization = YamlConfiguration.MapSerialization.FLOW_MAP
)
)*/ else */Yaml.default
}
private val yaml = Yaml.default
@ConsoleExperimentalApi
public override fun store(holder: PluginDataHolder, instance: PluginData) {
getPluginDataFile(holder, instance).writeText(
kotlin.runCatching {
yaml.encodeToString(instance.updaterSerializer, Unit)
}.recoverCatching {
// Just use mainLogger for convenience.
MiraiConsole.mainLogger.warning(
"Could not save ${instance.saveName} in YAML format due to exception in YAML encoder. " +
"Please report this exception and relevant configurations to https://github.com/mamoe/mirai-console/issues/new",
it
)
json.encodeToString(instance.updaterSerializer, Unit)
}.getOrElse {
throw IllegalStateException("Exception while saving $instance, saveName=${instance.saveName}", it)
}

View File

@ -27,6 +27,7 @@ import net.mamoe.mirai.console.plugin.loader.PluginLoadException
import net.mamoe.mirai.console.plugin.loader.PluginLoader
import net.mamoe.mirai.console.plugin.name
import net.mamoe.mirai.console.util.CoroutineScopeUtils.childScope
import net.mamoe.mirai.console.util.SemVersion.Companion.contains
import net.mamoe.mirai.utils.info
import java.io.File
import java.nio.file.Path

View File

@ -26,19 +26,63 @@ internal object SemVersionInternal {
private val versionMathRange =
"""\[([0-9]+(\.[0-9]+)+(|[\-+].+))\s*\,\s*([0-9]+(\.[0-9]+)+(|[\-+].+))\]""".toRegex()
private val versionRule = """^((\>\=)|(\<\=)|(\=)|(\>)|(\<))\s*([0-9]+(\.[0-9]+)+(|[\-+].+))$""".toRegex()
private fun Collection<*>.dump() {
forEachIndexed { index, value ->
println("$index, $value")
private val SEM_VERSION_REGEX =
"""^(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$""".toRegex()
/** 解析核心版本号, eg: `1.0.0` -> IntArray[1, 0, 0] */
@JvmStatic
private fun String.parseMainVersion(): IntArray =
split('.').map { it.toInt() }.toIntArray()
fun parse(version: String): SemVersion {
if (!SEM_VERSION_REGEX.matches(version)) {
throw IllegalArgumentException("`$version` not a valid version")
}
var mainVersionEnd = 0
kotlin.run {
val iterator = version.iterator()
while (iterator.hasNext()) {
val next = iterator.next()
if (next == '-' || next == '+') {
break
}
mainVersionEnd++
}
}
var identifier: String? = null
var metadata: String? = null
if (mainVersionEnd != version.length) {
when (version[mainVersionEnd]) {
'-' -> {
val metadataSplitter = version.indexOf('+', startIndex = mainVersionEnd)
if (metadataSplitter == -1) {
identifier = version.substring(mainVersionEnd + 1)
} else {
identifier = version.substring(mainVersionEnd + 1, metadataSplitter)
metadata = version.substring(metadataSplitter + 1)
}
}
'+' -> {
metadata = version.substring(mainVersionEnd + 1)
}
}
}
return SemVersion(
mainVersion = version.substring(0, mainVersionEnd).parseMainVersion(),
identifier = identifier,
metadata = metadata
)
}
@JvmStatic
private fun String.parseRule(): SemVersion.RangeRequirement {
private fun String.parseRule(): SemVersion.Requirement {
val trimmed = trim()
if (directVersion.matches(trimmed)) {
val parsed = SemVersion.parse(trimmed)
return SemVersion.RangeRequirement {
it.compareTo(parsed) == 0
val parsed = SemVersion.invoke(trimmed)
return object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean = version.compareTo(parsed) == 0
}
}
if (versionSelect.matches(trimmed)) {
@ -47,62 +91,67 @@ internal object SemVersionInternal {
.replace("x", ".+") +
"$"
).toRegex()
return SemVersion.RangeRequirement {
regex.matches(it.toString())
return object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean = regex.matches(version.toString())
}
}
(versionRange.matchEntire(trimmed) ?: versionMathRange.matchEntire(trimmed))?.let { range ->
var start = SemVersion.parse(range.groupValues[1])
var end = SemVersion.parse(range.groupValues[4])
var start = SemVersion.invoke(range.groupValues[1])
var end = SemVersion.invoke(range.groupValues[4])
if (start > end) {
val c = end
end = start
start = c
}
val compareRange = start..end
return SemVersion.RangeRequirement {
it in compareRange
return object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean = version in compareRange
}
}
versionRule.matchEntire(trimmed)?.let { result ->
val operator = result.groupValues[1]
val version = SemVersion.parse(result.groupValues[7])
val version1 = SemVersion.invoke(result.groupValues[7])
return when (operator) {
">=" -> {
SemVersion.RangeRequirement { it >= version }
object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean = version >= version1
}
}
">" -> {
SemVersion.RangeRequirement { it > version }
object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean = version > version1
}
}
"<=" -> {
SemVersion.RangeRequirement { it <= version }
object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean = version <= version1
}
}
"<" -> {
SemVersion.RangeRequirement { it < version }
object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean = version < version1
}
}
"=" -> {
SemVersion.RangeRequirement { it.compareTo(version) == 0 }
}
else -> throw AssertionError("operator=$operator, version=$version")
object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean = version.compareTo(version1) == 0
}
}
throw UnsupportedOperationException("Cannot parse $this")
else -> error("operator=$operator, version=$version1")
}
}
throw IllegalArgumentException("Cannot parse $this")
}
private fun SemVersion.RangeRequirement.withRule(rule: String): SemVersion.RangeRequirement {
return object : SemVersion.RangeRequirement {
override fun check(version: SemVersion): Boolean {
return this@withRule.check(version)
}
override fun toString(): String {
return rule
}
private fun SemVersion.Requirement.withRule(rule: String): SemVersion.Requirement {
return object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean = this@withRule.test(version)
override fun toString(): String = rule
}
}
@JvmStatic
fun parseRangeRequirement(requirement: String): SemVersion.RangeRequirement {
fun parseRangeRequirement(requirement: String): SemVersion.Requirement {
if (requirement.isBlank()) {
throw IllegalArgumentException("Invalid requirement: Empty requirement rule.")
}
@ -110,33 +159,35 @@ internal object SemVersionInternal {
it.parseRule().withRule(it)
}.let { checks ->
if (checks.size == 1) return checks[0]
SemVersion.RangeRequirement {
object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean {
checks.forEach { rule ->
if (rule.check(it)) return@RangeRequirement true
if (rule.test(version)) return true
}
return false
}
return@RangeRequirement false
}.withRule(requirement)
}
}
@JvmStatic
fun SemVersion.compareInternal(other: SemVersion): Int {
fun compareInternal(source: SemVersion, other: SemVersion): Int {
// ignored metadata in comparing
// If $this equals $other (without metadata),
// return same.
if (other.mainVersion.contentEquals(mainVersion) && identifier == other.identifier) {
if (other.mainVersion.contentEquals(source.mainVersion) && source.identifier == other.identifier) {
return 0
}
fun IntArray.getSafe(index: Int) = getOrElse(index) { 0 }
// Compare main-version
for (index in 0 until (max(mainVersion.size, other.mainVersion.size))) {
val result = mainVersion.getSafe(index).compareTo(other.mainVersion.getSafe(index))
for (index in 0 until (max(source.mainVersion.size, other.mainVersion.size))) {
val result = source.mainVersion.getSafe(index).compareTo(other.mainVersion.getSafe(index))
if (result != 0) return result
}
// If main-versions are same.
var identifier0 = identifier
var identifier0 = source.identifier
var identifier1 = other.identifier
// If anyone doesn't have the identifier...
if (identifier0 == null || identifier1 == null) {

View File

@ -11,7 +11,6 @@
package net.mamoe.mirai.console.plugin
import com.vdurmont.semver4j.Semver
import net.mamoe.mirai.console.command.CommandOwner
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.disable
import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable
@ -20,6 +19,7 @@ import net.mamoe.mirai.console.plugin.description.PluginDependency
import net.mamoe.mirai.console.plugin.description.PluginDescription
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
import net.mamoe.mirai.console.plugin.loader.PluginLoader
import net.mamoe.mirai.console.util.SemVersion
/**
* 表示一个 mirai-console 插件.
@ -62,7 +62,7 @@ public inline val Plugin.name: String get() = this.description.name
/**
* 获取 [PluginDescription.version]
*/
public inline val Plugin.version: Semver get() = this.description.version
public inline val Plugin.version: SemVersion get() = this.description.version
/**
* 获取 [PluginDescription.info]

View File

@ -11,6 +11,10 @@
package net.mamoe.mirai.console.plugin.description
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.PLUGIN_ID
import net.mamoe.mirai.console.util.SemVersion
/**
* 插件的一个依赖的信息.
*
@ -20,16 +24,15 @@ public data class PluginDependency @JvmOverloads constructor(
/**
* 依赖插件 ID, [PluginDescription.id]
*/
public val id: String,
@ResolveContext(PLUGIN_ID) public val id: String,
/**
* 依赖版本号. null 时则为不限制版本.
*
* 版本遵循 [语义化版本 2.0 规范](https://semver.org/lang/zh-CN/),
*
* ### 示例
* `Requirement.buildIvy("[1.0, 2.0)")`
* @see SemVersion.Requirement
*/
public val versionRequirement: VersionRequirement? = null,
public val versionRequirement: SemVersion.Requirement? = null,
/**
* 若为 `false`, 插件在找不到此依赖时也能正常加载.
*/
@ -46,7 +49,10 @@ public data class PluginDependency @JvmOverloads constructor(
/**
* @see PluginDependency
*/
public constructor(name: String, isOptional: Boolean = false) : this(
name, null, isOptional
public constructor(
@ResolveContext(PLUGIN_ID) id: String,
isOptional: Boolean = false,
) : this(
id, null, isOptional
)
}

View File

@ -9,10 +9,10 @@
package net.mamoe.mirai.console.plugin.description
import com.vdurmont.semver4j.Semver
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.*
import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.util.SemVersion
/**
@ -93,7 +93,7 @@ public interface PluginDescription {
* @see Semver 语义化版本. 允许 [宽松][Semver.SemverType.LOOSE] 类型版本.
*/
@ResolveContext(PLUGIN_VERSION)
public val version: Semver
public val version: SemVersion
/**
* 插件信息, 允许为空

View File

@ -1,242 +0,0 @@
package net.mamoe.mirai.console.plugin.description
import com.vdurmont.semver4j.Requirement
import com.vdurmont.semver4j.Semver
public sealed class VersionRequirement {
public abstract operator fun contains(version: Semver): Boolean
public fun contains(version: String): Boolean = contains(Semver(version, Semver.SemverType.LOOSE))
public class Exact
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
constructor(
version: Semver,
) : VersionRequirement() {
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
public val version: Semver = version.toStrict()
@Suppress("DEPRECATION_ERROR")
public constructor(version: String) : this(Semver(version, Semver.SemverType.LOOSE))
@Suppress("DEPRECATION_ERROR")
override fun contains(version: Semver): Boolean = this.version.isEquivalentTo(version.toStrict())
}
public data class MatchesNpmPattern(
val pattern: String,
) : VersionRequirement() {
private val requirement = Requirement.buildNPM(pattern)
override fun contains(version: Semver): Boolean = requirement.isSatisfiedBy(version.toStrict())
}
public data class MatchesIvyPattern(
val pattern: String,
) : VersionRequirement() {
private val requirement = Requirement.buildIvy(pattern)
override fun contains(version: Semver): Boolean = requirement.isSatisfiedBy(version.toStrict())
}
public data class MatchesCocoapodsPattern(
val pattern: String,
) : VersionRequirement() {
private val requirement = Requirement.buildCocoapods(pattern)
override fun contains(version: Semver): Boolean = requirement.isSatisfiedBy(version.toStrict())
}
public abstract class Custom : VersionRequirement()
@Suppress("MemberVisibilityCanBePrivate")
public class InRange(
begin: Semver,
public val beginInclusive: Boolean,
end: Semver,
public val endInclusive: Boolean,
) : VersionRequirement() {
public val end: Semver = end.toStrict()
public val begin: Semver = begin.toStrict()
public constructor(
begin: String,
beginInclusive: Boolean,
end: Semver,
endInclusive: Boolean,
) : this(Semver(begin, Semver.SemverType.LOOSE), beginInclusive, end, endInclusive)
public constructor(
begin: String,
beginInclusive: Boolean,
end: String,
endInclusive: Boolean,
) : this(Semver(begin, Semver.SemverType.LOOSE),
beginInclusive,
Semver(end, Semver.SemverType.LOOSE),
endInclusive)
public constructor(
begin: Semver,
beginInclusive: Boolean,
end: String,
endInclusive: Boolean,
) : this(begin, beginInclusive, Semver(end, Semver.SemverType.LOOSE), endInclusive)
override fun contains(version: Semver): Boolean {
val strict = version.toStrict()
return if (beginInclusive) {
strict.isGreaterThanOrEqualTo(begin)
} else {
strict.isGreaterThan(begin)
} && if (endInclusive) {
strict.isLowerThanOrEqualTo(end)
} else {
strict.isLowerThan(end)
}
}
override fun toString(): String {
return buildString {
append(if (beginInclusive) "[" else "(")
append(begin)
append(",")
append(end)
append(if (endInclusive) "]" else ")")
}
}
}
@Suppress("unused", "DeprecatedCallableAddReplaceWith")
public class Builder {
@Suppress("DEPRECATION_ERROR")
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@ILoveKafuuChinoForever
public fun exact(version: Semver): VersionRequirement = Exact(version)
@ILoveKafuuChinoForever
public fun exact(version: String): VersionRequirement = Exact(version)
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@ILoveKafuuChinoForever
public fun custom(checker: (version: Semver) -> Boolean): VersionRequirement {
return object : Custom() {
override fun contains(version: Semver): Boolean = checker(version)
}
}
/**
* @see Semver.SemverType.NPM
*/
@ILoveKafuuChinoForever
public fun npmPattern(versionPattern: String): VersionRequirement {
return MatchesNpmPattern(versionPattern)
}
/**
* @see Semver.SemverType.IVY
*/
@ILoveKafuuChinoForever
public fun ivyPattern(versionPattern: String): VersionRequirement {
return MatchesIvyPattern(versionPattern)
}
/**
* @see Semver.SemverType.COCOAPODS
*/
@ILoveKafuuChinoForever
public fun cocoapodsPattern(versionPattern: String): VersionRequirement {
return MatchesCocoapodsPattern(versionPattern)
}
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@ILoveKafuuChinoForever
public fun range(
begin: Semver,
beginInclusive: Boolean,
end: Semver,
endInclusive: Boolean,
): VersionRequirement = InRange(begin, beginInclusive, end, endInclusive)
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@ILoveKafuuChinoForever
public fun range(
begin: String,
beginInclusive: Boolean,
end: Semver,
endInclusive: Boolean,
): VersionRequirement = InRange(begin, beginInclusive, end, endInclusive)
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@ILoveKafuuChinoForever
public fun range(
begin: Semver,
beginInclusive: Boolean,
end: String,
endInclusive: Boolean,
): VersionRequirement = InRange(begin, beginInclusive, end, endInclusive)
@ILoveKafuuChinoForever
public fun range(
begin: String,
beginInclusive: Boolean,
end: String,
endInclusive: Boolean,
): VersionRequirement = InRange(begin, beginInclusive, end, endInclusive)
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@ILoveKafuuChinoForever
public operator fun Semver.rangeTo(endInclusive: Semver): VersionRequirement {
return InRange(this, true, endInclusive, true)
}
@ILoveKafuuChinoForever
public operator fun Semver.rangeTo(endInclusive: String): VersionRequirement {
return InRange(this, true, Semver(endInclusive, Semver.SemverType.LOOSE), true)
}
@ILoveKafuuChinoForever
public operator fun String.rangeTo(endInclusive: String): VersionRequirement {
return InRange(Semver(this, Semver.SemverType.LOOSE),
true,
Semver(endInclusive, Semver.SemverType.LOOSE),
true)
}
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@ILoveKafuuChinoForever
public operator fun String.rangeTo(endInclusive: Semver): VersionRequirement {
return InRange(Semver(this, Semver.SemverType.LOOSE), true, endInclusive, true)
}
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@ILoveKafuuChinoForever
public infix fun Semver.until(endExclusive: Semver): VersionRequirement {
return InRange(this, true, endExclusive, false)
}
@ILoveKafuuChinoForever
public infix fun Semver.until(endExclusive: String): VersionRequirement {
return InRange(this, true, Semver(endExclusive, Semver.SemverType.LOOSE), false)
}
@ILoveKafuuChinoForever
public infix fun String.until(endExclusive: String): VersionRequirement {
return InRange(Semver(this, Semver.SemverType.LOOSE),
true,
Semver(endExclusive, Semver.SemverType.LOOSE),
false)
}
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@ILoveKafuuChinoForever
public infix fun String.until(endExclusive: Semver): VersionRequirement {
return InRange(Semver(this, Semver.SemverType.LOOSE), true, endExclusive, false)
}
@Suppress("SpellCheckingInspection")
@Retention(AnnotationRetention.BINARY)
@DslMarker
internal annotation class ILoveKafuuChinoForever
}
}

View File

@ -11,12 +11,12 @@
package net.mamoe.mirai.console.plugin.jvm
import com.vdurmont.semver4j.Semver
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.*
import net.mamoe.mirai.console.plugin.description.PluginDependency
import net.mamoe.mirai.console.plugin.description.PluginDescription
import net.mamoe.mirai.console.plugin.description.VersionRequirement
import net.mamoe.mirai.console.util.SemVersion
import net.mamoe.mirai.console.util.SemVersionRangeRequirementBuilder
/**
* JVM 插件的描述. 通常作为 `plugin.yml`
@ -34,6 +34,7 @@ public interface JvmPluginDescription : PluginDescription {
* 构建 [JvmPluginDescription]
* @see JvmPluginDescriptionBuilder
*/
@JvmName("create")
@JvmSynthetic
public operator fun invoke(
/**
@ -55,8 +56,7 @@ public interface JvmPluginDescription : PluginDescription {
* 构建 [JvmPluginDescription]
* @see JvmPluginDescriptionBuilder
*/
@Suppress("DEPRECATION_ERROR")
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@JvmName("create")
@JvmSynthetic
public operator fun invoke(
/**
@ -66,7 +66,7 @@ public interface JvmPluginDescription : PluginDescription {
/**
* @see [PluginDescription.version]
*/
@ResolveContext(PLUGIN_VERSION) version: Semver,
@ResolveContext(PLUGIN_VERSION) version: SemVersion,
/**
* @see [PluginDescription.name]
*/
@ -97,17 +97,14 @@ public interface JvmPluginDescription : PluginDescription {
*
* @see [JvmPluginDescription.invoke]
*/
public class JvmPluginDescriptionBuilder
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
constructor(
public class JvmPluginDescriptionBuilder(
private var id: String,
private var version: Semver,
private var version: SemVersion,
) {
@Suppress("DEPRECATION_ERROR")
public constructor(
@ResolveContext(PLUGIN_NAME) id: String,
@ResolveContext(PLUGIN_ID) id: String,
@ResolveContext(PLUGIN_VERSION) version: String,
) : this(id, Semver(version, Semver.SemverType.LOOSE))
) : this(id, SemVersion(version))
private var name: String = id
private var author: String = ""
@ -118,14 +115,12 @@ constructor(
public fun name(@ResolveContext(PLUGIN_NAME) value: String): JvmPluginDescriptionBuilder =
apply { this.name = value.trim() }
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@ILoveKuriyamaMiraiForever
public fun version(@ResolveContext(PLUGIN_VERSION) value: String): JvmPluginDescriptionBuilder =
apply { this.version = Semver(value, Semver.SemverType.LOOSE) }
apply { this.version = SemVersion(value) }
@Deprecated("Semver 将会在 1.0-RC 被替换为 Console 自己实现的版本。请临时使用 String。", level = DeprecationLevel.ERROR)
@ILoveKuriyamaMiraiForever
public fun version(@ResolveContext(PLUGIN_VERSION) value: Semver): JvmPluginDescriptionBuilder =
public fun version(@ResolveContext(PLUGIN_VERSION) value: SemVersion): JvmPluginDescriptionBuilder =
apply { this.version = value }
@ILoveKuriyamaMiraiForever
@ -161,7 +156,7 @@ constructor(
public fun dependsOn(
@ResolveContext(PLUGIN_ID) pluginId: String,
isOptional: Boolean = false,
versionRequirement: VersionRequirement,
versionRequirement: SemVersion.Requirement,
): JvmPluginDescriptionBuilder = apply {
this.dependencies.add(PluginDependency(pluginId, versionRequirement, isOptional))
}
@ -174,7 +169,7 @@ constructor(
@ILoveKuriyamaMiraiForever
public fun dependsOn(
@ResolveContext(PLUGIN_ID) pluginId: String,
versionRequirement: VersionRequirement,
versionRequirement: SemVersion.Requirement,
): JvmPluginDescriptionBuilder = apply {
this.dependencies.add(PluginDependency(pluginId, versionRequirement, false))
}
@ -203,17 +198,17 @@ constructor(
* ```
*
* @see PluginDependency
* @see VersionRequirement.Builder
* @see SemVersionRangeRequirementBuilder
*/
@ILoveKuriyamaMiraiForever
public fun dependsOn(
@ResolveContext(PLUGIN_ID) pluginId: String,
isOptional: Boolean = false,
versionRequirement: VersionRequirement.Builder.() -> VersionRequirement,
versionRequirement: SemVersionRangeRequirementBuilder.() -> SemVersion.Requirement,
): JvmPluginDescriptionBuilder =
apply {
this.dependencies.add(PluginDependency(pluginId,
VersionRequirement.Builder().run(versionRequirement),
SemVersionRangeRequirementBuilder.run(versionRequirement),
isOptional))
}
@ -236,56 +231,26 @@ constructor(
*
* @see JvmPluginDescription
*/
@Deprecated(
"""
将在 1.0-RC 删除. 请使用 JvmPluginDescription.
""",
replaceWith = ReplaceWith(
"JvmPluginDescription",
"net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription"
),
level = DeprecationLevel.ERROR
)
public data class SimpleJvmPluginDescription
@Deprecated(
"""
构造器不稳定, 将在 1.0-RC 删除. 请使用 JvmPluginDescriptionBuilder.
""",
replaceWith = ReplaceWith(
"JvmPluginDescription(name, version) {}",
"net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription.Companion.invoke"
),
level = DeprecationLevel.ERROR
)
@JvmOverloads public constructor(
public override val name: String,
public override val version: Semver,
public override val id: String = name,
public override val author: String = "",
public override val info: String = "",
public override val dependencies: Set<PluginDependency> = setOf(),
internal data class SimpleJvmPluginDescription
@JvmOverloads constructor(
override val name: String,
override val version: SemVersion,
override val id: String = name,
override val author: String = "",
override val info: String = "",
override val dependencies: Set<PluginDependency> = setOf(),
) : JvmPluginDescription {
@Deprecated(
"""
构造器不稳定, 将在 1.0-RC 删除. 请使用 JvmPluginDescriptionBuilder.
""",
replaceWith = ReplaceWith(
"JvmPluginDescription.invoke(name, version) {}",
"net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription.Companion.invoke"
),
level = DeprecationLevel.ERROR
)
@Suppress("DEPRECATION_ERROR")
@JvmOverloads
public constructor(
constructor(
name: String,
version: String,
id: String = name,
author: String = "",
info: String = "",
dependencies: Set<PluginDependency> = setOf(),
) : this(name, Semver(version, Semver.SemverType.LOOSE), id, author, info, dependencies)
) : this(name, SemVersion(version), id, author, info, dependencies)
init {
require(!name.contains(':')) { "':' is forbidden in plugin name" }

View File

@ -15,7 +15,7 @@ import net.mamoe.mirai.console.internal.plugin.BuiltInJvmPluginLoaderImpl
import net.mamoe.mirai.console.plugin.loader.FilePluginLoader
/**
* 内建的 Jar (JVM) 插件加载器
* JVM 插件加载器
*/
public interface JvmPluginLoader : CoroutineScope, FilePluginLoader<JvmPlugin, JvmPluginDescription> {
/**

View File

@ -61,10 +61,9 @@ public interface PluginLoader<P : Plugin, D : PluginDescription> {
* @throws PluginLoadException 在加载插件遇到意料之中的错误时抛出 (如无法读取插件信息等).
*
* @see PluginDescription 插件描述
* @see getPluginDescription receiver, 接受参数的版本.
*/
@Throws(PluginLoadException::class)
public fun getPluginDescription(plugin: P): D // Java signature: `public D getDescription(P)`
public fun getPluginDescription(plugin: P): D
/**
* 主动加载一个插件 (实例), 但不 [启用][enable] . 返回加载成功的主类实例

View File

@ -12,29 +12,27 @@
* @author Karlatemp <karlatemp@vip.qq.com> <https://github.com/Karlatemp>
*/
@file:Suppress("unused")
package net.mamoe.mirai.console.util
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.builtins.serializer
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.PLUGIN_VERSION
import net.mamoe.mirai.console.internal.data.map
import net.mamoe.mirai.console.internal.util.SemVersionInternal
import net.mamoe.mirai.console.util.SemVersion.Companion.equals
import net.mamoe.mirai.console.util.SemVersion.Requirement
/**
* 语义化版本支持
* [语义化版本](https://semver.org/lang/zh-CN/) 支持
*
* 在阅读此文件前, 请先阅读 https://semver.org/lang/zh-CN/
* 该文档说明了语义化版本是什么, 此文件不再过多描述
* 解析示例:
*
* ----
*
* 这是一个例子 `1.0.0-M4+c25733b8`
*
* 将会解析出三个内容, mainVersion(核心版本号), identifier(先行版本号) metadata(元数据).
*
* 对这个例子进行解析会得到
* `1.0.0-M4+c25733b8` 将会解析出三个内容, mainVersion (核心版本号), [identifier] (先行版本号) [metadata] (元数据).
* ```
* SemVersion(
* mainVersion = IntArray [1, 0, 0],
@ -43,47 +41,45 @@ import net.mamoe.mirai.console.util.SemVersion.Companion.equals
* )
* ```
* 其中 identifier metadata 都是可选的.
* 对于核心版本号, 此实现稍微比 semver 宽松一些, 允许 x.y 的存在.
* 但是不允许 0.0.0.0 之类的存在
*
* 对于核心版本号, 此实现稍微比 semver 宽松一些, 允许 x.y 的存在.
*
* @see Requirement
* @see SemVersion.invoke
*/
@Serializable(with = SemVersion.SemVersionAsStringSerializer::class)
public data class SemVersion internal constructor(
public data class SemVersion
/**
* @see SemVersion.invoke 字符串解析
*/
internal constructor(
/** 核心版本号, 由主版本号, 次版本号和修订号组成, 其中修订号不一定存在 */
public val mainVersion: IntArray,
/** 先行版本号识别符 */
public val identifier: String? = null,
/** 版本号元数据, 不参与版本号对比([compareTo]), 但是参与版本号严格对比([equals]) */
public val metadata: String? = null
public val metadata: String? = null,
) : Comparable<SemVersion> {
/**
* 一条依赖规则
* @see [parseRangeRequirement]
*/
public fun interface RangeRequirement {
public interface Requirement {
/** 在 [version] 满足此要求时返回 true */
public fun check(version: SemVersion): Boolean
public fun test(version: SemVersion): Boolean
}
public object SemVersionAsStringSerializer : KSerializer<SemVersion> by String.serializer().map(
serializer = { it.toString() },
deserializer = { parse(it) }
deserializer = { SemVersion(it) }
)
public companion object {
private val SEM_VERSION_REGEX =
"""^(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$""".toRegex()
/** 解析核心版本号, eg: `1.0.0` -> IntArray[1, 0, 0] */
@JvmStatic
private fun String.parseMainVersion(): IntArray =
split('.').map { it.toInt() }.toIntArray()
/**
* 解析一个版本号, 将会返回一个 [SemVersion],
* 如果发生解析错误将会抛出一个 [IllegalArgumentException] 或者 [NumberFormatException]
*
* 对于版本号的组成, 我们有以下规定:
* 对于版本号的组成, 有以下规定:
* - 必须包含主版本号和次版本号
* - 存在 先行版本号 的时候 先行版本号 不能为空
* - 存在 元数据 的时候 元数据 不能为空
@ -100,45 +96,8 @@ public data class SemVersion internal constructor(
*/
@Throws(IllegalArgumentException::class, NumberFormatException::class)
@JvmStatic
public fun parse(version: String): SemVersion {
if (!SEM_VERSION_REGEX.matches(version)) {
throw IllegalArgumentException("`$version` not a valid version")
}
var mainVersionEnd: Int = 0
kotlin.run {
val iterator = version.iterator()
while (iterator.hasNext()) {
val next = iterator.next()
if (next == '-' || next == '+') {
break
}
mainVersionEnd++
}
}
var identifier: String? = null
var metadata: String? = null
if (mainVersionEnd != version.length) {
when (version[mainVersionEnd]) {
'-' -> {
val metadataSplitter = version.indexOf('+', startIndex = mainVersionEnd)
if (metadataSplitter == -1) {
identifier = version.substring(mainVersionEnd + 1)
} else {
identifier = version.substring(mainVersionEnd + 1, metadataSplitter)
metadata = version.substring(metadataSplitter + 1)
}
}
'+' -> {
metadata = version.substring(mainVersionEnd + 1)
}
}
}
return SemVersion(
mainVersion = version.substring(0, mainVersionEnd).parseMainVersion(),
identifier = identifier,
metadata = metadata
)
}
@JvmName("parse")
public operator fun invoke(@ResolveContext(PLUGIN_VERSION) version: String): SemVersion = SemVersionInternal.parse(version)
/**
* 解析一条依赖需求描述, 在无法解析的时候抛出 [IllegalArgumentException]
@ -154,7 +113,7 @@ public data class SemVersion internal constructor(
* - `< 1.0.0-RC` 要求 1.0.0-RC 之前的版本, 不能是 1.0.0-RC
* - `<= 1.0.0-RC` 要求 1.0.0-RC 或之前的版本, 可以是 1.0.0-RC
*
* 对于多个规则, 也允许使用 `||` 拼接在一起.
* 对于多个规则, 也允许使用 `||` 拼接.
* 例如:
* - `1.x || 2.x || 3.0.0`
* - `<= 0.5.3 || >= 1.0.0`
@ -165,35 +124,32 @@ public data class SemVersion internal constructor(
*/
@Throws(IllegalArgumentException::class)
@JvmStatic
public fun parseRangeRequirement(requirement: String): RangeRequirement {
return SemVersionInternal.parseRangeRequirement(requirement)
}
public fun parseRangeRequirement(requirement: String): Requirement =
SemVersionInternal.parseRangeRequirement(requirement)
/** @see [RangeRequirement.check] */
/** @see [Requirement.test] */
@JvmStatic
public fun RangeRequirement.check(version: String): Boolean = check(parse(version))
public fun Requirement.test(@ResolveContext(PLUGIN_VERSION) version: String): Boolean = test(invoke(version))
/**
* 当满足 [requirement] 时返回 true, 否则返回 false
*/
@JvmStatic
public fun SemVersion.satisfies(requirement: RangeRequirement): Boolean = requirement.check(this)
public fun SemVersion.satisfies(requirement: Requirement): Boolean = requirement.test(this)
/** for Kotlin only */
@JvmStatic
@JvmSynthetic
public operator fun RangeRequirement.contains(version: SemVersion): Boolean = check(version)
public operator fun Requirement.contains(version: SemVersion): Boolean = test(version)
/** for Kotlin only */
@JvmStatic
@JvmSynthetic
public operator fun RangeRequirement.contains(version: String): Boolean = check(version)
public operator fun Requirement.contains(@ResolveContext(PLUGIN_VERSION) version: String): Boolean = test(version)
}
@Transient
private var toString: String? = null // For cache.
override fun toString(): String {
return toString ?: kotlin.run {
private val toString: String by lazy(LazyThreadSafetyMode.NONE) {
buildString {
mainVersion.joinTo(this, ".")
identifier?.let { identifier ->
@ -204,10 +160,11 @@ public data class SemVersion internal constructor(
append('+')
append(metadata)
}
}.also { toString = it }
}
}
override fun toString(): String = toString
/**
* [SemVersion] 转为 Kotlin data class 风格的 [String]
*/
@ -237,6 +194,6 @@ public data class SemVersion internal constructor(
* if it's greater than [other].
*/
public override operator fun compareTo(other: SemVersion): Int {
return SemVersionInternal.run { compareInternal(other) }
return SemVersionInternal.run { compareInternal(this@SemVersion, other) }
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found via the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*
*/
package net.mamoe.mirai.console.util
/**
* 构造 [SemVersion.Requirement] DSL
*/
public object SemVersionRangeRequirementBuilder {
/** @see [SemVersion.parseRangeRequirement] */
@ILoveHim188moeForever
public fun parse(rule: String): SemVersion.Requirement = SemVersion.parseRangeRequirement(rule)
@ILoveHim188moeForever
public infix fun SemVersion.Requirement.or(other: SemVersion.Requirement): SemVersion.Requirement {
return object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean {
return this@or.test(version) || other.test(version)
}
override fun toString(): String {
return "(${this@or}) or ($other)"
}
}
}
@ILoveHim188moeForever
public infix fun String.or(other: String): SemVersion.Requirement = parse(this) or parse(other)
@ILoveHim188moeForever
public infix fun SemVersion.Requirement.or(other: String): SemVersion.Requirement = or(parse(other))
@ILoveHim188moeForever
public infix fun String.or(other: SemVersion.Requirement): SemVersion.Requirement = parse(this) or other
@ILoveHim188moeForever
public infix fun SemVersion.Requirement.and(other: SemVersion.Requirement): SemVersion.Requirement {
return object : SemVersion.Requirement {
override fun test(version: SemVersion): Boolean {
return this@and.test(version) && other.test(version)
}
override fun toString(): String {
return "(${this@and}) or ($other)"
}
}
}
@ILoveHim188moeForever
public infix fun String.and(other: String): SemVersion.Requirement = parse(this) and parse(other)
@ILoveHim188moeForever
public infix fun SemVersion.Requirement.and(other: String): SemVersion.Requirement = and(parse(other))
@ILoveHim188moeForever
public infix fun String.and(other: SemVersion.Requirement): SemVersion.Requirement = parse(this) and other
@Suppress("NOTHING_TO_INLINE")
@ILoveHim188moeForever
public inline fun custom(rule: SemVersion.Requirement): SemVersion.Requirement = rule
/**
* 标注一个 [SemVersionRangeRequirementBuilder] DSL
*/
@Suppress("SpellCheckingInspection")
@Retention(AnnotationRetention.BINARY)
@DslMarker
internal annotation class ILoveHim188moeForever
/** [SemVersionRangeRequirementBuilder] 的使用示例 */
@Suppress("unused")
private class ExampleOfBuilder {
val e1 = SemVersionRangeRequirementBuilder.run {
"1.0.0" or "1.1.5"
}
val e2 = SemVersionRangeRequirementBuilder.run {
parse("> 1.0.0") and parse("< 1.2.3")
}
val e3 = SemVersionRangeRequirementBuilder.run {
("> 1.0.0" and "< 1.2.3") or "2.0.0"
}
}
}

View File

@ -9,7 +9,6 @@
package net.mamoe.mirai.console
import com.vdurmont.semver4j.Semver
import kotlinx.coroutines.*
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
import net.mamoe.mirai.console.command.CommandManager
@ -20,6 +19,7 @@ import net.mamoe.mirai.console.plugin.loader.PluginLoader
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.console.util.ConsoleInput
import net.mamoe.mirai.console.util.ConsoleInternalApi
import net.mamoe.mirai.console.util.SemVersion
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.LoginSolver
@ -43,8 +43,8 @@ fun initTestEnvironment() {
get() = "Test"
override val vendor: String
get() = "Test"
override val version: Semver
get() = Semver("1.0.0")
override val version: SemVersion
get() = SemVersion("1.0.0")
}
override val builtInPluginLoaders: List<Lazy<PluginLoader<*, *>>> = listOf(lazy { JvmPluginLoader })

View File

@ -14,13 +14,13 @@
package net.mamoe.mirai.console.util
import net.mamoe.mirai.console.util.SemVersion.Companion.check
import net.mamoe.mirai.console.util.SemVersion.Companion.test
import org.junit.jupiter.api.Test
internal class TestSemVersion {
@Test
internal fun testCompare() {
fun String.sem(): SemVersion = SemVersion.parse(this)
fun String.sem(): SemVersion = SemVersion.invoke(this)
assert("1.0".sem() < "1.0.1".sem())
assert("1.0.0".sem() == "1.0".sem())
assert("1.1".sem() > "1.0.0".sem())
@ -43,13 +43,13 @@ internal class TestSemVersion {
@Test
internal fun testRequirement() {
fun SemVersion.RangeRequirement.assert(version: String): SemVersion.RangeRequirement {
assert(check(version)) { version }
fun SemVersion.Requirement.assert(version: String): SemVersion.Requirement {
assert(test(version)) { version }
return this
}
fun SemVersion.RangeRequirement.assertFalse(version: String): SemVersion.RangeRequirement {
assert(!check(version)) { version }
fun SemVersion.Requirement.assertFalse(version: String): SemVersion.Requirement {
assert(!test(version)) { version }
return this
}
SemVersion.parseRangeRequirement("1.0")
@ -85,12 +85,12 @@ internal class TestSemVersion {
}
private fun String.check() {
val sem = SemVersion.parse(this)
val sem = SemVersion.invoke(this)
assert(this == sem.toString()) { "$this != $sem" }
}
private fun String.checkInvalid() {
kotlin.runCatching { SemVersion.parse(this) }
kotlin.runCatching { SemVersion.invoke(this) }
.onSuccess { assert(false) { "$this not a invalid sem-version" } }
}

View File

@ -26,9 +26,22 @@ fun DependencyHandler.compileAndTestRuntime(any: Any) {
fun DependencyHandler.smartApi(
dependencyNotation: String
): ExternalModuleDependency {
return smart("api", dependencyNotation)
}
fun DependencyHandler.smartImplementation(
dependencyNotation: String
): ExternalModuleDependency {
return smart("implementation", dependencyNotation)
}
private fun DependencyHandler.smart(
configuration: String,
dependencyNotation: String
): ExternalModuleDependency {
return addDependencyTo(
this, "api", dependencyNotation
this, configuration, dependencyNotation
) {
fun exclude(group: String, module: String) {
exclude(mapOf(

View File

@ -1,2 +1,32 @@
# Mirai Console - Contributing
感谢你来到这里,感谢你对 Mirai Console 做的一切贡献。
## 开发 Mirai Console
### 模块
Mirai Console 项目由四个模块组成后端前端Gradle 插件Intellij 插件。
```
/
|--- backend 后端
| |--- codegen 后端代码生成工具
| `--- mirai-console 后端主模块, 发布为 net.mamoe:mirai-console
|--- buildSrc 项目构建
|--- frontend 前端
| `--- mirai-console-terminal 终端前端,发布为 net.mamoe:mirai-console-terminal
`--- tools 开发工具
|--- compiler-common 编译器通用模块
|--- gradle-plugin Gradle 插件,发布为 net.mamoe.mirai-console
`--- intellij-plugin IntelliJ 平台 IDE 插件,发布为 Mirai Console
```
请前往各模块内的 README.md 查看详细说明。
### 构建
```shell script
gradlew build
```
首次加载和构建 mirai-console 项目可能要花费数小时时间。

View File

@ -23,7 +23,6 @@
package net.mamoe.mirai.console.terminal
import com.vdurmont.semver4j.Semver
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName
@ -39,10 +38,7 @@ import net.mamoe.mirai.console.plugin.loader.PluginLoader
import net.mamoe.mirai.console.terminal.ConsoleInputImpl.requestInput
import net.mamoe.mirai.console.terminal.noconsole.AllEmptyLineReader
import net.mamoe.mirai.console.terminal.noconsole.NoConsole
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.console.util.ConsoleInput
import net.mamoe.mirai.console.util.ConsoleInternalApi
import net.mamoe.mirai.console.util.NamedSupervisorJob
import net.mamoe.mirai.console.util.*
import net.mamoe.mirai.utils.*
import org.fusesource.jansi.Ansi
import org.jline.reader.LineReader
@ -155,7 +151,7 @@ val terminal: Terminal = run {
private object ConsoleFrontEndDescImpl : MiraiConsoleFrontEndDescription {
override val name: String get() = "Terminal"
override val vendor: String get() = "Mamoe Technologies"
override val version: Semver = net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version
override val version: SemVersion = net.mamoe.mirai.console.internal.MiraiConsoleBuildConstants.version
}
private val ANSI_RESET = Ansi().reset().toString()