Merge branch 'master' into command

# Conflicts:
#	backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt
This commit is contained in:
Him188 2020-10-24 21:22:46 +08:00
commit f80c4b3fd1
13 changed files with 71 additions and 36 deletions

View File

@ -40,6 +40,8 @@ public annotation class ResolveContext(
PLUGIN_NAME, // ILLEGAL_PLUGIN_DESCRIPTION PLUGIN_NAME, // ILLEGAL_PLUGIN_DESCRIPTION
PLUGIN_VERSION, // ILLEGAL_PLUGIN_DESCRIPTION PLUGIN_VERSION, // ILLEGAL_PLUGIN_DESCRIPTION
VERSION_REQUIREMENT, // ILLEGAL_VERSION_REQUIREMENT // TODO
COMMAND_NAME, // ILLEGAL_COMMAND_NAME COMMAND_NAME, // ILLEGAL_COMMAND_NAME
PERMISSION_NAMESPACE, // ILLEGAL_COMMAND_NAMESPACE PERMISSION_NAMESPACE, // ILLEGAL_COMMAND_NAMESPACE

View File

@ -182,7 +182,7 @@ internal inline fun AtomicLong.updateWhen(condition: (Long) -> Boolean, update:
while (true) { while (true) {
val current = value val current = value
if (condition(current)) { if (condition(current)) {
if (compareAndSet(0, update(current))) { if (compareAndSet(current, update(current))) {
return true return true
} else continue } else continue
} }

View File

@ -31,10 +31,10 @@ public data class PermissionId(
@ResolveContext(PERMISSION_NAME) public val name: String, @ResolveContext(PERMISSION_NAME) public val name: String,
) { ) {
init { init {
require(!namespace.contains(' ')) { require(namespace.none { it.isWhitespace() }) {
"' ' is not allowed in namespace" "' ' is not allowed in namespace"
} }
require(!name.contains(' ')) { require(name.none { it.isWhitespace() }) {
"' ' is not allowed in name" "' ' is not allowed in name"
} }

View File

@ -17,6 +17,7 @@ import net.mamoe.mirai.console.extensions.PermissionServiceProvider
import net.mamoe.mirai.console.internal.permission.checkType import net.mamoe.mirai.console.internal.permission.checkType
import net.mamoe.mirai.console.permission.Permission.Companion.parentsWithSelf import net.mamoe.mirai.console.permission.Permission.Companion.parentsWithSelf
import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.plugin.description
import net.mamoe.mirai.console.plugin.name import net.mamoe.mirai.console.plugin.name
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -154,7 +155,7 @@ public interface PermissionService<P : Permission> {
@ResolveContext(COMMAND_NAME) permissionName: String, @ResolveContext(COMMAND_NAME) permissionName: String,
reason: PluginPermissionIdRequestType reason: PluginPermissionIdRequestType
) = PermissionId( ) = PermissionId(
plugin.name.toLowerCase().replace(' ', '.'), plugin.description.id.toLowerCase(),
permissionName.toLowerCase() permissionName.toLowerCase()
) )

View File

@ -35,7 +35,7 @@ public interface JvmPluginDescription : PluginDescription {
*/ */
@JvmName("create") @JvmName("create")
@JvmSynthetic @JvmSynthetic
public operator fun invoke( public inline operator fun invoke(
/** /**
* @see [PluginDescription.id] * @see [PluginDescription.id]
*/ */
@ -57,7 +57,7 @@ public interface JvmPluginDescription : PluginDescription {
*/ */
@JvmName("create") @JvmName("create")
@JvmSynthetic @JvmSynthetic
public operator fun invoke( public inline operator fun invoke(
/** /**
* @see [PluginDescription.id] * @see [PluginDescription.id]
*/ */
@ -65,7 +65,7 @@ public interface JvmPluginDescription : PluginDescription {
/** /**
* @see [PluginDescription.version] * @see [PluginDescription.version]
*/ */
@ResolveContext(PLUGIN_VERSION) version: SemVersion, version: SemVersion,
/** /**
* @see [PluginDescription.name] * @see [PluginDescription.name]
*/ */
@ -87,17 +87,17 @@ public interface JvmPluginDescription : PluginDescription {
* ``` * ```
* *
* #### Java Example * #### Java Example
* ``` * ```java
* JvmPluginDescription desc = new JvmPluginDescriptionBuilder("org.example.example-plugin", "1.0.0") * JvmPluginDescription desc = new JvmPluginDescriptionBuilder("org.example.example-plugin", "1.0.0")
* .info("This is an example plugin") * .info("This is an example plugin")
* .dependsOn("org.example.another-plugin") * .dependsOn("org.example.another-plugin")
* .build() * .build();
* ``` * ```
* *
* @see [JvmPluginDescription.invoke] * @see [JvmPluginDescription.invoke]
*/ */
public class JvmPluginDescriptionBuilder( public class JvmPluginDescriptionBuilder(
private var id: String, @ResolveContext(PLUGIN_ID) private var id: String,
private var version: SemVersion, private var version: SemVersion,
) { ) {
public constructor( public constructor(
@ -119,7 +119,7 @@ public class JvmPluginDescriptionBuilder(
apply { this.version = SemVersion(value) } apply { this.version = SemVersion(value) }
@ILoveKuriyamaMiraiForever @ILoveKuriyamaMiraiForever
public fun version(@ResolveContext(PLUGIN_VERSION) value: SemVersion): JvmPluginDescriptionBuilder = public fun version(value: SemVersion): JvmPluginDescriptionBuilder =
apply { this.version = value } apply { this.version = value }
@ILoveKuriyamaMiraiForever @ILoveKuriyamaMiraiForever
@ -148,18 +148,6 @@ public class JvmPluginDescriptionBuilder(
} }
} }
/**
* @see PluginDependency
*/
@ILoveKuriyamaMiraiForever
public fun dependsOn(
@ResolveContext(PLUGIN_ID) pluginId: String,
isOptional: Boolean = false,
versionRequirement: SemVersion.Requirement,
): JvmPluginDescriptionBuilder = apply {
this.dependencies.add(PluginDependency(pluginId, versionRequirement, isOptional))
}
/** /**
* isOptional = false * isOptional = false
* *
@ -169,13 +157,28 @@ public class JvmPluginDescriptionBuilder(
public fun dependsOn( public fun dependsOn(
@ResolveContext(PLUGIN_ID) pluginId: String, @ResolveContext(PLUGIN_ID) pluginId: String,
versionRequirement: SemVersion.Requirement, versionRequirement: SemVersion.Requirement,
isOptional: Boolean = false,
): JvmPluginDescriptionBuilder = apply { ): JvmPluginDescriptionBuilder = apply {
this.dependencies.add(PluginDependency(pluginId, versionRequirement, false)) this.dependencies.add(PluginDependency(pluginId, versionRequirement, isOptional))
}
/**
* @see PluginDependency
*/
@ILoveKuriyamaMiraiForever
public fun dependsOn(
@ResolveContext(PLUGIN_ID) pluginId: String,
@ResolveContext(VERSION_REQUIREMENT) versionRequirement: String,
isOptional: Boolean = false,
): JvmPluginDescriptionBuilder = apply {
this.dependencies.add(PluginDependency(pluginId, SemVersion.parseRangeRequirement(versionRequirement), isOptional))
} }
/** /**
* 无版本要求 * 无版本要求
* *
* @param isOptional [PluginDependency.isOptional]
*
* @see PluginDependency * @see PluginDependency
*/ */
@ILoveKuriyamaMiraiForever @ILoveKuriyamaMiraiForever
@ -187,8 +190,8 @@ public class JvmPluginDescriptionBuilder(
} }
@Suppress("DEPRECATION_ERROR")
public fun build(): JvmPluginDescription = public fun build(): JvmPluginDescription =
@Suppress("DEPRECATION_ERROR")
SimpleJvmPluginDescription(name, version, id, author, info, dependencies) SimpleJvmPluginDescription(name, version, id, author, info, dependencies)
/** /**
@ -227,6 +230,6 @@ internal data class SimpleJvmPluginDescription
) : this(name, SemVersion(version), id, author, info, dependencies) ) : this(name, SemVersion(version), id, author, info, dependencies)
init { init {
require(!name.contains(':')) { "':' is forbidden in plugin name" } PluginDescription.checkPluginDescription(this)
} }
} }

View File

@ -22,10 +22,12 @@ import kotlinx.serialization.Transient
import kotlinx.serialization.builtins.serializer 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.compiler.common.ResolveContext.Kind.VERSION_REQUIREMENT
import net.mamoe.mirai.console.internal.data.map import net.mamoe.mirai.console.internal.data.map
import net.mamoe.mirai.console.internal.util.semver.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
import kotlin.LazyThreadSafetyMode.PUBLICATION
/** /**
* [语义化版本](https://semver.org/lang/zh-CN/) 支持 * [语义化版本](https://semver.org/lang/zh-CN/) 支持
@ -138,7 +140,7 @@ internal constructor(
*/ */
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
@JvmStatic @JvmStatic
public fun parseRangeRequirement(requirement: String): Requirement = public fun parseRangeRequirement(@ResolveContext(VERSION_REQUIREMENT) requirement: String): Requirement =
SemVersionInternal.parseRangeRequirement(requirement) SemVersionInternal.parseRangeRequirement(requirement)
/** @see [Requirement.test] */ /** @see [Requirement.test] */
@ -151,6 +153,12 @@ internal constructor(
@JvmStatic @JvmStatic
public fun SemVersion.satisfies(requirement: Requirement): Boolean = requirement.test(this) public fun SemVersion.satisfies(requirement: Requirement): Boolean = requirement.test(this)
/**
* 当满足 [requirement] 时返回 true, 否则返回 false
*/
@JvmStatic
public fun SemVersion.satisfies(@ResolveContext(VERSION_REQUIREMENT) requirement: String): Boolean = parseRangeRequirement(requirement).test(this)
/** for Kotlin only */ /** for Kotlin only */
@JvmStatic @JvmStatic
@JvmSynthetic @JvmSynthetic
@ -163,7 +171,7 @@ internal constructor(
} }
@Transient @Transient
private val toString: String by lazy(LazyThreadSafetyMode.NONE) { private val toString: String by lazy(PUBLICATION) {
buildString { buildString {
append(major) append(major)
append('.').append(minor) append('.').append(minor)

View File

@ -1,8 +1,18 @@
package net.mamoe.mirai.console.permission package net.mamoe.mirai.console.permission
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import kotlin.test.assertFails
internal class PermissionsBasicsTest { internal class PermissionsBasicsTest {
@Test
fun testInvalidPermissionId() {
assertFails { PermissionId("space namespace", "name") }
assertFails { PermissionId("namespace", "space name") }
// assertFails { PermissionId("", "name") }
// assertFails { PermissionId("namespace", "") }
assertFails { PermissionId("namespace:name", "name") }
assertFails { PermissionId("namespace", "namespace:name") }
}
@Test @Test
fun parentsWithSelfSequence() { fun parentsWithSelfSequence() {

View File

@ -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-30" const val console = "1.0-RC-dev-31"
const val consoleGraphical = "0.0.7" const val consoleGraphical = "0.0.7"
const val consoleTerminal = console const val consoleTerminal = console

View File

@ -57,6 +57,8 @@ enum class ResolveContextKind {
PLUGIN_NAME, PLUGIN_NAME,
PLUGIN_VERSION, PLUGIN_VERSION,
VERSION_REQUIREMENT,
COMMAND_NAME, COMMAND_NAME,
PERMISSION_NAMESPACE, PERMISSION_NAMESPACE,

View File

@ -1,7 +1,7 @@
plugins { plugins {
kotlin("jvm") version "1.4.0" kotlin("jvm") version "1.4.10"
kotlin("plugin.serialization") version "1.4.0" kotlin("plugin.serialization") version "1.4.10"
kotlin("kapt") version "1.4.0" kotlin("kapt") version "1.4.10"
id("com.github.johnrengelman.shadow") version "5.2.0" id("com.github.johnrengelman.shadow") version "5.2.0"
} }
@ -33,7 +33,7 @@ dependencies {
testImplementation("net.mamoe:mirai-console:$console") testImplementation("net.mamoe:mirai-console:$console")
testImplementation("net.mamoe:mirai-core:$core") testImplementation("net.mamoe:mirai-core:$core")
testImplementation("net.mamoe:mirai-console-pure:$console") testImplementation("net.mamoe:mirai-console-terminal:$console")
testImplementation(kotlin("stdlib-jdk8")) testImplementation(kotlin("stdlib-jdk8"))
} }

View File

@ -21,6 +21,8 @@ object MyPluginMain : KotlinPlugin(
override fun onEnable() { override fun onEnable() {
super.onEnable() super.onEnable()
PermissionService.INSTANCE.register(permissionId("dvs"), "ok") PermissionService.INSTANCE.register(permissionId("dvs"), "ok")
PermissionService.INSTANCE.register(permissionId("perm with space"), "error")
PermissionId("Namespace with space", "Name with space")
} }
fun test() { fun test() {

View File

@ -85,7 +85,7 @@ class ContextualParametersChecker : DeclarationChecker {
fun checkPermissionNamespace(inspectionTarget: PsiElement, value: String): Diagnostic? { fun checkPermissionNamespace(inspectionTarget: PsiElement, value: String): Diagnostic? {
return when { return when {
value.isBlank() -> ILLEGAL_PERMISSION_NAMESPACE.on(inspectionTarget, value, "权限命名空间不能为空") value.isBlank() -> ILLEGAL_PERMISSION_NAMESPACE.on(inspectionTarget, value, "权限命名空间不能为空")
value.any { it.isWhitespace() } -> ILLEGAL_PERMISSION_NAMESPACE.on(inspectionTarget, value, "暂时不允许权限命名空间中存在空格") value.any { it.isWhitespace() } -> ILLEGAL_PERMISSION_NAMESPACE.on(inspectionTarget, value, "不允许权限命名空间中存在空格")
value.contains(':') -> ILLEGAL_PERMISSION_NAMESPACE.on(inspectionTarget, value, "权限命名空间不允许包含 ':'") value.contains(':') -> ILLEGAL_PERMISSION_NAMESPACE.on(inspectionTarget, value, "权限命名空间不允许包含 ':'")
else -> null else -> null
} }
@ -94,7 +94,7 @@ class ContextualParametersChecker : DeclarationChecker {
fun checkPermissionName(inspectionTarget: PsiElement, value: String): Diagnostic? { fun checkPermissionName(inspectionTarget: PsiElement, value: String): Diagnostic? {
return when { return when {
value.isBlank() -> ILLEGAL_PERMISSION_NAME.on(inspectionTarget, value, "权限名称不能为空") value.isBlank() -> ILLEGAL_PERMISSION_NAME.on(inspectionTarget, value, "权限名称不能为空")
value.any { it.isWhitespace() } -> ILLEGAL_PERMISSION_NAME.on(inspectionTarget, value, "暂时不允许权限名称中存在空格") value.any { it.isWhitespace() } -> ILLEGAL_PERMISSION_NAME.on(inspectionTarget, value, "不允许权限名称中存在空格")
value.contains(':') -> ILLEGAL_PERMISSION_NAME.on(inspectionTarget, value, "权限名称不允许包含 ':'") value.contains(':') -> ILLEGAL_PERMISSION_NAME.on(inspectionTarget, value, "权限名称不允许包含 ':'")
else -> null else -> null
} }
@ -108,6 +108,12 @@ class ContextualParametersChecker : DeclarationChecker {
else -> null else -> null
} }
} }
@Suppress("UNUSED_PARAMETER")
fun checkVersionRequirement(inspectionTarget: PsiElement, value: String): Diagnostic? {
// TODO: 2020/10/23 checkVersionRequirement
return null
}
} }
private val checkersMap: EnumMap<ResolveContextKind, (declaration: PsiElement, value: String) -> Diagnostic?> = private val checkersMap: EnumMap<ResolveContextKind, (declaration: PsiElement, value: String) -> Diagnostic?> =
@ -119,6 +125,7 @@ class ContextualParametersChecker : DeclarationChecker {
put(ResolveContextKind.PERMISSION_NAME, ::checkPermissionName) put(ResolveContextKind.PERMISSION_NAME, ::checkPermissionName)
put(ResolveContextKind.PERMISSION_NAMESPACE, ::checkPermissionNamespace) put(ResolveContextKind.PERMISSION_NAMESPACE, ::checkPermissionNamespace)
put(ResolveContextKind.PERMISSION_ID, ::checkPermissionId) put(ResolveContextKind.PERMISSION_ID, ::checkPermissionId)
put(ResolveContextKind.VERSION_REQUIREMENT, ::checkVersionRequirement)
} }
override fun check( override fun check(

View File

@ -25,7 +25,7 @@ class AddSerializerFix(
element: KtClassOrObject, element: KtClassOrObject,
) : KotlinCrossLanguageQuickFixAction<KtModifierListOwner>(element), KotlinUniversalQuickFix { ) : KotlinCrossLanguageQuickFixAction<KtModifierListOwner>(element), KotlinUniversalQuickFix {
override fun getFamilyName(): String = "添加注解" override fun getFamilyName(): String = "Mirai Console"
override fun getText(): String = "添加 @Serializable" override fun getText(): String = "添加 @Serializable"
override fun invokeImpl(project: Project, editor: Editor?, file: PsiFile) { override fun invokeImpl(project: Project, editor: Editor?, file: PsiFile) {