feat: wildcard excludeの実装
This commit is contained in:
parent
2275cd9993
commit
660f9a3436
23
README.md
23
README.md
|
|
@ -18,7 +18,9 @@ class ExamplePlugin : JavaPlugin() {
|
||||||
node("command", NodeRegistration.STRUCTURAL) {
|
node("command", NodeRegistration.STRUCTURAL) {
|
||||||
description = "Access to all example commands"
|
description = "Access to all example commands"
|
||||||
defaultValue = PermissionDefault.OP
|
defaultValue = PermissionDefault.OP
|
||||||
wildcard = true // create example.command.*
|
wildcard {
|
||||||
|
exclude("cooldown") // example.command.* will skip cooldown
|
||||||
|
}
|
||||||
|
|
||||||
node("reload", NodeRegistration.PERMISSION) {
|
node("reload", NodeRegistration.PERMISSION) {
|
||||||
description = "Allows /example reload (permission example.command.reload)"
|
description = "Allows /example reload (permission example.command.reload)"
|
||||||
|
|
@ -32,7 +34,6 @@ class ExamplePlugin : JavaPlugin() {
|
||||||
|
|
||||||
node("cooldown", NodeRegistration.PERMISSION) {
|
node("cooldown", NodeRegistration.PERMISSION) {
|
||||||
description = "Allows /example cooldown tweaks"
|
description = "Allows /example cooldown tweaks"
|
||||||
wildcard = false // keep cooldown out of example.command.*
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +51,7 @@ class ExamplePlugin : JavaPlugin() {
|
||||||
// The tree above materializes as permissions such as:
|
// The tree above materializes as permissions such as:
|
||||||
// example.command, example.command.reload, example.command.helper, example.command.cooldown,
|
// example.command, example.command.reload, example.command.helper, example.command.cooldown,
|
||||||
// example.tools.repair,
|
// example.tools.repair,
|
||||||
// plus the auto-generated example.command.* wildcard (command opted in while cooldown did not).
|
// plus the auto-generated example.command.* wildcard (command opted in, cooldown was excluded).
|
||||||
// export to plugin.yml or inspect Bukkit's /permissions output).
|
// export to plugin.yml or inspect Bukkit's /permissions output).
|
||||||
|
|
||||||
configureRuntimePermissions()
|
configureRuntimePermissions()
|
||||||
|
|
@ -96,12 +97,12 @@ val baseTree = permissionTree("example") {
|
||||||
val mutable = MutablePermissionTree.from(baseTree)
|
val mutable = MutablePermissionTree.from(baseTree)
|
||||||
mutable.node("command", NodeRegistration.STRUCTURAL) {
|
mutable.node("command", NodeRegistration.STRUCTURAL) {
|
||||||
wildcard = true
|
wildcard = true
|
||||||
|
excludeWildcardChild("helper") // keep helper out of command.*
|
||||||
node("debug", NodeRegistration.PERMISSION) {
|
node("debug", NodeRegistration.PERMISSION) {
|
||||||
description = "Allows /example debug"
|
description = "Allows /example debug"
|
||||||
defaultValue = PermissionDefault.OP
|
defaultValue = PermissionDefault.OP
|
||||||
wildcard = true
|
wildcard = true
|
||||||
}
|
}
|
||||||
child("helper", value = false) // unlink helper if present
|
|
||||||
}
|
}
|
||||||
mutable.removeNode("command.legacy")
|
mutable.removeNode("command.legacy")
|
||||||
|
|
||||||
|
|
@ -130,9 +131,17 @@ stage edits procedurally before ever touching `MutationSession`.
|
||||||
other namespaces.
|
other namespaces.
|
||||||
- **PermissionRegistry** – calculates a diff between snapshots and performs the minimum additions,
|
- **PermissionRegistry** – calculates a diff between snapshots and performs the minimum additions,
|
||||||
removals, or updates via Bukkit's `PluginManager`.
|
removals, or updates via Bukkit's `PluginManager`.
|
||||||
- **Wildcards** – disabled by default; opt in by setting `wildcard = true` on any permission you want pulled
|
- **Wildcards** – disabled by default; opt in via `wildcard = true` or the richer `wildcard { ... }` block.
|
||||||
into its parent `namespace.command.*`. Enabled nodes automatically add their wildcard descendants (e.g.,
|
The block automatically enables the wildcard and lets you `exclude("sub.path")` so only selected DSL
|
||||||
`example.command.debug.*`) so granting a parent wildcard cascades through the tree.
|
children end up under `namespace.command.*`. Enabled nodes automatically add their wildcard descendants
|
||||||
|
(e.g., `example.command.debug.*`) so granting the wildcard cascades to the remaining children.
|
||||||
|
|
||||||
|
### Selective wildcards
|
||||||
|
|
||||||
|
- **DSL** – call `wildcard { exclude("cooldown") }` to enable the `*. *` permission while skipping specific
|
||||||
|
literal/argument branches. You can chain `exclude` calls and pass multi-segment paths (`exclude("debug.logs")`).
|
||||||
|
- **Mutable tree** – after `wildcard = true`, invoke `excludeWildcardChild("helper")` (relative) or
|
||||||
|
`excludeWildcardChildAbsolute("example.command.helper.extras")` to trim wildcard membership imperatively.
|
||||||
- **Mutable edits** – `permits.edit { ... }` clones the currently registered tree, lets you mutate nodes
|
- **Mutable edits** – `permits.edit { ... }` clones the currently registered tree, lets you mutate nodes
|
||||||
imperatively, re-validates, and only pushes the structural diff to Bukkit.
|
imperatively, re-validates, and only pushes the structural diff to Bukkit.
|
||||||
- **AttachmentSynchronizer** – manages identity-based `PermissionAttachment`s and exposes high-level
|
- **AttachmentSynchronizer** – manages identity-based `PermissionAttachment`s and exposes high-level
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,18 @@ class MutablePermissionTree internal constructor(
|
||||||
val newPermissionId = PermissionId.of("${this.id.value}.${newId.lowercase()}")
|
val newPermissionId = PermissionId.of("${this.id.value}.${newId.lowercase()}")
|
||||||
renameSubtree(oldPermissionId, newPermissionId)
|
renameSubtree(oldPermissionId, newPermissionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun excludeWildcardChild(id: String) {
|
||||||
|
require(id.isNotBlank()) { "Wildcard exclusion id must not be blank." }
|
||||||
|
val permissionId = PermissionId.of("${this.id.value}.${id.lowercase()}")
|
||||||
|
draft.wildcardExclusions.add(permissionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun excludeWildcardChildAbsolute(id: String) {
|
||||||
|
require(id.isNotBlank()) { "Wildcard exclusion id must not be blank." }
|
||||||
|
val permissionId = PermissionId.of(id.lowercase())
|
||||||
|
draft.wildcardExclusions.add(permissionId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeSubtree(rootId: PermissionId) {
|
private fun removeSubtree(rootId: PermissionId) {
|
||||||
|
|
@ -150,7 +162,8 @@ class MutablePermissionTree internal constructor(
|
||||||
defaultValue = draft.defaultValue,
|
defaultValue = draft.defaultValue,
|
||||||
children = draft.children.toMutableMap(),
|
children = draft.children.toMutableMap(),
|
||||||
wildcard = draft.wildcard,
|
wildcard = draft.wildcard,
|
||||||
registration = draft.registration
|
registration = draft.registration,
|
||||||
|
wildcardExclusions = draft.wildcardExclusions.toMutableSet()
|
||||||
)
|
)
|
||||||
drafts[newId] = newDraft
|
drafts[newId] = newDraft
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ data class PermissionNode(
|
||||||
val defaultValue: PermissionDefault = PermissionDefault.FALSE,
|
val defaultValue: PermissionDefault = PermissionDefault.FALSE,
|
||||||
val children: Map<PermissionId, Boolean> = emptyMap(),
|
val children: Map<PermissionId, Boolean> = emptyMap(),
|
||||||
val wildcard: Boolean = false,
|
val wildcard: Boolean = false,
|
||||||
val registration: NodeRegistration = NodeRegistration.PERMISSION
|
val registration: NodeRegistration = NodeRegistration.PERMISSION,
|
||||||
|
val wildcardExclusions: Set<PermissionId> = emptySet()
|
||||||
) {
|
) {
|
||||||
init {
|
init {
|
||||||
require(children.keys.none { it == id }) { "Permission node cannot be a child of itself." }
|
require(children.keys.none { it == id }) { "Permission node cannot be a child of itself." }
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ internal data class PermissionNodeDraft(
|
||||||
var defaultValue: PermissionDefault = PermissionDefault.FALSE,
|
var defaultValue: PermissionDefault = PermissionDefault.FALSE,
|
||||||
val children: MutableMap<PermissionId, Boolean> = linkedMapOf(),
|
val children: MutableMap<PermissionId, Boolean> = linkedMapOf(),
|
||||||
var wildcard: Boolean = false,
|
var wildcard: Boolean = false,
|
||||||
var registration: NodeRegistration = NodeRegistration.PERMISSION
|
var registration: NodeRegistration = NodeRegistration.PERMISSION,
|
||||||
|
val wildcardExclusions: MutableSet<PermissionId> = linkedSetOf()
|
||||||
) {
|
) {
|
||||||
fun toNode(): PermissionNode =
|
fun toNode(): PermissionNode =
|
||||||
PermissionNode(
|
PermissionNode(
|
||||||
|
|
@ -17,7 +18,8 @@ internal data class PermissionNodeDraft(
|
||||||
defaultValue = defaultValue,
|
defaultValue = defaultValue,
|
||||||
children = children.toMap(),
|
children = children.toMap(),
|
||||||
wildcard = wildcard,
|
wildcard = wildcard,
|
||||||
registration = registration
|
registration = registration,
|
||||||
|
wildcardExclusions = wildcardExclusions.toSet()
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
@ -28,7 +30,8 @@ internal data class PermissionNodeDraft(
|
||||||
defaultValue = node.defaultValue,
|
defaultValue = node.defaultValue,
|
||||||
children = node.children.toMutableMap(),
|
children = node.children.toMutableMap(),
|
||||||
wildcard = node.wildcard,
|
wildcard = node.wildcard,
|
||||||
registration = node.registration
|
registration = node.registration,
|
||||||
|
wildcardExclusions = node.wildcardExclusions.toMutableSet()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ internal object WildcardAugmentor {
|
||||||
if (node.id.value.endsWith(".*")) return@forEach
|
if (node.id.value.endsWith(".*")) return@forEach
|
||||||
|
|
||||||
val wildcardId = PermissionId.of("${node.id.value}.*")
|
val wildcardId = PermissionId.of("${node.id.value}.*")
|
||||||
val updatedChildren = node.children.toMutableMap()
|
val updatedChildren = node.children
|
||||||
|
.filterKeys { childId -> childId !in node.wildcardExclusions }
|
||||||
|
.toMutableMap()
|
||||||
|
|
||||||
val existing = result[wildcardId]
|
val existing = result[wildcardId]
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,11 @@ class PermissionNodeBuilder internal constructor(
|
||||||
draft.wildcard = value
|
draft.wildcard = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun wildcard(block: WildcardDsl.() -> Unit) {
|
||||||
|
wildcard = true
|
||||||
|
WildcardDsl(draft).apply(block)
|
||||||
|
}
|
||||||
|
|
||||||
var registration: NodeRegistration
|
var registration: NodeRegistration
|
||||||
get() = draft.registration
|
get() = draft.registration
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|
@ -66,3 +71,18 @@ class PermissionNodeBuilder internal constructor(
|
||||||
treeBuilder.nestedNode(draft, id, registration, block)
|
treeBuilder.nestedNode(draft, id, registration, block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WildcardDsl internal constructor(
|
||||||
|
private val draft: PermissionNodeDraft
|
||||||
|
) {
|
||||||
|
fun exclude(vararg segments: String) {
|
||||||
|
val normalized = segments
|
||||||
|
.flatMap { it.split('.') }
|
||||||
|
.map { it.trim().lowercase() }
|
||||||
|
.filter { it.isNotEmpty() }
|
||||||
|
if (normalized.isEmpty()) return
|
||||||
|
val suffix = normalized.joinToString(".")
|
||||||
|
val permissionId = PermissionId.of("${draft.id.value}.$suffix")
|
||||||
|
draft.wildcardExclusions.add(permissionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user