chore: fmt/lint

This commit is contained in:
Keisuke Hirata 2026-03-03 22:18:42 +09:00
parent 72312a45e0
commit 7eb0534d21
17 changed files with 159 additions and 103 deletions

View File

@ -1,5 +1,3 @@
import net.minecrell.pluginyml.paper.PaperPluginDescription
group = "net.hareworks" group = "net.hareworks"
version = "1.1" version = "1.1"
@ -33,7 +31,8 @@ paper {
description = "Permission Library" description = "Permission Library"
version = getVersion().toString() version = getVersion().toString()
apiVersion = "1.21.10" apiVersion = "1.21.10"
authors = listOf( authors =
"Hare-K02" listOf(
) "Hare-K02",
)
} }

View File

@ -3,4 +3,4 @@ package net.hareworks.permits_lib.plugin
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
@Suppress("unused") @Suppress("unused")
class Plugin : JavaPlugin() {} class Plugin : JavaPlugin()

View File

@ -7,7 +7,7 @@ import net.hareworks.permits_lib.domain.PermissionId
* `true`/`false` represent forced grant/deny, while `null` removes the override. * `true`/`false` represent forced grant/deny, while `null` removes the override.
*/ */
data class AttachmentPatch( data class AttachmentPatch(
val changes: Map<PermissionId, Boolean?> val changes: Map<PermissionId, Boolean?>,
) { ) {
companion object { companion object {
val EMPTY = AttachmentPatch(emptyMap()) val EMPTY = AttachmentPatch(emptyMap())

View File

@ -1,27 +1,30 @@
package net.hareworks.permits_lib.bukkit package net.hareworks.permits_lib.bukkit
import java.util.IdentityHashMap
import net.hareworks.permits_lib.domain.PermissionId import net.hareworks.permits_lib.domain.PermissionId
import net.hareworks.permits_lib.util.ThreadChecks import net.hareworks.permits_lib.util.ThreadChecks
import org.bukkit.permissions.PermissionAttachment
import org.bukkit.permissions.Permissible import org.bukkit.permissions.Permissible
import org.bukkit.permissions.PermissionAttachment
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
import java.util.IdentityHashMap
/** /**
* Manages [PermissionAttachment] instances per [Permissible], applying patches and cleaning up once no * Manages [PermissionAttachment] instances per [Permissible], applying patches and cleaning up once no
* overrides remain. * overrides remain.
*/ */
class AttachmentSynchronizer( class AttachmentSynchronizer(
private val plugin: JavaPlugin private val plugin: JavaPlugin,
) { ) {
private data class AttachmentHandle( private data class AttachmentHandle(
val attachment: PermissionAttachment, val attachment: PermissionAttachment,
val overrides: MutableMap<PermissionId, Boolean> = linkedMapOf() val overrides: MutableMap<PermissionId, Boolean> = linkedMapOf(),
) )
private val handles = IdentityHashMap<Permissible, AttachmentHandle>() private val handles = IdentityHashMap<Permissible, AttachmentHandle>()
fun applyPatch(permissible: Permissible, patch: AttachmentPatch) { fun applyPatch(
permissible: Permissible,
patch: AttachmentPatch,
) {
ThreadChecks.ensurePrimaryThread("AttachmentSynchronizer.applyPatch") ThreadChecks.ensurePrimaryThread("AttachmentSynchronizer.applyPatch")
if (patch.changes.isEmpty()) return if (patch.changes.isEmpty()) return
val handle = ensureHandle(permissible) val handle = ensureHandle(permissible)
@ -39,11 +42,18 @@ class AttachmentSynchronizer(
} }
} }
fun grant(permissible: Permissible, permission: PermissionId, value: Boolean = true) { fun grant(
permissible: Permissible,
permission: PermissionId,
value: Boolean = true,
) {
applyPatch(permissible, AttachmentPatch(mapOf(permission to value))) applyPatch(permissible, AttachmentPatch(mapOf(permission to value)))
} }
fun revoke(permissible: Permissible, permission: PermissionId) { fun revoke(
permissible: Permissible,
permission: PermissionId,
) {
applyPatch(permissible, AttachmentPatch(mapOf(permission to null))) applyPatch(permissible, AttachmentPatch(mapOf(permission to null)))
} }

View File

@ -9,7 +9,7 @@ import net.hareworks.permits_lib.domain.TreeDiff
*/ */
class MutationSession( class MutationSession(
private val registry: PermissionRegistry, private val registry: PermissionRegistry,
val attachments: AttachmentSynchronizer val attachments: AttachmentSynchronizer,
) { ) {
private var tree: PermissionTree? = null private var tree: PermissionTree? = null
private var diff: TreeDiff? = null private var diff: TreeDiff? = null
@ -34,19 +34,23 @@ class MutationSession(
* Mutates the existing tree or creates a fresh one for the provided [namespace] when none was applied * Mutates the existing tree or creates a fresh one for the provided [namespace] when none was applied
* before. * before.
*/ */
fun edit(namespace: String, block: MutablePermissionTree.() -> Unit): TreeDiff { fun edit(
val mutable = tree?.let { namespace: String,
require(it.namespace == namespace) { block: MutablePermissionTree.() -> Unit,
"Existing tree namespace '${it.namespace}' differs from requested '$namespace'." ): TreeDiff {
} val mutable =
MutablePermissionTree.from(it) tree?.let {
} ?: MutablePermissionTree.create(namespace) require(it.namespace == namespace) {
"Existing tree namespace '${it.namespace}' differs from requested '$namespace'."
}
MutablePermissionTree.from(it)
} ?: MutablePermissionTree.create(namespace)
return editInternal(mutable, block) return editInternal(mutable, block)
} }
private fun editInternal( private fun editInternal(
mutable: MutablePermissionTree, mutable: MutablePermissionTree,
block: MutablePermissionTree.() -> Unit block: MutablePermissionTree.() -> Unit,
): TreeDiff { ): TreeDiff {
mutable.block() mutable.block()
val next = mutable.build() val next = mutable.build()
@ -61,13 +65,14 @@ class MutationSession(
} }
fun currentTree(): PermissionTree? = tree fun currentTree(): PermissionTree? = tree
fun lastDiff(): TreeDiff? = diff fun lastDiff(): TreeDiff? = diff
companion object { companion object {
fun create(plugin: org.bukkit.plugin.java.JavaPlugin): MutationSession = fun create(plugin: org.bukkit.plugin.java.JavaPlugin): MutationSession =
MutationSession( MutationSession(
registry = PermissionRegistry(plugin), registry = PermissionRegistry(plugin),
attachments = AttachmentSynchronizer(plugin) attachments = AttachmentSynchronizer(plugin),
) )
} }
} }

View File

@ -16,7 +16,7 @@ import org.bukkit.plugin.java.JavaPlugin
*/ */
class PermissionRegistry( class PermissionRegistry(
private val plugin: JavaPlugin, private val plugin: JavaPlugin,
private val pluginManager: PluginManager = plugin.server.pluginManager private val pluginManager: PluginManager = plugin.server.pluginManager,
) { ) {
private var snapshot: TreeSnapshot? = null private var snapshot: TreeSnapshot? = null

View File

@ -8,9 +8,13 @@ import org.bukkit.permissions.PermissionDefault
*/ */
class MutablePermissionTree internal constructor( class MutablePermissionTree internal constructor(
private val namespace: String, private val namespace: String,
private val drafts: MutableMap<PermissionId, PermissionNodeDraft> private val drafts: MutableMap<PermissionId, PermissionNodeDraft>,
) { ) {
fun node(id: String, registration: NodeRegistration, block: MutableNode.() -> Unit = {}): MutableNode { fun node(
id: String,
registration: NodeRegistration,
block: MutableNode.() -> Unit = {},
): MutableNode {
require(id.isNotBlank()) { "Node id must not be blank." } require(id.isNotBlank()) { "Node id must not be blank." }
val permissionId = PermissionId.of("$namespace.${id.lowercase()}") val permissionId = PermissionId.of("$namespace.${id.lowercase()}")
val draft = drafts.getOrPut(permissionId) { PermissionNodeDraft(permissionId) } val draft = drafts.getOrPut(permissionId) { PermissionNodeDraft(permissionId) }
@ -24,7 +28,10 @@ class MutablePermissionTree internal constructor(
removeSubtree(permissionId) removeSubtree(permissionId)
} }
fun renameNode(oldId: String, newId: String) { fun renameNode(
oldId: String,
newId: String,
) {
require(oldId.isNotBlank()) { "Old node id must not be blank." } require(oldId.isNotBlank()) { "Old node id must not be blank." }
require(newId.isNotBlank()) { "New node id must not be blank." } require(newId.isNotBlank()) { "New node id must not be blank." }
val oldPermissionId = PermissionId.of("$namespace.${oldId.lowercase()}") val oldPermissionId = PermissionId.of("$namespace.${oldId.lowercase()}")
@ -44,7 +51,7 @@ class MutablePermissionTree internal constructor(
inner class MutableNode internal constructor( inner class MutableNode internal constructor(
val id: PermissionId, val id: PermissionId,
private val draft: PermissionNodeDraft private val draft: PermissionNodeDraft,
) { ) {
var description: String? var description: String?
get() = draft.description get() = draft.description
@ -70,18 +77,28 @@ class MutablePermissionTree internal constructor(
draft.registration = value draft.registration = value
} }
fun child(id: String, value: Boolean = true) { fun child(
id: String,
value: Boolean = true,
) {
require(id.isNotBlank()) { "Child id must not be blank." } require(id.isNotBlank()) { "Child id must not be blank." }
val permissionId = PermissionId.of("${this.id.value}.${id.lowercase()}") val permissionId = PermissionId.of("${this.id.value}.${id.lowercase()}")
draft.children[permissionId] = value draft.children[permissionId] = value
} }
fun childAbsolute(id: String, value: Boolean = true) { fun childAbsolute(
id: String,
value: Boolean = true,
) {
val permissionId = PermissionId.of(id.lowercase()) val permissionId = PermissionId.of(id.lowercase())
draft.children[permissionId] = value draft.children[permissionId] = value
} }
fun node(id: String, registration: NodeRegistration, block: MutableNode.() -> Unit = {}) { fun node(
id: String,
registration: NodeRegistration,
block: MutableNode.() -> Unit = {},
) {
require(id.isNotBlank()) { "Node id must not be blank." } require(id.isNotBlank()) { "Node id must not be blank." }
val permissionId = PermissionId.of("${this.id.value}.${id.lowercase()}") val permissionId = PermissionId.of("${this.id.value}.${id.lowercase()}")
draft.children[permissionId] = true draft.children[permissionId] = true
@ -96,7 +113,10 @@ class MutablePermissionTree internal constructor(
removeSubtree(permissionId) removeSubtree(permissionId)
} }
fun renameNode(oldId: String, newId: String) { fun renameNode(
oldId: String,
newId: String,
) {
require(oldId.isNotBlank()) { "Old node id must not be blank." } require(oldId.isNotBlank()) { "Old node id must not be blank." }
require(newId.isNotBlank()) { "New node id must not be blank." } require(newId.isNotBlank()) { "New node id must not be blank." }
val oldPermissionId = PermissionId.of("${this.id.value}.${oldId.lowercase()}") val oldPermissionId = PermissionId.of("${this.id.value}.${oldId.lowercase()}")
@ -119,9 +139,10 @@ class MutablePermissionTree internal constructor(
private fun removeSubtree(rootId: PermissionId) { private fun removeSubtree(rootId: PermissionId) {
val prefix = "${rootId.value}." val prefix = "${rootId.value}."
val targets = drafts.keys.filter { key -> val targets =
key.value == rootId.value || key.value.startsWith(prefix) drafts.keys.filter { key ->
}.toSet() key.value == rootId.value || key.value.startsWith(prefix)
}.toSet()
if (targets.isEmpty()) return if (targets.isEmpty()) return
targets.forEach { drafts.remove(it) } targets.forEach { drafts.remove(it) }
drafts.values.forEach { draft -> drafts.values.forEach { draft ->
@ -135,12 +156,16 @@ class MutablePermissionTree internal constructor(
} }
} }
private fun renameSubtree(oldRoot: PermissionId, newRoot: PermissionId) { private fun renameSubtree(
oldRoot: PermissionId,
newRoot: PermissionId,
) {
if (oldRoot == newRoot) return if (oldRoot == newRoot) return
val prefix = "${oldRoot.value}." val prefix = "${oldRoot.value}."
val affected = drafts.keys.filter { key -> val affected =
key.value == oldRoot.value || key.value.startsWith(prefix) drafts.keys.filter { key ->
} key.value == oldRoot.value || key.value.startsWith(prefix)
}
if (affected.isEmpty()) return if (affected.isEmpty()) return
val affectedSet = affected.toSet() val affectedSet = affected.toSet()
val mapping = linkedMapOf<PermissionId, PermissionId>() val mapping = linkedMapOf<PermissionId, PermissionId>()
@ -156,15 +181,16 @@ class MutablePermissionTree internal constructor(
mapping.forEach { (oldId, newId) -> mapping.forEach { (oldId, newId) ->
val draft = drafts.remove(oldId) ?: return@forEach val draft = drafts.remove(oldId) ?: return@forEach
val newDraft = PermissionNodeDraft( val newDraft =
id = newId, PermissionNodeDraft(
description = draft.description, id = newId,
defaultValue = draft.defaultValue, description = draft.description,
children = draft.children.toMutableMap(), defaultValue = draft.defaultValue,
wildcard = draft.wildcard, children = draft.children.toMutableMap(),
registration = draft.registration, wildcard = draft.wildcard,
wildcardExclusions = draft.wildcardExclusions.toMutableSet() registration = draft.registration,
) wildcardExclusions = draft.wildcardExclusions.toMutableSet(),
)
drafts[newId] = newDraft drafts[newId] = newDraft
} }
@ -184,13 +210,12 @@ class MutablePermissionTree internal constructor(
} }
companion object { companion object {
fun create(namespace: String): MutablePermissionTree = fun create(namespace: String): MutablePermissionTree = MutablePermissionTree(namespace.trim().lowercase(), linkedMapOf())
MutablePermissionTree(namespace.trim().lowercase(), linkedMapOf())
fun from(tree: PermissionTree): MutablePermissionTree = fun from(tree: PermissionTree): MutablePermissionTree =
MutablePermissionTree( MutablePermissionTree(
namespace = tree.namespace, namespace = tree.namespace,
drafts = tree.nodes.mapValues { PermissionNodeDraft.from(it.value) }.toMutableMap() drafts = tree.nodes.mapValues { PermissionNodeDraft.from(it.value) }.toMutableMap(),
) )
} }
} }

View File

@ -6,5 +6,5 @@ package net.hareworks.permits_lib.domain
*/ */
enum class NodeRegistration(val registersPermission: Boolean) { enum class NodeRegistration(val registersPermission: Boolean) {
PERMISSION(true), PERMISSION(true),
STRUCTURAL(false) STRUCTURAL(false),
} }

View File

@ -15,7 +15,7 @@ data class PermissionNode(
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() 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." }

View File

@ -9,7 +9,7 @@ internal data class PermissionNodeDraft(
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() val wildcardExclusions: MutableSet<PermissionId> = linkedSetOf(),
) { ) {
fun toNode(): PermissionNode = fun toNode(): PermissionNode =
PermissionNode( PermissionNode(
@ -19,7 +19,7 @@ internal data class PermissionNodeDraft(
children = children.toMap(), children = children.toMap(),
wildcard = wildcard, wildcard = wildcard,
registration = registration, registration = registration,
wildcardExclusions = wildcardExclusions.toSet() wildcardExclusions = wildcardExclusions.toSet(),
) )
companion object { companion object {
@ -31,7 +31,7 @@ internal data class PermissionNodeDraft(
children = node.children.toMutableMap(), children = node.children.toMutableMap(),
wildcard = node.wildcard, wildcard = node.wildcard,
registration = node.registration, registration = node.registration,
wildcardExclusions = node.wildcardExclusions.toMutableSet() wildcardExclusions = node.wildcardExclusions.toMutableSet(),
) )
} }
} }

View File

@ -5,7 +5,7 @@ package net.hareworks.permits_lib.domain
*/ */
class PermissionTree internal constructor( class PermissionTree internal constructor(
val namespace: String, val namespace: String,
internal val nodes: Map<PermissionId, PermissionNode> internal val nodes: Map<PermissionId, PermissionNode>,
) { ) {
init { init {
require(namespace.isNotBlank()) { "Permission namespace must not be blank." } require(namespace.isNotBlank()) { "Permission namespace must not be blank." }
@ -15,13 +15,15 @@ class PermissionTree internal constructor(
operator fun get(id: PermissionId): PermissionNode? = nodes[id] operator fun get(id: PermissionId): PermissionNode? = nodes[id]
fun toSnapshot(): TreeSnapshot = fun toSnapshot(): TreeSnapshot = TreeSnapshot(nodes.filterValues { it.registration.registersPermission })
TreeSnapshot(nodes.filterValues { it.registration.registersPermission })
companion object { companion object {
fun empty(namespace: String): PermissionTree = PermissionTree(namespace, emptyMap()) fun empty(namespace: String): PermissionTree = PermissionTree(namespace, emptyMap())
fun from(namespace: String, rawNodes: Map<PermissionId, PermissionNode>): PermissionTree { fun from(
namespace: String,
rawNodes: Map<PermissionId, PermissionNode>,
): PermissionTree {
val augmented = WildcardAugmentor.apply(rawNodes) val augmented = WildcardAugmentor.apply(rawNodes)
PermissionTreeValidator.validate(augmented) PermissionTreeValidator.validate(augmented)
return PermissionTree(namespace, augmented) return PermissionTree(namespace, augmented)

View File

@ -3,7 +3,7 @@ package net.hareworks.permits_lib.domain
data class TreeDiff( data class TreeDiff(
val added: List<PermissionNode>, val added: List<PermissionNode>,
val removed: List<PermissionNode>, val removed: List<PermissionNode>,
val updated: List<UpdatedNode> val updated: List<UpdatedNode>,
) { ) {
val hasChanges: Boolean val hasChanges: Boolean
get() = added.isNotEmpty() || removed.isNotEmpty() || updated.isNotEmpty() get() = added.isNotEmpty() || removed.isNotEmpty() || updated.isNotEmpty()

View File

@ -1,7 +1,10 @@
package net.hareworks.permits_lib.domain package net.hareworks.permits_lib.domain
object TreeDiffer { object TreeDiffer {
fun diff(previous: TreeSnapshot?, next: TreeSnapshot): TreeDiff { fun diff(
previous: TreeSnapshot?,
next: TreeSnapshot,
): TreeDiff {
val prevNodes = previous?.nodes.orEmpty() val prevNodes = previous?.nodes.orEmpty()
val nextNodes = next.nodes val nextNodes = next.nodes
@ -23,7 +26,7 @@ object TreeDiffer {
return TreeDiff( return TreeDiff(
added = added.sortedBy { it.id.value }, added = added.sortedBy { it.id.value },
removed = removed.sortedBy { it.id.value }, removed = removed.sortedBy { it.id.value },
updated = updated.sortedBy { it.after.id.value } updated = updated.sortedBy { it.after.id.value },
) )
} }
} }

View File

@ -6,7 +6,7 @@ import java.security.MessageDigest
* Snapshot of a tree at a specific point in time. Holds a deterministic digest useful for caching. * Snapshot of a tree at a specific point in time. Holds a deterministic digest useful for caching.
*/ */
class TreeSnapshot internal constructor( class TreeSnapshot internal constructor(
internal val nodes: Map<PermissionId, PermissionNode> internal val nodes: Map<PermissionId, PermissionNode>,
) { ) {
val digest: String = computeDigest(nodes) val digest: String = computeDigest(nodes)

View File

@ -1,7 +1,5 @@
package net.hareworks.permits_lib.domain package net.hareworks.permits_lib.domain
import org.bukkit.permissions.PermissionDefault
internal object WildcardAugmentor { internal object WildcardAugmentor {
fun apply(nodes: Map<PermissionId, PermissionNode>): Map<PermissionId, PermissionNode> { fun apply(nodes: Map<PermissionId, PermissionNode>): Map<PermissionId, PermissionNode> {
if (nodes.isEmpty()) return nodes if (nodes.isEmpty()) return nodes
@ -12,19 +10,21 @@ 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 val updatedChildren =
.filterKeys { childId -> childId !in node.wildcardExclusions } node.children
.toMutableMap() .filterKeys { childId -> childId !in node.wildcardExclusions }
.toMutableMap()
val existing = result[wildcardId] val existing = result[wildcardId]
if (existing == null) { if (existing == null) {
result[wildcardId] = PermissionNode( result[wildcardId] =
id = wildcardId, PermissionNode(
description = "Wildcard for ${node.id.value}", id = wildcardId,
defaultValue = node.defaultValue, description = "Wildcard for ${node.id.value}",
children = updatedChildren, defaultValue = node.defaultValue,
wildcard = false children = updatedChildren,
) wildcard = false,
)
} else { } else {
result[wildcardId] = existing.copy(children = updatedChildren) result[wildcardId] = existing.copy(children = updatedChildren)
} }

View File

@ -8,7 +8,7 @@ import org.bukkit.permissions.PermissionDefault
@PermissionDsl @PermissionDsl
class PermissionNodeBuilder internal constructor( class PermissionNodeBuilder internal constructor(
private val treeBuilder: PermissionTreeBuilder, private val treeBuilder: PermissionTreeBuilder,
private val draft: PermissionNodeDraft private val draft: PermissionNodeDraft,
) { ) {
var description: String? var description: String?
get() = draft.description get() = draft.description
@ -39,18 +39,27 @@ class PermissionNodeBuilder internal constructor(
draft.registration = value draft.registration = value
} }
fun child(id: String, value: Boolean = true) { fun child(
id: String,
value: Boolean = true,
) {
treeBuilder.childRelative(draft, id, value) treeBuilder.childRelative(draft, id, value)
} }
fun child(id: PermissionId, value: Boolean = true) { fun child(
id: PermissionId,
value: Boolean = true,
) {
treeBuilder.childAbsolute(draft, id.value, value) treeBuilder.childAbsolute(draft, id.value, value)
} }
/** /**
* Links to a fully-qualified permission id. The provided [id] must already include its namespace. * Links to a fully-qualified permission id. The provided [id] must already include its namespace.
*/ */
fun childAbsolute(id: String, value: Boolean = true) { fun childAbsolute(
id: String,
value: Boolean = true,
) {
treeBuilder.childAbsolute(draft, id, value) treeBuilder.childAbsolute(draft, id, value)
} }
@ -66,20 +75,21 @@ class PermissionNodeBuilder internal constructor(
fun node( fun node(
id: String, id: String,
registration: NodeRegistration, registration: NodeRegistration,
block: PermissionNodeBuilder.() -> Unit = {} block: PermissionNodeBuilder.() -> Unit = {},
) { ) {
treeBuilder.nestedNode(draft, id, registration, block) treeBuilder.nestedNode(draft, id, registration, block)
} }
} }
class WildcardDsl internal constructor( class WildcardDsl internal constructor(
private val draft: PermissionNodeDraft private val draft: PermissionNodeDraft,
) { ) {
fun exclude(vararg segments: String) { fun exclude(vararg segments: String) {
val normalized = segments val normalized =
.flatMap { it.split('.') } segments
.map { it.trim().lowercase() } .flatMap { it.split('.') }
.filter { it.isNotEmpty() } .map { it.trim().lowercase() }
.filter { it.isNotEmpty() }
if (normalized.isEmpty()) return if (normalized.isEmpty()) return
val suffix = normalized.joinToString(".") val suffix = normalized.joinToString(".")
val permissionId = PermissionId.of("${draft.id.value}.$suffix") val permissionId = PermissionId.of("${draft.id.value}.$suffix")

View File

@ -7,14 +7,14 @@ import net.hareworks.permits_lib.domain.PermissionTree
@PermissionDsl @PermissionDsl
class PermissionTreeBuilder internal constructor( class PermissionTreeBuilder internal constructor(
private val namespace: String private val namespace: String,
) { ) {
private val drafts = linkedMapOf<PermissionId, PermissionNodeDraft>() private val drafts = linkedMapOf<PermissionId, PermissionNodeDraft>()
fun node( fun node(
id: String, id: String,
registration: NodeRegistration, registration: NodeRegistration,
block: PermissionNodeBuilder.() -> Unit = {} block: PermissionNodeBuilder.() -> Unit = {},
) { ) {
require(id.isNotBlank()) { "Node id must not be blank." } require(id.isNotBlank()) { "Node id must not be blank." }
val permissionId = PermissionId.of("$namespace.${id.lowercase()}") val permissionId = PermissionId.of("$namespace.${id.lowercase()}")
@ -27,14 +27,15 @@ class PermissionTreeBuilder internal constructor(
parent: PermissionNodeDraft, parent: PermissionNodeDraft,
id: String, id: String,
value: Boolean, value: Boolean,
relative: Boolean relative: Boolean,
) { ) {
val target = if (relative) { val target =
require(id.isNotBlank()) { "Child id must not be blank." } if (relative) {
"${parent.id.value}.${id.lowercase()}" require(id.isNotBlank()) { "Child id must not be blank." }
} else { "${parent.id.value}.${id.lowercase()}"
normalizeAbsolute(id) } else {
} normalizeAbsolute(id)
}
val permissionId = PermissionId.of(target) val permissionId = PermissionId.of(target)
parent.children[permissionId] = value parent.children[permissionId] = value
} }
@ -42,20 +43,20 @@ class PermissionTreeBuilder internal constructor(
internal fun childRelative( internal fun childRelative(
parent: PermissionNodeDraft, parent: PermissionNodeDraft,
id: String, id: String,
value: Boolean value: Boolean,
) = child(parent, id, value, relative = true) ) = child(parent, id, value, relative = true)
internal fun childAbsolute( internal fun childAbsolute(
parent: PermissionNodeDraft, parent: PermissionNodeDraft,
id: String, id: String,
value: Boolean value: Boolean,
) = child(parent, id, value, relative = false) ) = child(parent, id, value, relative = false)
internal fun nestedNode( internal fun nestedNode(
parent: PermissionNodeDraft, parent: PermissionNodeDraft,
id: String, id: String,
registration: NodeRegistration, registration: NodeRegistration,
block: PermissionNodeBuilder.() -> Unit block: PermissionNodeBuilder.() -> Unit,
) { ) {
require(id.isNotBlank()) { "Nested node id must not be blank." } require(id.isNotBlank()) { "Nested node id must not be blank." }
val composedId = PermissionId.of("${parent.id.value}.${id.lowercase()}") val composedId = PermissionId.of("${parent.id.value}.${id.lowercase()}")
@ -65,8 +66,7 @@ class PermissionTreeBuilder internal constructor(
PermissionNodeBuilder(this, draft).apply(block) PermissionNodeBuilder(this, draft).apply(block)
} }
fun build(): PermissionTree = fun build(): PermissionTree = PermissionTree.from(namespace, drafts.mapValues { it.value.toNode() })
PermissionTree.from(namespace, drafts.mapValues { it.value.toNode() })
private fun normalizeAbsolute(id: String): String { private fun normalizeAbsolute(id: String): String {
require(id.isNotBlank()) { "Absolute permission id must not be blank." } require(id.isNotBlank()) { "Absolute permission id must not be blank." }
@ -74,5 +74,7 @@ class PermissionTreeBuilder internal constructor(
} }
} }
fun permissionTree(namespace: String, block: PermissionTreeBuilder.() -> Unit): PermissionTree = fun permissionTree(
PermissionTreeBuilder(namespace.trim().lowercase()).apply(block).build() namespace: String,
block: PermissionTreeBuilder.() -> Unit,
): PermissionTree = PermissionTreeBuilder(namespace.trim().lowercase()).apply(block).build()