diff --git a/src/main/kotlin/ArgParse.kt b/src/main/kotlin/ArgParse.kt new file mode 100644 index 0000000..9179c66 --- /dev/null +++ b/src/main/kotlin/ArgParse.kt @@ -0,0 +1,69 @@ +import org.bukkit.Bukkit +import org.bukkit.OfflinePlayer +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import org.bukkit.permissions.Permission +import java.util.* + +typealias Suggestor = (args: List, sender: CommandSender, current: String) -> Optional> +typealias ArgParser = (String) -> Optional +typealias ArgNode = Pair, Suggestor> + +inline fun constantParseNode(value: T, crossinline toStringFunc: T.() -> String = { this.toString() }): ArgNode = + { it: String -> + if (it == value.toStringFunc()) Optional.of(value!!) + else Optional.empty() + } to { _, _, current -> + if (current.startsWith(value.toStringFunc())) Optional.of(listOf(value.toStringFunc())) + else Optional.empty>() + } + +val PARSE_NODE_STRING: ArgNode = { str: String -> Optional.of(str) } to { _, _, _ -> Optional.of(emptyList()) } +val PARSE_NODE_PLAYER: ArgNode = + { str: String -> + val player = Bukkit.getOfflinePlayers().firstOrNull { it.name?.equals(str) == true } + if (player == null) Optional.empty() + else Optional.of(player) + } to { _, _, current -> + Optional.of(Bukkit.getOfflinePlayers().filter { it.name?.startsWith(current) == true }.map { it.name!! }) + } + +open class ParseBranch(private vararg val nodes: ArgNode<*>) { + open fun isEligible(sender: CommandSender): Boolean = true + fun getSuggestions(args: Array, sender: CommandSender): Optional> { + if (args.size > nodes.size) return Optional.empty() + + return nodes[args.size - 1].second((0 until args.size - 1).map { + val parsed = nodes[it].first(args[it]) + + if (parsed.isEmpty) return Optional.empty() + + parsed.get() + }, sender, args[args.size - 1]) + } +} + +class PermissionParseBranch(private val permissionNode: Permission, private val allowConsole: Boolean, vararg nodes: ArgNode<*>): ParseBranch(*nodes) { + constructor(permissionNode: Permission, vararg nodes: ArgNode<*>): this(permissionNode, true, *nodes) + override fun isEligible(sender: CommandSender) = + (sender is OfflinePlayer && sender.hasPermission(permissionNode)) || (sender !is OfflinePlayer && allowConsole) +} + +class ParseTree { + private val branches = LinkedList() + + fun branch(vararg nodes: ArgNode<*>) = branch(ParseBranch(*nodes)) + fun branch(parseBranch: ParseBranch): ParseTree { + branches += parseBranch + return this + } + + fun getSuggestions(args: Array, sender: CommandSender) = + branches.filter { it.isEligible(sender) } + .mapNotNull { + val suggestions = it.getSuggestions(args, sender) + return@mapNotNull if (suggestions.isEmpty) null else suggestions.get() + } + .flatten() + .toHashSet() +} \ No newline at end of file