mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
Merge branch 'master' into command
This commit is contained in:
commit
12a035fce8
BIN
.idea/icon.png
Normal file
BIN
.idea/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
@ -14,7 +14,7 @@ import java.time.Instant
|
|||||||
|
|
||||||
internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants)
|
internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants)
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val buildDate: Instant = Instant.ofEpochSecond(1600596035)
|
val buildDate: Instant = Instant.ofEpochSecond(1600663022)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val version: SemVersion = SemVersion("1.0-RC-dev-28")
|
val version: SemVersion = SemVersion("1.0-RC-dev-28")
|
||||||
|
@ -0,0 +1,251 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.internal.util.semver
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.util.SemVersion
|
||||||
|
import kotlin.math.max
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
internal object RangeTokenReader {
|
||||||
|
enum class TokenType {
|
||||||
|
STRING,
|
||||||
|
|
||||||
|
/* 左括号 */
|
||||||
|
LEFT,
|
||||||
|
|
||||||
|
/* 右括号 */
|
||||||
|
RIGHT,
|
||||||
|
|
||||||
|
/* || */
|
||||||
|
OR,
|
||||||
|
|
||||||
|
/* && */
|
||||||
|
AND,
|
||||||
|
GROUP
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Token {
|
||||||
|
abstract val type: TokenType
|
||||||
|
abstract val value: String
|
||||||
|
abstract val position: Int
|
||||||
|
|
||||||
|
class LeftBracket(override val position: Int) : Token() {
|
||||||
|
override val type: TokenType get() = TokenType.LEFT
|
||||||
|
override val value: String get() = "{"
|
||||||
|
|
||||||
|
override fun toString(): String = "LB{"
|
||||||
|
}
|
||||||
|
|
||||||
|
class RightBracket(override val position: Int) : Token() {
|
||||||
|
override val type: TokenType get() = TokenType.RIGHT
|
||||||
|
override val value: String get() = "}"
|
||||||
|
|
||||||
|
override fun toString(): String = "RB}"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Or(override val position: Int) : Token() {
|
||||||
|
override val type: TokenType get() = TokenType.OR
|
||||||
|
override val value: String get() = "||"
|
||||||
|
override fun toString(): String = "OR||"
|
||||||
|
}
|
||||||
|
|
||||||
|
class And(override val position: Int) : Token() {
|
||||||
|
override val type: TokenType get() = TokenType.AND
|
||||||
|
override val value: String get() = "&&"
|
||||||
|
|
||||||
|
override fun toString(): String = "AD&&"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Group(val values: List<Token>, override val position: Int) : Token() {
|
||||||
|
override val type: TokenType get() = TokenType.GROUP
|
||||||
|
override val value: String get() = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
class Raw(val source: String, val start: Int, val end: Int) : Token() {
|
||||||
|
override val value: String get() = source.substring(start, end)
|
||||||
|
override val position: Int
|
||||||
|
get() = start
|
||||||
|
override val type: TokenType get() = TokenType.STRING
|
||||||
|
|
||||||
|
override fun toString(): String = "R:$value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseToTokens(source: String): List<Token> = ArrayList<Token>(
|
||||||
|
max(source.length / 3, 16)
|
||||||
|
).apply {
|
||||||
|
var index = 0
|
||||||
|
var position = 0
|
||||||
|
fun flushOld() {
|
||||||
|
if (position > index) {
|
||||||
|
val id = index
|
||||||
|
index = position
|
||||||
|
for (i in id until position) {
|
||||||
|
if (!source[i].isWhitespace()) {
|
||||||
|
add(Token.Raw(source, id, position))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val iterator = source.indices.iterator()
|
||||||
|
for (i in iterator) {
|
||||||
|
position = i
|
||||||
|
when (source[i]) {
|
||||||
|
'{' -> {
|
||||||
|
flushOld()
|
||||||
|
add(Token.LeftBracket(i))
|
||||||
|
index = i + 1
|
||||||
|
}
|
||||||
|
'|' -> {
|
||||||
|
if (source.getOrNull(i + 1) == '|') {
|
||||||
|
flushOld()
|
||||||
|
add(Token.Or(i))
|
||||||
|
index = i + 2
|
||||||
|
iterator.nextInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'&' -> {
|
||||||
|
if (source.getOrNull(i + 1) == '&') {
|
||||||
|
flushOld()
|
||||||
|
add(Token.And(i))
|
||||||
|
index = i + 2
|
||||||
|
iterator.nextInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'}' -> {
|
||||||
|
flushOld()
|
||||||
|
add(Token.RightBracket(i))
|
||||||
|
index = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
position = source.length
|
||||||
|
flushOld()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun collect(source: String, tokens: Iterator<Token>, root: Boolean): List<Token> = ArrayList<Token>().apply {
|
||||||
|
tokens.forEach { token ->
|
||||||
|
if (token is Token.LeftBracket) {
|
||||||
|
add(Token.Group(collect(source, tokens, false), token.position))
|
||||||
|
} else if (token is Token.RightBracket) {
|
||||||
|
if (root) {
|
||||||
|
throw IllegalArgumentException("Syntax error: Unexpected }, ${buildMsg(source, token.position)}")
|
||||||
|
} else {
|
||||||
|
return@apply
|
||||||
|
}
|
||||||
|
} else add(token)
|
||||||
|
}
|
||||||
|
if (!root) {
|
||||||
|
throw IllegalArgumentException("Syntax error: Excepted }, ${buildMsg(source, source.length)}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildMsg(source: String, position: Int): String {
|
||||||
|
val ed = min(position + 10, source.length)
|
||||||
|
val st = max(0, position - 10)
|
||||||
|
return buildString {
|
||||||
|
append('`')
|
||||||
|
if (st != 0) append("...")
|
||||||
|
append(source, st, ed)
|
||||||
|
if (ed != source.length) append("...")
|
||||||
|
append("` at ").append(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun check(source: String, tokens: Iterator<Token>, group: Token.Group?) {
|
||||||
|
if (!tokens.hasNext()) {
|
||||||
|
throw IllegalArgumentException("Syntax error: empty rule, ${buildMsg(source, group?.position ?: 0)}")
|
||||||
|
}
|
||||||
|
var type = false
|
||||||
|
do {
|
||||||
|
val next = tokens.next()
|
||||||
|
if (type) {
|
||||||
|
if (next is Token.Group || next is Token.Raw) {
|
||||||
|
throw IllegalArgumentException("Syntax error: Except logic but got expression, ${buildMsg(source, next.position)}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (next is Token.Or || next is Token.And) {
|
||||||
|
throw IllegalArgumentException("Syntax error: Except expression but got logic, ${buildMsg(source, next.position)}")
|
||||||
|
}
|
||||||
|
if (next is Token.Group) {
|
||||||
|
check(source, next.values.iterator(), next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type = !type
|
||||||
|
} while (tokens.hasNext())
|
||||||
|
if (!type) {
|
||||||
|
throw IllegalArgumentException("Syntax error: Except more expression, ${buildMsg(source, group?.values?.last()?.position ?: source.length)}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parse(source: String, token: Token): SemVersion.Requirement {
|
||||||
|
return when (token) {
|
||||||
|
is Token.Group -> {
|
||||||
|
if (token.values.size == 1) {
|
||||||
|
parse(source, token.values.first())
|
||||||
|
} else {
|
||||||
|
val logic = token.values.asSequence().map { it.type }.filter {
|
||||||
|
it == TokenType.OR || it == TokenType.AND
|
||||||
|
}.toSet()
|
||||||
|
if (logic.size == 2) {
|
||||||
|
throw IllegalArgumentException("Syntax error: || and && cannot use in one group, ${buildMsg(source, token.position)}")
|
||||||
|
}
|
||||||
|
val rules = token.values.asSequence().filter {
|
||||||
|
it is Token.Raw || it is Token.Group
|
||||||
|
}.map { parse(source, it) }.toList()
|
||||||
|
when (logic.first()) {
|
||||||
|
TokenType.OR -> {
|
||||||
|
return object : SemVersion.Requirement {
|
||||||
|
override fun test(version: SemVersion): Boolean {
|
||||||
|
rules.forEach { if (it.test(version)) return true }
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenType.AND -> {
|
||||||
|
return object : SemVersion.Requirement {
|
||||||
|
override fun test(version: SemVersion): Boolean {
|
||||||
|
rules.forEach { if (!it.test(version)) return false }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssertionError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Token.Raw -> SemVersionInternal.parseRule(token.value)
|
||||||
|
else -> throw AssertionError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun StringBuilder.dump(prefix: String, token: Token) {
|
||||||
|
when (token) {
|
||||||
|
is Token.LeftBracket -> append("${prefix}LF {\n")
|
||||||
|
|
||||||
|
is Token.RightBracket -> append("${prefix}LR }\n")
|
||||||
|
|
||||||
|
is Token.Or -> append("${prefix}OR ||\n")
|
||||||
|
|
||||||
|
is Token.And -> append("${prefix}AND &&\n")
|
||||||
|
is Token.Group -> {
|
||||||
|
append("${prefix}GROUP {\n")
|
||||||
|
token.values.forEach { dump("$prefix ", it) }
|
||||||
|
append("${prefix}}\n")
|
||||||
|
}
|
||||||
|
is Token.Raw -> append("${prefix}RAW ${token.value}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,12 +8,9 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
package net.mamoe.mirai.console.internal.util.semver
|
||||||
* @author Karlatemp <karlatemp@vip.qq.com> <https://github.com/Karlatemp>
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.mamoe.mirai.console.internal.util
|
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.internal.util.semver.RangeTokenReader.dump
|
||||||
import net.mamoe.mirai.console.util.SemVersion
|
import net.mamoe.mirai.console.util.SemVersion
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@ -22,10 +19,9 @@ import kotlin.math.min
|
|||||||
internal object SemVersionInternal {
|
internal object SemVersionInternal {
|
||||||
private val directVersion = """^[0-9]+(\.[0-9]+)+(|[\-+].+)$""".toRegex()
|
private val directVersion = """^[0-9]+(\.[0-9]+)+(|[\-+].+)$""".toRegex()
|
||||||
private val versionSelect = """^[0-9]+(\.[0-9]+)*\.x$""".toRegex()
|
private val versionSelect = """^[0-9]+(\.[0-9]+)*\.x$""".toRegex()
|
||||||
private val versionRange = """([0-9]+(\.[0-9]+)+(|[\-+].+))\s*\-\s*([0-9]+(\.[0-9]+)+(|[\-+].+))""".toRegex()
|
|
||||||
private val versionMathRange =
|
private val versionMathRange =
|
||||||
"""\[([0-9]+(\.[0-9]+)+(|[\-+].+))\s*\,\s*([0-9]+(\.[0-9]+)+(|[\-+].+))\]""".toRegex()
|
"""([\[\(])([0-9]+(\.[0-9]+)+(|[\-+].+))\s*\,\s*([0-9]+(\.[0-9]+)+(|[\-+].+))([\]\)])""".toRegex()
|
||||||
private val versionRule = """^((\>\=)|(\<\=)|(\=)|(\>)|(\<))\s*([0-9]+(\.[0-9]+)+(|[\-+].+))$""".toRegex()
|
private val versionRule = """^((\>\=)|(\<\=)|(\=)|(\!\=)|(\>)|(\<))\s*([0-9]+(\.[0-9]+)+(|[\-+].+))$""".toRegex()
|
||||||
|
|
||||||
private val SEM_VERSION_REGEX =
|
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()
|
"""^(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()
|
||||||
@ -69,16 +65,19 @@ internal object SemVersionInternal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val mainVersion = version.substring(0, mainVersionEnd).parseMainVersion()
|
||||||
return SemVersion(
|
return SemVersion(
|
||||||
mainVersion = version.substring(0, mainVersionEnd).parseMainVersion(),
|
major = mainVersion[0],
|
||||||
|
minor = mainVersion[1],
|
||||||
|
patch = mainVersion.getOrNull(2),
|
||||||
identifier = identifier,
|
identifier = identifier,
|
||||||
metadata = metadata
|
metadata = metadata
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
private fun String.parseRule(): SemVersion.Requirement {
|
internal fun parseRule(rule: String): SemVersion.Requirement {
|
||||||
val trimmed = trim()
|
val trimmed = rule.trim()
|
||||||
if (directVersion.matches(trimmed)) {
|
if (directVersion.matches(trimmed)) {
|
||||||
val parsed = SemVersion.invoke(trimmed)
|
val parsed = SemVersion.invoke(trimmed)
|
||||||
return object : SemVersion.Requirement {
|
return object : SemVersion.Requirement {
|
||||||
@ -95,22 +94,40 @@ internal object SemVersionInternal {
|
|||||||
override fun test(version: SemVersion): Boolean = regex.matches(version.toString())
|
override fun test(version: SemVersion): Boolean = regex.matches(version.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(versionRange.matchEntire(trimmed) ?: versionMathRange.matchEntire(trimmed))?.let { range ->
|
versionMathRange.matchEntire(trimmed)?.let { range ->
|
||||||
var start = SemVersion.invoke(range.groupValues[1])
|
// 1 mode
|
||||||
var end = SemVersion.invoke(range.groupValues[4])
|
// 2 first
|
||||||
|
// 5 sec
|
||||||
|
// 8 type
|
||||||
|
var typeStart = range.groupValues[1][0]
|
||||||
|
var typeEnd = range.groupValues[8][0]
|
||||||
|
var start = SemVersion.invoke(range.groupValues[2])
|
||||||
|
var end = SemVersion.invoke(range.groupValues[5])
|
||||||
if (start > end) {
|
if (start > end) {
|
||||||
val c = end
|
val c = end
|
||||||
end = start
|
end = start
|
||||||
start = c
|
start = c
|
||||||
|
val x = typeEnd
|
||||||
|
typeEnd = typeStart
|
||||||
|
typeStart = x
|
||||||
|
}
|
||||||
|
val a: (SemVersion) -> Boolean = when (typeStart) {
|
||||||
|
'[', ']' -> ({ start <= it })
|
||||||
|
'(', ')' -> ({ start < it })
|
||||||
|
else -> throw AssertionError()
|
||||||
|
}
|
||||||
|
val b: (SemVersion) -> Boolean = when (typeEnd) {
|
||||||
|
'[', ']' -> ({ it <= end })
|
||||||
|
'(', ')' -> ({ it < end })
|
||||||
|
else -> throw AssertionError()
|
||||||
}
|
}
|
||||||
val compareRange = start..end
|
|
||||||
return object : SemVersion.Requirement {
|
return object : SemVersion.Requirement {
|
||||||
override fun test(version: SemVersion): Boolean = version in compareRange
|
override fun test(version: SemVersion): Boolean = a(version) && b(version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
versionRule.matchEntire(trimmed)?.let { result ->
|
versionRule.matchEntire(trimmed)?.let { result ->
|
||||||
val operator = result.groupValues[1]
|
val operator = result.groupValues[1]
|
||||||
val version1 = SemVersion.invoke(result.groupValues[7])
|
val version1 = SemVersion.invoke(result.groupValues[8])
|
||||||
return when (operator) {
|
return when (operator) {
|
||||||
">=" -> {
|
">=" -> {
|
||||||
object : SemVersion.Requirement {
|
object : SemVersion.Requirement {
|
||||||
@ -137,10 +154,15 @@ internal object SemVersionInternal {
|
|||||||
override fun test(version: SemVersion): Boolean = version.compareTo(version1) == 0
|
override fun test(version: SemVersion): Boolean = version.compareTo(version1) == 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"!=" -> {
|
||||||
|
object : SemVersion.Requirement {
|
||||||
|
override fun test(version: SemVersion): Boolean = version.compareTo(version1) != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> error("operator=$operator, version=$version1")
|
else -> error("operator=$operator, version=$version1")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw IllegalArgumentException("Cannot parse $this")
|
throw IllegalArgumentException("Cannot parse $rule")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SemVersion.Requirement.withRule(rule: String): SemVersion.Requirement {
|
private fun SemVersion.Requirement.withRule(rule: String): SemVersion.Requirement {
|
||||||
@ -155,19 +177,16 @@ internal object SemVersionInternal {
|
|||||||
if (requirement.isBlank()) {
|
if (requirement.isBlank()) {
|
||||||
throw IllegalArgumentException("Invalid requirement: Empty requirement rule.")
|
throw IllegalArgumentException("Invalid requirement: Empty requirement rule.")
|
||||||
}
|
}
|
||||||
return requirement.split("||").map {
|
val tokens = RangeTokenReader.parseToTokens(requirement)
|
||||||
it.parseRule().withRule(it)
|
val collected = RangeTokenReader.collect(requirement, tokens.iterator(), true)
|
||||||
}.let { checks ->
|
RangeTokenReader.check(requirement, collected.iterator(), null)
|
||||||
if (checks.size == 1) return checks[0]
|
return kotlin.runCatching {
|
||||||
object : SemVersion.Requirement {
|
RangeTokenReader.parse(requirement, RangeTokenReader.Token.Group(collected, 0)).withRule(requirement)
|
||||||
override fun test(version: SemVersion): Boolean {
|
}.onFailure { error ->
|
||||||
checks.forEach { rule ->
|
throw IllegalArgumentException("Exception in parsing $requirement\n\n" + buildString {
|
||||||
if (rule.test(version)) return true
|
collected.forEach { dump("", it) }
|
||||||
}
|
}, error)
|
||||||
return false
|
}.getOrThrow()
|
||||||
}
|
|
||||||
}.withRule(requirement)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@ -176,16 +195,12 @@ internal object SemVersionInternal {
|
|||||||
|
|
||||||
// If $this equals $other (without metadata),
|
// If $this equals $other (without metadata),
|
||||||
// return same.
|
// return same.
|
||||||
if (other.mainVersion.contentEquals(source.mainVersion) && source.identifier == other.identifier) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
fun IntArray.getSafe(index: Int) = getOrElse(index) { 0 }
|
|
||||||
|
|
||||||
// Compare main-version
|
// Compare main-version
|
||||||
for (index in 0 until (max(source.mainVersion.size, other.mainVersion.size))) {
|
|
||||||
val result = source.mainVersion.getSafe(index).compareTo(other.mainVersion.getSafe(index))
|
source.major.compareTo(other.major).takeUnless { it == 0 }?.let { return it }
|
||||||
if (result != 0) return result
|
source.minor.compareTo(other.minor).takeUnless { it == 0 }?.let { return it }
|
||||||
}
|
(source.patch ?: 0).compareTo(other.patch ?: 0).takeUnless { it == 0 }?.let { return it }
|
||||||
|
|
||||||
// If main-versions are same.
|
// If main-versions are same.
|
||||||
var identifier0 = source.identifier
|
var identifier0 = source.identifier
|
||||||
var identifier1 = other.identifier
|
var identifier1 = other.identifier
|
@ -16,7 +16,6 @@ import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.*
|
|||||||
import net.mamoe.mirai.console.plugin.description.PluginDependency
|
import net.mamoe.mirai.console.plugin.description.PluginDependency
|
||||||
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||||
import net.mamoe.mirai.console.util.SemVersion
|
import net.mamoe.mirai.console.util.SemVersion
|
||||||
import net.mamoe.mirai.console.util.SemVersionRangeRequirementBuilder
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JVM 插件的描述. 通常作为 `plugin.yml`
|
* JVM 插件的描述. 通常作为 `plugin.yml`
|
||||||
@ -187,31 +186,6 @@ public class JvmPluginDescriptionBuilder(
|
|||||||
this.dependencies.add(PluginDependency(pluginId, null, isOptional))
|
this.dependencies.add(PluginDependency(pluginId, null, isOptional))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 示例:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* dependsOn("org.example.test-plugin") { "1.0.0".."1.2.0" }
|
|
||||||
* dependsOn("org.example.test-plugin") { npmPattern("1.x || >=2.5.0 || 5.0.0 - 7.2.3") }
|
|
||||||
* dependsOn("org.example.test-plugin") { ivyPattern("[1.0,2.0[") }
|
|
||||||
* dependsOn("org.example.test-plugin") { custom { it.toString() == "1.0.0" } }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @see PluginDependency
|
|
||||||
* @see SemVersionRangeRequirementBuilder
|
|
||||||
*/
|
|
||||||
@ILoveKuriyamaMiraiForever
|
|
||||||
public fun dependsOn(
|
|
||||||
@ResolveContext(PLUGIN_ID) pluginId: String,
|
|
||||||
isOptional: Boolean = false,
|
|
||||||
versionRequirement: SemVersionRangeRequirementBuilder.() -> SemVersion.Requirement,
|
|
||||||
): JvmPluginDescriptionBuilder =
|
|
||||||
apply {
|
|
||||||
this.dependencies.add(PluginDependency(pluginId,
|
|
||||||
SemVersionRangeRequirementBuilder.run(versionRequirement),
|
|
||||||
isOptional))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
@Suppress("DEPRECATION_ERROR")
|
||||||
public fun build(): JvmPluginDescription =
|
public fun build(): JvmPluginDescription =
|
||||||
|
@ -23,7 +23,7 @@ import kotlinx.serialization.builtins.serializer
|
|||||||
import net.mamoe.mirai.console.compiler.common.ResolveContext
|
import net.mamoe.mirai.console.compiler.common.ResolveContext
|
||||||
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.PLUGIN_VERSION
|
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.data.map
|
||||||
import net.mamoe.mirai.console.internal.util.SemVersionInternal
|
import net.mamoe.mirai.console.internal.util.semver.SemVersionInternal
|
||||||
import net.mamoe.mirai.console.util.SemVersion.Companion.equals
|
import net.mamoe.mirai.console.util.SemVersion.Companion.equals
|
||||||
import net.mamoe.mirai.console.util.SemVersion.Requirement
|
import net.mamoe.mirai.console.util.SemVersion.Requirement
|
||||||
|
|
||||||
@ -32,10 +32,13 @@ import net.mamoe.mirai.console.util.SemVersion.Requirement
|
|||||||
*
|
*
|
||||||
* 解析示例:
|
* 解析示例:
|
||||||
*
|
*
|
||||||
* `1.0.0-M4+c25733b8` 将会解析出三个内容, mainVersion (核心版本号), [identifier] (先行版本号) 和 [metadata] (元数据).
|
* `1.0.0-M4+c25733b8` 将会解析出下面的内容,
|
||||||
|
* [major] (主本号), [minor] (次版本号), [patch] (修订号), [identifier] (先行版本号) 和 [metadata] (元数据).
|
||||||
* ```
|
* ```
|
||||||
* SemVersion(
|
* SemVersion(
|
||||||
* mainVersion = IntArray [1, 0, 0],
|
* major = 1,
|
||||||
|
* minor = 0,
|
||||||
|
* patch = 0,
|
||||||
* identifier = "M4"
|
* identifier = "M4"
|
||||||
* metadata = "c25733b8"
|
* metadata = "c25733b8"
|
||||||
* )
|
* )
|
||||||
@ -53,8 +56,12 @@ public data class SemVersion
|
|||||||
* @see SemVersion.invoke 字符串解析
|
* @see SemVersion.invoke 字符串解析
|
||||||
*/
|
*/
|
||||||
internal constructor(
|
internal constructor(
|
||||||
/** 核心版本号, 由主版本号, 次版本号和修订号组成, 其中修订号不一定存在 */
|
/** 主版本号 */
|
||||||
public val mainVersion: IntArray,
|
public val major: Int,
|
||||||
|
/** 次版本号 */
|
||||||
|
public val minor: Int,
|
||||||
|
/** 修订号 */
|
||||||
|
public val patch: Int?,
|
||||||
/** 先行版本号识别符 */
|
/** 先行版本号识别符 */
|
||||||
public val identifier: String? = null,
|
public val identifier: String? = null,
|
||||||
/** 版本号元数据, 不参与版本号对比([compareTo]), 但是参与版本号严格对比([equals]) */
|
/** 版本号元数据, 不参与版本号对比([compareTo]), 但是参与版本号严格对比([equals]) */
|
||||||
@ -106,21 +113,28 @@ internal constructor(
|
|||||||
*
|
*
|
||||||
* - `1.0.0-M4` 要求 1.0.0-M4 版本, 且只能是 1.0.0-M4 版本
|
* - `1.0.0-M4` 要求 1.0.0-M4 版本, 且只能是 1.0.0-M4 版本
|
||||||
* - `1.x` 要求 1.x 版本
|
* - `1.x` 要求 1.x 版本
|
||||||
* - `1.0.0 - 1.2.0` 要求 1.0.0 到 1.2.0 的任意版本, 注意 `-` 两边必须要有空格
|
|
||||||
* - `[1.0.0, 1.2.0]` 与 `1.0.0 - 1.2.0` 一致
|
|
||||||
* - `> 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.0.0-RC` 要求 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.0.0-RC 之前的版本, 不能是 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.0.0-RC
|
* - `<= 1.0.0-RC` 要求 1.0.0-RC 或之前的版本, 可以是 1.0.0-RC
|
||||||
|
* - `!= 1.0.0-RC` 要求 除了1.0.0-RC 的任何版本
|
||||||
|
* - `[1.0.0, 1.2.0]`
|
||||||
|
* - `(1.0.0, 1.2.0]`
|
||||||
|
* - `[1.0.0, 1.2.0)`
|
||||||
|
* - `(1.0.0, 1.2.0)` [数学区间](https://baike.baidu.com/item/%E5%8C%BA%E9%97%B4/1273117)
|
||||||
*
|
*
|
||||||
* 对于多个规则, 也允许使用 `||` 拼接.
|
* 对于多个规则, 允许使用逻辑符号 `{}`, `||`, `&&`
|
||||||
* 例如:
|
* 例如:
|
||||||
* - `1.x || 2.x || 3.0.0`
|
* - `1.x || 2.x || 3.0.0`
|
||||||
* - `<= 0.5.3 || >= 1.0.0`
|
* - `<= 0.5.3 || >= 1.0.0`
|
||||||
|
* - `{> 1.0 && < 1.5} || {> 1.8}`
|
||||||
|
* - `{> 1.0 && < 1.5} || {> 1.8}`
|
||||||
|
* - `> 1.0.0 && != 1.2.0`
|
||||||
*
|
*
|
||||||
* 特别注意:
|
* 特别注意:
|
||||||
* - 依赖规则版本号不需要携带版本号元数据, 元数据不参与依赖需求的检查
|
* - 依赖规则版本号不需要携带版本号元数据, 元数据不参与依赖需求的检查
|
||||||
* - 如果目标版本号携带有先行版本号, 请不要忘记先行版本号
|
* - 如果目标版本号携带有先行版本号, 请不要忘记先行版本号
|
||||||
|
* - 因为 `()` 已经用于数学区间, 使用 `{}` 替代 `()`
|
||||||
*/
|
*/
|
||||||
@Throws(IllegalArgumentException::class)
|
@Throws(IllegalArgumentException::class)
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@ -151,14 +165,14 @@ internal constructor(
|
|||||||
@Transient
|
@Transient
|
||||||
private val toString: String by lazy(LazyThreadSafetyMode.NONE) {
|
private val toString: String by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
buildString {
|
buildString {
|
||||||
mainVersion.joinTo(this, ".")
|
append(major)
|
||||||
|
append('.').append(minor)
|
||||||
|
patch?.let { append('.').append(it) }
|
||||||
identifier?.let { identifier ->
|
identifier?.let { identifier ->
|
||||||
append('-')
|
append('-').append(identifier)
|
||||||
append(identifier)
|
|
||||||
}
|
}
|
||||||
metadata?.let { metadata ->
|
metadata?.let { metadata ->
|
||||||
append('+')
|
append('+').append(metadata)
|
||||||
append(metadata)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,7 +183,7 @@ internal constructor(
|
|||||||
* 将 [SemVersion] 转为 Kotlin data class 风格的 [String]
|
* 将 [SemVersion] 转为 Kotlin data class 风格的 [String]
|
||||||
*/
|
*/
|
||||||
public fun toStructuredString(): String {
|
public fun toStructuredString(): String {
|
||||||
return "SemVersion(mainVersion=${mainVersion.contentToString()}, identifier=$identifier, metadata=$metadata)"
|
return "SemVersion(major=$major, minor=$minor, patch=$patch, identifier=$identifier, metadata=$metadata)"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
@ -182,7 +196,8 @@ internal constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = mainVersion.contentHashCode()
|
var result = major shl minor
|
||||||
|
result *= (patch ?: 1)
|
||||||
result = 31 * result + (identifier?.hashCode() ?: 0)
|
result = 31 * result + (identifier?.hashCode() ?: 0)
|
||||||
result = 31 * result + (metadata?.hashCode() ?: 0)
|
result = 31 * result + (metadata?.hashCode() ?: 0)
|
||||||
return result
|
return result
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -48,6 +48,12 @@ internal class TestSemVersion {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun assertInvalid(requirement: String) {
|
||||||
|
kotlin.runCatching {
|
||||||
|
SemVersion.parseRangeRequirement(requirement)
|
||||||
|
}.onSuccess { assert(false) { requirement } }
|
||||||
|
}
|
||||||
|
|
||||||
fun SemVersion.Requirement.assertFalse(version: String): SemVersion.Requirement {
|
fun SemVersion.Requirement.assertFalse(version: String): SemVersion.Requirement {
|
||||||
assert(!test(version)) { version }
|
assert(!test(version)) { version }
|
||||||
return this
|
return this
|
||||||
@ -59,19 +65,24 @@ internal class TestSemVersion {
|
|||||||
.assert("1.0").assert("1.1")
|
.assert("1.0").assert("1.1")
|
||||||
.assert("1.5").assert("1.14514")
|
.assert("1.5").assert("1.14514")
|
||||||
.assertFalse("2.33")
|
.assertFalse("2.33")
|
||||||
|
SemVersion.parseRangeRequirement("2.0||1.2.x")
|
||||||
|
SemVersion.parseRangeRequirement("{2.0||1.2.x} && 1.1.0 &&1.2.3")
|
||||||
SemVersion.parseRangeRequirement("2.0 || 1.2.x")
|
SemVersion.parseRangeRequirement("2.0 || 1.2.x")
|
||||||
.assert("2.0").assert("2.0.0")
|
.assert("2.0").assert("2.0.0")
|
||||||
.assertFalse("2.1")
|
.assertFalse("2.1")
|
||||||
.assert("1.2.5").assert("1.2.0").assertFalse("1.2")
|
.assert("1.2.5").assert("1.2.0").assertFalse("1.2")
|
||||||
.assertFalse("1.0.0")
|
.assertFalse("1.0.0")
|
||||||
SemVersion.parseRangeRequirement("1.0.0 - 114.514.1919")
|
|
||||||
.assert("1.0.0")
|
|
||||||
.assert("114.514").assert("114.514.1919")
|
|
||||||
.assertFalse("0.0.1")
|
|
||||||
.assertFalse("4444.4444")
|
|
||||||
SemVersion.parseRangeRequirement("[1.0.0, 19190.0]")
|
SemVersion.parseRangeRequirement("[1.0.0, 19190.0]")
|
||||||
.assert("1.0.0").assertFalse("0.1.0")
|
.assert("1.0.0").assertFalse("0.1.0")
|
||||||
.assert("19190.0").assertFalse("19198.10")
|
.assert("19190.0").assertFalse("19198.10")
|
||||||
|
SemVersion.parseRangeRequirement("[1.0.0, 2.0.0)")
|
||||||
|
.assert("1.0.0").assert("1.2.3").assertFalse("2.0.0")
|
||||||
|
SemVersion.parseRangeRequirement("(2.0.0, 1.0.0]")
|
||||||
|
.assert("1.0.0").assert("1.2.3").assertFalse("2.0.0")
|
||||||
|
SemVersion.parseRangeRequirement("(2.0.0, 1.0.0)")
|
||||||
|
.assertFalse("1.0.0").assert("1.2.3").assertFalse("2.0.0")
|
||||||
|
SemVersion.parseRangeRequirement("(1.0.0, 2.0.0)")
|
||||||
|
.assertFalse("1.0.0").assert("1.2.3").assertFalse("2.0.0")
|
||||||
SemVersion.parseRangeRequirement(" >= 1.0.0")
|
SemVersion.parseRangeRequirement(" >= 1.0.0")
|
||||||
.assert("1.0.0")
|
.assert("1.0.0")
|
||||||
.assert("114.514.1919")
|
.assert("114.514.1919")
|
||||||
@ -79,9 +90,30 @@ internal class TestSemVersion {
|
|||||||
.assertFalse("0.98774587")
|
.assertFalse("0.98774587")
|
||||||
SemVersion.parseRangeRequirement("> 1.0.0")
|
SemVersion.parseRangeRequirement("> 1.0.0")
|
||||||
.assertFalse("1.0.0")
|
.assertFalse("1.0.0")
|
||||||
kotlin.runCatching { SemVersion.parseRangeRequirement("WPOXAXW") }
|
SemVersion.parseRangeRequirement("!= 1.0.0 && != 2.0.0")
|
||||||
.onSuccess { assert(false) }
|
.assert("1.2.3").assert("2.1.1")
|
||||||
|
.assertFalse("1.0").assertFalse("1.0.0")
|
||||||
|
.assertFalse("2.0").assertFalse("2.0.0")
|
||||||
|
.assert("2.0.1").assert("1.0.1")
|
||||||
|
|
||||||
|
SemVersion.parseRangeRequirement("> 1.0.0 || < 0.9.0")
|
||||||
|
.assertFalse("1.0.0")
|
||||||
|
.assert("0.8.0")
|
||||||
|
.assertFalse("0.9.0")
|
||||||
|
SemVersion.parseRangeRequirement("{>= 1.0.0 && <= 1.2.3} || {>= 2.0.0 && <= 2.2.3}")
|
||||||
|
.assertFalse("1.3.0")
|
||||||
|
.assert("1.0.0").assert("1.2.3")
|
||||||
|
.assertFalse("0.9.0")
|
||||||
|
.assert("2.0.0").assert("2.2.3").assertFalse("2.3.4")
|
||||||
|
|
||||||
|
assertInvalid("WPOXAXW")
|
||||||
|
assertInvalid("1.0.0 || 1.0.0 && 1.0.0")
|
||||||
|
assertInvalid("{")
|
||||||
|
assertInvalid("}")
|
||||||
|
assertInvalid("")
|
||||||
|
assertInvalid("1.2.3 - 3.2.1")
|
||||||
|
assertInvalid("1.5.78 &&")
|
||||||
|
assertInvalid("|| 1.0.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.check() {
|
private fun String.check() {
|
||||||
@ -112,8 +144,9 @@ internal class TestSemVersion {
|
|||||||
"5.1+68-7".check()
|
"5.1+68-7".check()
|
||||||
"5.1+68-".check()
|
"5.1+68-".check()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
internal fun testSemVersionOfficial(){
|
internal fun testSemVersionOfficial() {
|
||||||
"""
|
"""
|
||||||
1.0-RC
|
1.0-RC
|
||||||
0.0.4
|
0.0.4
|
||||||
|
@ -7,6 +7,9 @@ import org.gradle.api.tasks.TaskContainer
|
|||||||
import org.gradle.api.tasks.bundling.Jar
|
import org.gradle.api.tasks.bundling.Jar
|
||||||
import org.gradle.kotlin.dsl.*
|
import org.gradle.kotlin.dsl.*
|
||||||
import upload.Bintray
|
import upload.Bintray
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.security.MessageDigest
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
@ -51,6 +54,44 @@ internal fun org.gradle.api.Project.`publishing`(configure: org.gradle.api.publi
|
|||||||
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("publishing", configure)
|
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("publishing", configure)
|
||||||
|
|
||||||
|
|
||||||
|
fun InputStream.md5(): ByteArray {
|
||||||
|
val digest = MessageDigest.getInstance("md5")
|
||||||
|
digest.reset()
|
||||||
|
use { input ->
|
||||||
|
object : OutputStream() {
|
||||||
|
override fun write(b: Int) {
|
||||||
|
digest.update(b.toByte())
|
||||||
|
}
|
||||||
|
}.use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return digest.digest()
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
|
@JvmOverloads
|
||||||
|
fun ByteArray.toUHexString(
|
||||||
|
separator: String = " ",
|
||||||
|
offset: Int = 0,
|
||||||
|
length: Int = this.size - offset
|
||||||
|
): String {
|
||||||
|
if (length == 0) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
val lastIndex = offset + length
|
||||||
|
return buildString(length * 2) {
|
||||||
|
this@toUHexString.forEachIndexed { index, it ->
|
||||||
|
if (index in offset until lastIndex) {
|
||||||
|
var ret = it.toUByte().toString(16).toUpperCase()
|
||||||
|
if (ret.length == 1) ret = "0$ret"
|
||||||
|
append(ret)
|
||||||
|
if (index < lastIndex - 1) append(separator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline fun Project.setupPublishing(
|
inline fun Project.setupPublishing(
|
||||||
artifactId: String,
|
artifactId: String,
|
||||||
bintrayRepo: String = "mirai",
|
bintrayRepo: String = "mirai",
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
object Versions {
|
object Versions {
|
||||||
const val core = "1.3.0"
|
const val core = "1.3.0"
|
||||||
const val console = "1.0-RC-dev-28"
|
const val console = "1.0-RC-dev-29"
|
||||||
const val consoleGraphical = "0.0.7"
|
const val consoleGraphical = "0.0.7"
|
||||||
const val consoleTerminal = console
|
const val consoleTerminal = console
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ Mirai 鼓励插件开发者将自己的作品开源,并为此提供了模板
|
|||||||
|
|
||||||
### 使用 Gradle 插件配置项目
|
### 使用 Gradle 插件配置项目
|
||||||
|
|
||||||
`VERSION` 可在
|
`VERSION`: [选择版本](#选择版本)
|
||||||
|
|
||||||
若使用 `build.gradle.kts`:
|
若使用 `build.gradle.kts`:
|
||||||
```kotlin
|
```kotlin
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
Mirai Console 可以独立启动,也可以被嵌入到某个应用中。
|
Mirai Console 可以独立启动,也可以被嵌入到某个应用中。
|
||||||
|
|
||||||
## 使用第三方工具自动独立启动
|
## 使用工具自动独立启动
|
||||||
|
|
||||||
https://github.com/LXY1226/MiraiOK
|
官方: https://github.com/iTXTech/mirai-console-loader
|
||||||
|
第三方: https://github.com/LXY1226/MiraiOK
|
||||||
|
|
||||||
## 手动配置独立启动
|
## 手动配置独立启动
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user