Bläddra i källkod

Support ILLEGAL_COMMAND_NAME, ILLEGAL_PERMISSION_NAME, ILLEGAL_PERMISSION_ID, ILLEGAL_PERMISSION_NAMESPACE

Him188 5 år sedan
förälder
incheckning
3fa7c9e128

+ 1 - 0
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/command/Command.kt

@@ -104,6 +104,7 @@ public interface Command {
         /**
          * 检查指令名的合法性. 在非法时抛出 [IllegalArgumentException]
          */
+        @JvmStatic
         @Throws(IllegalArgumentException::class)
         public fun checkCommandName(@ResolveContext(COMMAND_NAME) name: String) {
             when {

+ 6 - 1
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/compiler/common/ResolveContext.kt

@@ -32,6 +32,10 @@ public annotation class ResolveContext(
      * 元素数量可能在任意时间被改动
      */
     public enum class Kind {
+        ///////////////////////////////////////////////////////////////////////////
+        // ConstantKind
+        ///////////////////////////////////////////////////////////////////////////
+
         PLUGIN_ID,
         PLUGIN_NAME,
         PLUGIN_VERSION,
@@ -40,10 +44,11 @@ public annotation class ResolveContext(
 
         PERMISSION_NAMESPACE,
         PERMISSION_NAME,
+        PERMISSION_ID, // for parseFromString
 
         /**
          * Custom serializers allowed
          */
-        RESTRICTED_NO_ARG_CONSTRUCTOR
+        RESTRICTED_NO_ARG_CONSTRUCTOR,
     }
 }

+ 28 - 3
backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/permission/PermissionId.kt

@@ -13,8 +13,7 @@ import kotlinx.serialization.KSerializer
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.builtins.serializer
 import net.mamoe.mirai.console.compiler.common.ResolveContext
-import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.PERMISSION_NAME
-import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.PERMISSION_NAMESPACE
+import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.*
 import net.mamoe.mirai.console.internal.data.map
 
 
@@ -57,13 +56,39 @@ public data class PermissionId(
          * @throws IllegalArgumentException 在解析失败时抛出.
          */
         @JvmStatic
-        public fun parseFromString(string: String): PermissionId {
+        public fun parseFromString(@ResolveContext(PERMISSION_ID) string: String): PermissionId {
             return kotlin.runCatching {
                 string.split(':').let { (namespace, id) -> PermissionId(namespace, id) }
             }.getOrElse {
                 throw IllegalArgumentException("Could not parse PermissionId from '$string'", it)
             }
         }
+
+        /**
+         * 检查 [PermissionId.name] 的合法性. 在非法时抛出 [IllegalArgumentException]
+         */
+        @JvmStatic
+        @Throws(IllegalArgumentException::class)
+        public fun checkPermissionIdName(@ResolveContext(PERMISSION_NAME) value: String) {
+            when {
+                value.isBlank() -> throw IllegalArgumentException("PermissionId.name should not be blank.")
+                value.any { it.isWhitespace() } -> throw IllegalArgumentException("Spaces is not yet allowed in PermissionId.name.")
+                value.contains(':') -> throw IllegalArgumentException("':' is forbidden in PermissionId.name.")
+            }
+        }
+
+        /**
+         * 检查 [PermissionId.namespace] 的合法性. 在非法时抛出 [IllegalArgumentException]
+         */
+        @JvmStatic
+        @Throws(IllegalArgumentException::class)
+        public fun checkPermissionIdNamespace(@ResolveContext(PERMISSION_NAME) value: String) {
+            when {
+                value.isBlank() -> throw IllegalArgumentException("PermissionId.namespace should not be blank.")
+                value.any { it.isWhitespace() } -> throw IllegalArgumentException("Spaces is not yet allowed in PermissionId.namespace.")
+                value.contains(':') -> throw IllegalArgumentException("':' is forbidden in PermissionId.namespace.")
+            }
+        }
     }
 }
 

+ 5 - 0
tools/compiler-common/src/main/java/net/mamoe/mirai/console/compiler/common/diagnostics/MiraiConsoleErrors.java

@@ -11,6 +11,7 @@ package net.mamoe.mirai.console.compiler.common.diagnostics;
 
 import com.intellij.psi.PsiElement;
 import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
+import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2;
 import org.jetbrains.kotlin.diagnostics.Errors;
 
 import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
@@ -19,6 +20,10 @@ public interface MiraiConsoleErrors {
     DiagnosticFactory1<PsiElement, String> ILLEGAL_PLUGIN_DESCRIPTION = DiagnosticFactory1.create(ERROR);
     DiagnosticFactory1<PsiElement, String> NOT_CONSTRUCTABLE_TYPE = DiagnosticFactory1.create(ERROR);
     DiagnosticFactory1<PsiElement, String> UNSERIALIZABLE_TYPE = DiagnosticFactory1.create(ERROR);
+    DiagnosticFactory2<PsiElement, String, String> ILLEGAL_COMMAND_NAME = DiagnosticFactory2.create(ERROR);
+    DiagnosticFactory2<PsiElement, String, String> ILLEGAL_PERMISSION_NAME = DiagnosticFactory2.create(ERROR);
+    DiagnosticFactory2<PsiElement, String, String> ILLEGAL_PERMISSION_ID = DiagnosticFactory2.create(ERROR);
+    DiagnosticFactory2<PsiElement, String, String> ILLEGAL_PERMISSION_NAMESPACE = DiagnosticFactory2.create(ERROR);
 
     @Deprecated
     Object _init = new Object() {

+ 33 - 5
tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/diagnostics/MiraiConsoleErrorsRendering.kt

@@ -19,19 +19,47 @@ object MiraiConsoleErrorsRendering : DefaultErrorMessages.Extension {
         put(
             ILLEGAL_PLUGIN_DESCRIPTION,
             "{0}",
-            Renderers.STRING
+            Renderers.STRING,
         )
 
         put(
             NOT_CONSTRUCTABLE_TYPE,
-            "类型 {0} 无法通过反射直接构造, 需要提供默认值.",
-            Renderers.STRING
+            "类型 ''{0}'' 无法通过反射直接构造, 需要提供默认值.",
+            Renderers.STRING,
         )
 
         put(
             UNSERIALIZABLE_TYPE,
-            "类型 {0} 无法被自动序列化, 需要添加序列化器",
-            Renderers.STRING
+            "类型 ''{0}'' 无法被自动序列化, 需要添加序列化器",
+            Renderers.STRING,
+        )
+
+        put(
+            ILLEGAL_COMMAND_NAME,
+            "指令名 ''{0}'' 无效: {1}",
+            Renderers.STRING,
+            Renderers.STRING,
+        )
+
+        put(
+            ILLEGAL_PERMISSION_NAME,
+            "权限名 ''{0}'' 无效: {1}",
+            Renderers.STRING,
+            Renderers.STRING,
+        )
+
+        put(
+            ILLEGAL_PERMISSION_ID,
+            "权限 Id ''{0}'' 无效: {1}",
+            Renderers.STRING,
+            Renderers.STRING,
+        )
+
+        put(
+            ILLEGAL_PERMISSION_NAMESPACE,
+            "权限命名空间 ''{0}'' 无效: {1}",
+            Renderers.STRING,
+            Renderers.STRING,
         )
     }
 

+ 7 - 1
tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/resolve/MiraiConsoleTypes.kt

@@ -50,11 +50,17 @@ enum class ResolveContextKind {
     PLUGIN_NAME,
     PLUGIN_VERSION,
 
+    COMMAND_NAME,
+
+    PERMISSION_NAMESPACE,
+    PERMISSION_NAME,
+    PERMISSION_ID,
+
     RESTRICTED_NO_ARG_CONSTRUCTOR
     ;
 
     companion object {
-        fun valueOfOrNull(string: String): ResolveContextKind? = ResolveContextKind.values().find { it.name == string }
+        fun valueOfOrNull(string: String): ResolveContextKind? = values().find { it.name == string }
     }
 }
 

+ 1 - 1
tools/intellij-plugin/run/projects/test-project/build.gradle.kts

@@ -22,7 +22,7 @@ dependencies {
     compileOnly(kotlin("stdlib-jdk8"))
 
     val core = "1.3.0"
-    val console = "1.0-RC-dev-3"
+    val console = "1.0-RC-dev-4"
 
     compileOnly("net.mamoe:mirai-console:$console")
     compileOnly("net.mamoe:mirai-core:$core")

+ 7 - 0
tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/MyPluginMain.kt

@@ -3,6 +3,8 @@ package org.example.myplugin
 import kotlinx.serialization.Serializable
 import net.mamoe.mirai.console.data.AutoSavePluginConfig
 import net.mamoe.mirai.console.data.value
+import net.mamoe.mirai.console.permission.PermissionId
+import net.mamoe.mirai.console.permission.PermissionService
 import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
 import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
 
@@ -17,6 +19,11 @@ object MyPluginMain : KotlinPlugin(
         id("")
     }
 ) {
+    override fun onEnable() {
+        super.onEnable()
+        PermissionService.INSTANCE.register(permissionId("dvs"), "ok")
+    }
+
     fun test() {
 
     }

+ 50 - 9
tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/diagnostics/ContextualParametersChecker.kt

@@ -10,7 +10,7 @@
 package net.mamoe.mirai.console.intellij.diagnostics
 
 import com.intellij.psi.PsiElement
-import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors
+import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.*
 import net.mamoe.mirai.console.compiler.common.resolve.ResolveContextKind
 import net.mamoe.mirai.console.compiler.common.resolve.resolveContextKind
 import net.mamoe.mirai.console.intellij.resolve.resolveAllCalls
@@ -31,7 +31,7 @@ class ContextualParametersChecker : DeclarationChecker {
         private val ID_REGEX: Regex = Regex("""([a-zA-Z]+(?:\.[a-zA-Z0-9]+)*)\.([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)""")
         private val FORBIDDEN_ID_NAMES: Array<String> = arrayOf("main", "console", "plugin", "config", "data")
 
-        private const val syntax = """类似于 "net.mamoe.mirai.example-plugin", 其中 "net.mamoe.mirai" 为 groupId, "example-plugin" 为插件名. """
+        private const val syntax = """类似于 "net.mamoe.mirai.example-plugin", 其中 "net.mamoe.mirai" 为 groupId, "example-plugin" 为插件名"""
 
         /**
          * https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
@@ -40,37 +40,74 @@ class ContextualParametersChecker : DeclarationChecker {
             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-]+)*))?${'$'}""")
 
         fun checkPluginId(inspectionTarget: PsiElement, value: String): Diagnostic? {
-            if (value.isBlank()) return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "插件 Id 不能为空. \n插件 Id$syntax")
-            if (value.none { it == '.' }) return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget,
+            if (value.isBlank()) return ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "插件 Id 不能为空. \n插件 Id$syntax")
+            if (value.none { it == '.' }) return ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget,
                 "插件 Id '$value' 无效. 插件 Id 必须同时包含 groupId 和插件名称. $syntax")
 
             val lowercaseId = value.toLowerCase()
 
             if (ID_REGEX.matchEntire(value) == null) {
-                return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "插件 Id 无效. 正确的插件 Id 应该满足正则表达式 '${ID_REGEX.pattern}', \n$syntax")
+                return ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "插件 Id 无效. 正确的插件 Id 应该满足正则表达式 '${ID_REGEX.pattern}', \n$syntax")
             }
 
             FORBIDDEN_ID_NAMES.firstOrNull { it == lowercaseId }?.let { illegal ->
-                return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "'$illegal' 不允许作为插件 Id. 确保插件 Id 不完全是这个名称.")
+                return ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "'$illegal' 不允许作为插件 Id. 确保插件 Id 不完全是这个名称")
             }
             return null
         }
 
         fun checkPluginName(inspectionTarget: PsiElement, value: String): Diagnostic? {
-            if (value.isBlank()) return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "插件名不能为空.")
+            if (value.isBlank()) return ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "插件名不能为空")
             val lowercaseName = value.toLowerCase()
             FORBIDDEN_ID_NAMES.firstOrNull { it == lowercaseName }?.let { illegal ->
-                return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "'$illegal' 不允许作为插件名. 确保插件名不完全是这个名称.")
+                return ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "'$illegal' 不允许作为插件名. 确保插件名不完全是这个名称")
             }
             return null
         }
 
         fun checkPluginVersion(inspectionTarget: PsiElement, value: String): Diagnostic? {
             if (!SEMANTIC_VERSIONING_REGEX.matches(value)) {
-                return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "版本号无效: '$value'. \nhttps://semver.org/lang/zh-CN/")
+                return ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "版本号无效: '$value'. \nhttps://semver.org/lang/zh-CN/")
             }
             return null
         }
+
+        fun checkCommandName(inspectionTarget: PsiElement, value: String): Diagnostic? {
+            return when {
+                value.isBlank() -> ILLEGAL_COMMAND_NAME.on(inspectionTarget, value, "指令名不能为空")
+                value.any { it.isWhitespace() } -> ILLEGAL_COMMAND_NAME.on(inspectionTarget, value, "暂时不允许指令名中存在空格")
+                value.contains(':') -> ILLEGAL_COMMAND_NAME.on(inspectionTarget, value, "指令名不允许包含 ':'")
+                value.contains('.') -> ILLEGAL_COMMAND_NAME.on(inspectionTarget, value, "指令名不允许包含 '.'")
+                else -> null
+            }
+        }
+
+        fun checkPermissionNamespace(inspectionTarget: PsiElement, value: String): Diagnostic? {
+            return when {
+                value.isBlank() -> ILLEGAL_PERMISSION_NAMESPACE.on(inspectionTarget, value, "权限命名空间不能为空")
+                value.any { it.isWhitespace() } -> ILLEGAL_PERMISSION_NAMESPACE.on(inspectionTarget, value, "暂时不允许权限命名空间中存在空格")
+                value.contains(':') -> ILLEGAL_PERMISSION_NAMESPACE.on(inspectionTarget, value, "权限命名空间不允许包含 ':'")
+                else -> null
+            }
+        }
+
+        fun checkPermissionName(inspectionTarget: PsiElement, value: String): Diagnostic? {
+            return when {
+                value.isBlank() -> ILLEGAL_PERMISSION_NAME.on(inspectionTarget, value, "权限名称不能为空")
+                value.any { it.isWhitespace() } -> ILLEGAL_PERMISSION_NAME.on(inspectionTarget, value, "暂时不允许权限名称中存在空格")
+                value.contains(':') -> ILLEGAL_PERMISSION_NAME.on(inspectionTarget, value, "权限名称不允许包含 ':'")
+                else -> null
+            }
+        }
+
+        fun checkPermissionId(inspectionTarget: PsiElement, value: String): Diagnostic? {
+            return when {
+                value.isBlank() -> ILLEGAL_PERMISSION_ID.on(inspectionTarget, value, "权限 Id 不能为空")
+                value.any { it.isWhitespace() } -> ILLEGAL_PERMISSION_ID.on(inspectionTarget, value, "暂时不允许权限 Id 中存在空格")
+                value.count { it == ':' } != 1 -> ILLEGAL_PERMISSION_ID.on(inspectionTarget, value, "权限 Id 必须为 \"命名空间:名称\". 且命名空间和名称均不能包含 ':'")
+                else -> null
+            }
+        }
     }
 
     private val checkersMap: EnumMap<ResolveContextKind, (declaration: PsiElement, value: String) -> Diagnostic?> =
@@ -78,6 +115,10 @@ class ContextualParametersChecker : DeclarationChecker {
             put(ResolveContextKind.PLUGIN_NAME, ::checkPluginName)
             put(ResolveContextKind.PLUGIN_ID, ::checkPluginId)
             put(ResolveContextKind.PLUGIN_VERSION, ::checkPluginVersion)
+            put(ResolveContextKind.COMMAND_NAME, ::checkCommandName)
+            put(ResolveContextKind.PERMISSION_NAME, ::checkPermissionName)
+            put(ResolveContextKind.PERMISSION_NAMESPACE, ::checkPermissionNamespace)
+            put(ResolveContextKind.PERMISSION_ID, ::checkPermissionId)
         }
 
     override fun check(