-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #112 from mfnalex/argument-parsing
Parsed Commands!!!
- Loading branch information
Showing
28 changed files
with
751 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
118 changes: 118 additions & 0 deletions
118
core/src/main/kotlin/com/github/spigotbasics/core/command/parsed/ArgumentPath.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package com.github.spigotbasics.core.command.parsed | ||
|
||
import com.github.spigotbasics.common.Either | ||
import com.github.spigotbasics.core.Basics | ||
import com.github.spigotbasics.core.extensions.lastOrEmpty | ||
import com.github.spigotbasics.core.messages.Message | ||
import org.bukkit.command.CommandSender | ||
import org.bukkit.permissions.Permission | ||
|
||
class ArgumentPath<T : CommandContext>( | ||
val senderArgument: SenderType<*>, | ||
val arguments: List<CommandArgument<*>>, | ||
// TODO: Check permission for specific paths! | ||
val permission: List<Permission> = emptyList(), | ||
private val contextBuilder: (CommandSender, List<Any?>) -> T, | ||
) { | ||
// fun matches(args: List<String>): Boolean { | ||
// if (args.size > arguments.size) return false | ||
// | ||
// for ((index, arg) in args.withIndex()) { | ||
// if (arguments[index].parse(arg) == null) return false | ||
// } | ||
// | ||
// return true | ||
// } | ||
|
||
fun matches( | ||
sender: CommandSender, | ||
args: List<String>, | ||
): Either<PathMatchResult, List<Message>> { | ||
// Exact match for the number of arguments | ||
if (args.size > arguments.size) { | ||
return Either.Left( | ||
PathMatchResult.NO, | ||
) // Maybe use != ? > allows to show "missing item", != wouldn't | ||
} | ||
// TODO: Keep a list of non-matches where size is too little, and if no other errors occur, say "missing item", only otherwise | ||
// fallback to CommandResult.USAGE | ||
|
||
// Each provided arg must be parseable by its corresponding CommandArgument | ||
val errors = mutableListOf<Message>() | ||
// val matches = // used to be all(...) | ||
args.indices.forEach { index -> | ||
val parsed = arguments[index].parse(args[index]) | ||
|
||
if (parsed == null) { | ||
val error = arguments[index].errorMessage(args[index]) | ||
errors.add(error) | ||
} | ||
|
||
// true | ||
} | ||
|
||
if (errors.isNotEmpty()) return Either.Right(errors) | ||
|
||
if (!senderArgument.requiredType.isInstance(sender)) return Either.Left(PathMatchResult.YES_BUT_NOT_FROM_CONSOLE) | ||
if (!hasPermission(sender)) return Either.Left(PathMatchResult.YES_BUT_NO_PERMISSION) | ||
return Either.Left(PathMatchResult.YES) | ||
} | ||
|
||
// fun parse(args: List<String>): T? { | ||
// if (!matches(args)) return null | ||
// val parsedArgs = | ||
// arguments.zip(args).mapNotNull { (arg, value) -> | ||
// arg.parse(value) | ||
// } | ||
// return contextBuilder(parsedArgs) | ||
// } | ||
|
||
fun parse( | ||
sender: CommandSender, | ||
args: List<String>, | ||
): ParseResult<T> { | ||
if (!senderArgument.requiredType.isInstance(sender)) { | ||
return ParseResult.Failure(listOf(Basics.messages.commandNotFromConsole)) | ||
} | ||
|
||
val parsedArgs = mutableListOf<Any?>() | ||
val errors = mutableListOf<Message>() | ||
|
||
for ((index, arg) in arguments.withIndex()) { | ||
if (index >= args.size) { | ||
// errors.add("Missing argument for ${arg.name}") | ||
errors.add(Basics.messages.missingArgument(arg.name)) | ||
break | ||
} | ||
|
||
val parsed = arg.parse(args[index]) | ||
if (parsed == null) { | ||
errors.add(arg.errorMessage(args[index])) | ||
break | ||
} else { | ||
parsedArgs.add(parsed) | ||
} | ||
} | ||
|
||
if (errors.isEmpty() && parsedArgs.size == arguments.size) { | ||
return ParseResult.Success(contextBuilder(sender, parsedArgs)) | ||
} else { | ||
return ParseResult.Failure(errors) | ||
} | ||
} | ||
|
||
fun tabComplete(args: List<String>): List<String> { | ||
if (args.isEmpty() || args.size > arguments.size) return emptyList() | ||
|
||
val currentArgIndex = args.size - 1 | ||
return arguments[currentArgIndex].tabComplete(args.lastOrEmpty()) | ||
} | ||
|
||
fun isCorrectSender(sender: CommandSender): Boolean { | ||
return senderArgument.requiredType.isInstance(sender) | ||
} | ||
|
||
fun hasPermission(sender: CommandSender): Boolean { | ||
return permission.all { sender.hasPermission(it) } | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
core/src/main/kotlin/com/github/spigotbasics/core/command/parsed/CommandArgument.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.github.spigotbasics.core.command.parsed | ||
|
||
import com.github.spigotbasics.core.Basics | ||
import com.github.spigotbasics.core.messages.Message | ||
|
||
abstract class CommandArgument<T>(val name: String) { | ||
abstract fun parse(value: String): T? | ||
|
||
open fun tabComplete(typing: String): List<String> = emptyList() | ||
|
||
// TODO: This is using the static Singleton :/ | ||
open fun errorMessage(value: String? = null): Message = Basics.messages.invalidValueForArgument(name, value ?: "null") | ||
} |
3 changes: 3 additions & 0 deletions
3
core/src/main/kotlin/com/github/spigotbasics/core/command/parsed/CommandContext.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package com.github.spigotbasics.core.command.parsed | ||
|
||
interface CommandContext |
10 changes: 10 additions & 0 deletions
10
core/src/main/kotlin/com/github/spigotbasics/core/command/parsed/CommandExecutor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.github.spigotbasics.core.command.parsed | ||
|
||
import org.bukkit.command.CommandSender | ||
|
||
interface CommandExecutor<T : CommandContext> { | ||
fun execute( | ||
sender: CommandSender, | ||
context: T, | ||
) | ||
} |
9 changes: 9 additions & 0 deletions
9
core/src/main/kotlin/com/github/spigotbasics/core/command/parsed/ParseResult.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.github.spigotbasics.core.command.parsed | ||
|
||
import com.github.spigotbasics.core.messages.Message | ||
|
||
sealed class ParseResult<out T> { | ||
data class Success<T>(val context: T) : ParseResult<T>() | ||
|
||
data class Failure(val errors: List<Message>) : ParseResult<Nothing>() | ||
} |
98 changes: 98 additions & 0 deletions
98
core/src/main/kotlin/com/github/spigotbasics/core/command/parsed/ParsedCommandBuilder.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package com.github.spigotbasics.core.command | ||
|
||
import com.github.spigotbasics.common.Either | ||
import com.github.spigotbasics.core.command.parsed.ArgumentPath | ||
import com.github.spigotbasics.core.command.parsed.CommandContext | ||
import com.github.spigotbasics.core.command.parsed.CommandExecutor | ||
import com.github.spigotbasics.core.command.parsed.ParsedCommandExecutor | ||
import com.github.spigotbasics.core.messages.Message | ||
import com.github.spigotbasics.core.module.BasicsModule | ||
import org.bukkit.permissions.Permission | ||
|
||
class ParsedCommandBuilder<T : CommandContext>( | ||
private val module: BasicsModule, | ||
private val name: String, | ||
private val permission: Permission, | ||
) { | ||
private var permissionMessage: Message = module.plugin.messages.noPermission | ||
private var description: String? = null | ||
private var usage: String = "" | ||
private var aliases: List<String> = emptyList() | ||
private var executor: BasicsCommandExecutor? = null | ||
private var tabCompleter: BasicsTabCompleter? = null | ||
private var parsedExecutor: CommandExecutor<T>? = null | ||
private var argumentPaths: List<ArgumentPath<T>>? = null | ||
|
||
fun description(description: String) = apply { this.description = description } | ||
|
||
fun usage(usage: String) = | ||
apply { | ||
if (usage.startsWith("/")) error("Usage should not start with /<command> - only pass the arguments.") | ||
this.usage = usage | ||
} | ||
|
||
fun paths(argumentPaths: List<ArgumentPath<T>>) = apply { this.argumentPaths = argumentPaths } | ||
|
||
fun paths(vararg argumentPaths: ArgumentPath<T>) = apply { this.argumentPaths = argumentPaths.toList() } | ||
|
||
fun executor(executor: CommandExecutor<T>) = apply { this.parsedExecutor = executor } | ||
|
||
private fun executor(executor: BasicsCommandExecutor) = apply { this.executor = executor } | ||
|
||
private fun executor(command: ParsedCommandExecutor<T>) = | ||
apply { | ||
this.executor = | ||
object : BasicsCommandExecutor(module) { | ||
override fun execute(context: BasicsCommandContext): CommandResult? { | ||
val result = command.execute(context.sender, context.args) | ||
|
||
if (result is Either.Left) { | ||
return result.value | ||
} | ||
|
||
if (result is Either.Right) { | ||
val failure = result.value | ||
// TODO: Proper messages | ||
failure.errors.forEach { it.sendToSender(context.sender) } | ||
} | ||
|
||
return null | ||
} | ||
|
||
override fun tabComplete(context: BasicsCommandContext): MutableList<String> { | ||
return command.tabComplete(context.sender, context.args).toMutableList() | ||
} | ||
} | ||
} | ||
|
||
fun register(): BasicsCommand { | ||
val command = build() | ||
module.commandManager.registerCommand(command) | ||
return command | ||
} | ||
|
||
private fun build(): BasicsCommand { | ||
val command = | ||
ParsedCommandExecutor( | ||
parsedExecutor ?: error("parsedExecutor must be set"), | ||
argumentPaths ?: error("Argument paths must be set"), | ||
) | ||
executor(command) | ||
val info = | ||
CommandInfo( | ||
name = name, | ||
permission = permission, | ||
permissionMessage = permissionMessage, | ||
description = description, | ||
usage = usage, | ||
aliases = aliases, | ||
) | ||
return BasicsCommand( | ||
info = info, | ||
executor = executor ?: error("Executor must be set"), | ||
tabCompleter = tabCompleter ?: executor, | ||
coreMessages = module.plugin.messages, | ||
messageFactory = module.plugin.messageFactory, | ||
) | ||
} | ||
} |
Oops, something went wrong.