From 4db84feb43b785adc8332089cdf40aaea85af131 Mon Sep 17 00:00:00 2001 From: kento2 Date: Sun, 2 Aug 2026 21:23:36 +0200 Subject: [PATCH] BAZEL + some code --- .bazelrc | 4 + .gitignore | 13 +- .idea/bazelVersionCache.xml | 2 +- .idea/misc.xml | 2 +- .idea/workspace.xml | 143 +++++++++++------- README.md | 83 +++++++++- kencommandapi-bukkit/BUILD | 5 +- .../kencommandapi/BukkitKenCommand.java | 6 +- .../de/kentoj/kencommandapi/CommandAPI.java | 8 +- .../kencommandapi/TabCompleteListener.java | 6 +- .../PlayerSuggestionProvider.java | 4 +- .../type/OfflinePlayerArgumentType.java | 13 +- .../type/PlayerArgumentType.java | 16 +- kencommandapi-core/BUILD | 5 +- .../kencommandapi/api/CommandHandler.java | 35 ++--- .../api/argument/ArgumentRequirement.java | 14 ++ .../api/argument/ArgumentType.java | 14 +- .../api/argument/CommandArgument.java | 26 +--- .../api/argument/CommandArgumentImpl.java | 9 ++ .../api/argument/CommandArgumentSpec.java | 32 ++++ .../argument/CommandArgumentSpecBuilder.java | 47 ++++++ .../api/argument/CommandArgumentSpecImpl.java | 42 +++++ .../argument/types/IntegerArgumentType.java | 7 +- .../argument/types/StringArgumentType.java | 7 +- .../api/invocation/CommandContext.java | 40 ++--- .../api/invocation/ParsedArgument.java | 10 -- .../kencommandapi/api/literal/Literal.java | 48 ++---- .../api/literal/LiteralBuilder.java | 100 ++++++++++++ .../api/literal/LiteralImpl.java | 38 +++++ .../api/literal/RootLiteral.java | 10 -- .../api/platform/PlainMessageStyle.java | 5 +- .../api/suggestion/NoSuggestionProvider.java | 6 +- .../api/suggestion/SuggestionProvider.java | 5 +- .../internal/CommandArgumentImpl.java | 34 ----- .../internal/CommandContextImpl.java | 26 ++++ .../kencommandapi/internal/LiteralImpl.java | 72 --------- .../internal/ParsedArgumentImpl.java | 11 -- .../internal/RootLiteralImpl.java | 17 --- .../parser/InvocationCommandParser.java | 35 ++--- .../parser/SuggestionCommandParser.java | 3 +- .../parser/SuggestionCommandParserTest.java | 4 +- .../de/kentoj/kencommandapi/CommandAPI.java | 4 +- .../kentoj/kencommandapi/VelocityCommand.java | 4 +- third_party/BUILD | 1 - 44 files changed, 627 insertions(+), 389 deletions(-) create mode 100644 .bazelrc create mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentRequirement.java create mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentImpl.java create mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpec.java create mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecBuilder.java create mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecImpl.java delete mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/ParsedArgument.java create mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralBuilder.java create mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralImpl.java delete mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/RootLiteral.java delete mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandArgumentImpl.java create mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandContextImpl.java delete mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/LiteralImpl.java delete mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/ParsedArgumentImpl.java delete mode 100644 kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/RootLiteralImpl.java diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..36dbde3 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,4 @@ +build --java_language_version=25 +build --java_runtime_version=25 +build --tool_java_language_version=25 +build --tool_java_runtime_version=25 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1fac4d5..bcca68a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,10 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ -.kotlin +/.idea/inspectionProfiles +/MODULE.bazel.lock +/.bazelbsp +/bazel-bin +/bazel-out +/bazel-KenCommandAPI +/bazel-testlogs ### IntelliJ IDEA ### .idea/modules.xml diff --git a/.idea/bazelVersionCache.xml b/.idea/bazelVersionCache.xml index ef2f15b..dc93e11 100644 --- a/.idea/bazelVersionCache.xml +++ b/.idea/bazelVersionCache.xml @@ -6,7 +6,7 @@ diff --git a/.idea/misc.xml b/.idea/misc.xml index 4467ac4..6b56b62 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 79f284c..cf49dca 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -9,7 +9,7 @@ @@ -37,20 +37,49 @@ - - - - - + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - { - "keyToString": { - "Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [:kencommandapi-core:de.kentoj.kencommandapi.Main.main()].executor": "Run", - "Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [build].executor": "Run", - "Gradle./home/a/src/kento2/KenCommandAPI/kencommandapi-core [publish].executor": "Run", - "Gradle.CommandParserTest.checkArgumentExpected.executor": "Run", - "Gradle.Download Sources.executor": "Run", - "Gradle.KenCommandAPI [:de.kencommand.Main.main()].executor": "Run", - "Gradle.KenCommandAPI [assemble].executor": "Run", - "Gradle.KenCommandAPI [buildEnvironment].executor": "Run", - "Gradle.KenCommandAPI [build].executor": "Run", - "Gradle.KenCommandAPI [clean test].executor": "Run", - "Gradle.KenCommandAPI [clean].executor": "Run", - "Gradle.KenCommandAPI [javadoc].executor": "Run", - "Gradle.KenCommandAPI [publishAllPublicationsToScrowRepository].executor": "Run", - "Gradle.KenCommandAPI [publish].executor": "Run", - "Gradle.KenCommandAPI [test].executor": "Run", - "Gradle.KenCommandAPI:kencommandapi-bukkit [build].executor": "Run", - "Gradle.KenCommandAPI:kencommandapi-bukkit [generatePomFileForKenCommandAPIBukkitPublication].executor": "Run", - "Gradle.KenCommandAPI:kencommandapi-bukkit [publishAllPublicationsToScrowRepository].executor": "Run", - "Gradle.KenCommandAPI:kencommandapi-bukkit [publish].executor": "Run", - "Gradle.KenCommandAPI:kencommandapi-velocity [publish].executor": "Run", - "Gradle.kencommandapi-core [build].executor": "Run", - "ModuleVcsDetector.initialDetectionPerformed": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", - "RunOnceActivity.git.unshallow": "true", - "SHARE_PROJECT_CONFIGURATION_FILES": "true", - "bazel.project.detector.is.bazel.project": "true", - "git-widget-placeholder": "master", - "kotlin-language-version-configured": "true", - "last_opened_file_path": "/home/a/src/scrow/KenCommandAPI/gradle", - "project.structure.last.edited": "Modules", - "project.structure.proportion": "0.15", - "project.structure.side.proportion": "0.6268199", - "remote.dev.show.docker.config.missing.jdk.notification": "false", - "settings.editor.selected.configurable": "preferences.pluginManager" + +}]]> @@ -200,8 +229,13 @@ + + + + + @@ -220,13 +254,13 @@ - - + + @@ -621,6 +655,7 @@ - \ No newline at end of file diff --git a/README.md b/README.md index 3002d3b..ad01ec3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,81 @@ -example usage: -http://forge.kentoj.de/scrow/core/src/commit/a55d985773t425e099fe9650cc1a705ceb2ece00/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/economy \ No newline at end of file +## Limitations & Bugs + +- argument's can't require specific permissions. +- errors are always formatted using the RootLiteral's MessageStyle + +## Design + +Commands are trees of Literals. +Literals can have arguments. +Literals can also have Executors. + +arguments can be optional by giving them a default value. +arguments can have suggestions. + +`/feed `: +- feed is the root literal +- `` is an argument: it's value is a User Object + +To allow the use of `/feed` without giving a user's name, i.e. to make the argument optional, we need to assign the +user-argument a default value. + +## An example command + +Let's say we want to make a command that allows seeing how many coins another User has. +If we don't specify a user, it should instead show the coins the sender has. + +## As a tree + +Visualized as a tree, we have a Node/Literal `coins` with an alias `balance` with an optional argument called `user`. +To make an argument optional, it must have a default value. + +```text +literal:coins/balance +- has argument:user +- has executor:getCoins +``` + +## As code +```java +/** + * Show the amount coins a user has. + * If no user is given, it should show the amount the sender of the command has. + */ +@NoArgsConstructor(access = AccessLevel.NONE) +public class CoinsCommand { + + @Getter + private final Literal rootLiteral; + private final CommandArgumentSpec userArg; + + public CoinsCommand() { + userArg = CommandArgumentSpec.builder("user", new UserArgumentType()) + .withRequirement(p -> !p.hasPermission("economy.immune.getcoins"), + "you can't see that player's balance") + .withDefaultValue(CommandContext::getSender) + .build(); + rootLiteral = Literal.builder("coins", "balance") + .withStyle(MessageStyle.PLAIN) + .withPermission("economy.command.coins") + .withArgument(userArg) + .withSyncExecutor(this::getCoins) + .build(); + } + + private Result getCoins(CommandContext ctx) { + var player = ctx.getArg(userArg); + ctx.getSender().sendMessage(player.getName() + " has " + player.getCoins() + " coins"); + return Results.success(new Object()); + } +} +``` + +If we wanted to report an error, we could do for example `return Results.failure("That user doesn't exist")`. +However, if we have a smart UserArgumentType, it automatically makes sure that the user exists. +Additionally, we can also a Requirement in the CommandArgumentSpec. + +The registration of the command is platform-dependant. Typically, a class called CommandAPI can be used. +```java +CommandAPI api = new CommandAPI(); +register(new CoinsCommand().getRootLiteral()); +``` \ No newline at end of file diff --git a/kencommandapi-bukkit/BUILD b/kencommandapi-bukkit/BUILD index bd977c3..c5538ff 100644 --- a/kencommandapi-bukkit/BUILD +++ b/kencommandapi-bukkit/BUILD @@ -6,9 +6,12 @@ java_library( srcs = glob(["src/main/**/*.java"]), deps = [ "//kencommandapi-core:core", - artifact("org.jetbrains:annotations"), artifact("org.projectlombok:lombok"), + artifact("org.jetbrains:annotations"), artifact("io.papermc.paper:paper-api:[26.2.build,)"), ], + exports = [ + "//kencommandapi-core:core", + ], plugins = [ "//third_party:lombok_plugin" ], ) \ No newline at end of file diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java index 20608b9..5a1fba0 100644 --- a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java +++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/BukkitKenCommand.java @@ -1,7 +1,7 @@ package de.kentoj.kencommandapi; import de.kentoj.kencommandapi.api.CommandHandler; -import de.kentoj.kencommandapi.api.literal.RootLiteral; +import de.kentoj.kencommandapi.api.literal.Literal; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; @@ -11,12 +11,12 @@ import java.util.stream.Stream; public class BukkitKenCommand extends Command { - private final RootLiteral rootNode; + private final Literal rootNode; private final CommandHandler commandHandler; protected BukkitKenCommand( CommandHandler commandHandler, - RootLiteral rootLiteral + Literal rootLiteral ) { super(rootLiteral.getNames()[0]); this.commandHandler = commandHandler; diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java index 9c27132..7fc80a2 100644 --- a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java +++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/CommandAPI.java @@ -1,7 +1,7 @@ package de.kentoj.kencommandapi; import de.kentoj.kencommandapi.api.CommandHandler; -import de.kentoj.kencommandapi.api.literal.RootLiteral; +import de.kentoj.kencommandapi.api.literal.Literal; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.command.SimpleCommandMap; @@ -16,7 +16,7 @@ import java.util.List; public class CommandAPI { private final CommandHandler commandHandler; - private final List> registeredNodes = new ArrayList<>(); + private final List> registeredNodes = new ArrayList<>(); private final SimpleCommandMap commandMap; public CommandAPI(Plugin plugin) { @@ -32,7 +32,7 @@ public class CommandAPI { } } - @Nullable RootLiteral getRegistered(String name) { + @Nullable Literal getRegistered(String name) { for (var node : registeredNodes) { for (String nodeName : node.getNames()) { if (nodeName.equals(name)) return node; @@ -41,7 +41,7 @@ public class CommandAPI { return null; } - public void register(RootLiteral rootNode) { + public void register(Literal rootNode) { registeredNodes.add(rootNode); commandMap.register(rootNode.getNames()[0], new BukkitKenCommand(commandHandler, rootNode)); } diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java index 5a1179d..977bc23 100644 --- a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java +++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/TabCompleteListener.java @@ -28,9 +28,9 @@ public class TabCompleteListener implements Listener { if (l >= 0) label = label.substring(l+1); } - var rootNode = commandAPI.getRegistered(label); - if (rootNode == null) return; + var literal = commandAPI.getRegistered(label); + if (literal == null) return; ev.setHandled(true); - ev.setCompletions(commandHandler.getSuggestions(rootNode, ev.getSender(), Arrays.copyOfRange(args, 1, args.length), ev.getBuffer().endsWith(" "))); + ev.setCompletions(commandHandler.getSuggestions(literal, ev.getSender(), Arrays.copyOfRange(args, 1, args.length), ev.getBuffer().endsWith(" "))); } } diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java index 3db53a4..f3010b2 100644 --- a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java +++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/suggestionprovider/PlayerSuggestionProvider.java @@ -7,9 +7,9 @@ import org.bukkit.entity.Player; import java.util.List; -public class PlayerSuggestionProvider implements SuggestionProvider { +public class PlayerSuggestionProvider implements SuggestionProvider { @Override - public List suggest(CommandSender __) { + public List suggest(T __) { return Bukkit.getOnlinePlayers().stream() .map(Player::getName) .toList(); diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java index 1b74400..17a8d94 100644 --- a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java +++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/OfflinePlayerArgumentType.java @@ -12,15 +12,12 @@ import org.bukkit.command.CommandSender; import java.util.UUID; -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class OfflinePlayerArgumentType implements ArgumentType { - - @Getter(lazy = true) - private static final OfflinePlayerArgumentType instance = new OfflinePlayerArgumentType(); +@NoArgsConstructor +public class OfflinePlayerArgumentType implements ArgumentType { // TODO make work with player names on offline players @Override - public OfflinePlayer parseInput(String string) { + public OfflinePlayer parseInputUnchecked(String string) { OfflinePlayer player = Bukkit.getPlayerExact(string); if (player == null) { try { @@ -34,7 +31,7 @@ public class OfflinePlayerArgumentType implements ArgumentType defaultSuggestionProvider() { - return new PlayerSuggestionProvider(); + public SuggestionProvider getDefaultSuggestionProvider() { + return new PlayerSuggestionProvider<>(); } } diff --git a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java index fe75d27..37a230a 100644 --- a/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java +++ b/kencommandapi-bukkit/src/main/java/de/kentoj/kencommandapi/type/PlayerArgumentType.java @@ -3,28 +3,22 @@ package de.kentoj.kencommandapi.type; import de.kentoj.kencommandapi.api.argument.ArgumentType; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider; import de.kentoj.kencommandapi.suggestionprovider.PlayerSuggestionProvider; -import lombok.AccessLevel; -import lombok.Getter; import lombok.NoArgsConstructor; import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class PlayerArgumentType implements ArgumentType { - - @Getter(lazy = true) - private static final PlayerArgumentType instance = new PlayerArgumentType(); +@NoArgsConstructor +public class PlayerArgumentType implements ArgumentType { @Override - public Player parseInput(String string) { + public Player parseInputUnchecked(String string) { var player = Bukkit.getPlayer(string); if (player == null) throw new IllegalArgumentException("player not online"); return player; } @Override - public SuggestionProvider defaultSuggestionProvider() { - return new PlayerSuggestionProvider(); + public SuggestionProvider getDefaultSuggestionProvider() { + return new PlayerSuggestionProvider<>(); } } diff --git a/kencommandapi-core/BUILD b/kencommandapi-core/BUILD index a92c507..442972e 100644 --- a/kencommandapi-core/BUILD +++ b/kencommandapi-core/BUILD @@ -6,13 +6,14 @@ java_library( srcs = glob(["src/main/**/*.java"]), visibility = ["//visibility:public"], deps = [ + artifact("org.projectlombok:lombok"), artifact("com.leakyabstractions:result-api"), artifact("com.leakyabstractions:result"), artifact("org.jetbrains:annotations"), artifact("com.google.guava:guava"), artifact("org.slf4j:slf4j-api"), artifact("net.kyori:adventure-api"), - artifact("org.projectlombok:lombok"), ], plugins = [ "//third_party:lombok_plugin" ], -) \ No newline at end of file +) +load("@rules_java//toolchains:default_java_toolchain.bzl", "default_java_toolchain") \ No newline at end of file diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java index e7d147d..02f8cc5 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/CommandHandler.java @@ -1,12 +1,11 @@ package de.kentoj.kencommandapi.api; -import de.kentoj.kencommandapi.api.invocation.CommandContext; -import de.kentoj.kencommandapi.api.literal.RootLiteral; +import de.kentoj.kencommandapi.api.literal.Literal; +import de.kentoj.kencommandapi.api.platform.HasPermissionMethod; +import de.kentoj.kencommandapi.api.platform.SendMessageMethod; import de.kentoj.kencommandapi.internal.parser.CommandParseHelper; import de.kentoj.kencommandapi.internal.parser.InvocationCommandParser; import de.kentoj.kencommandapi.internal.parser.SuggestionCommandParser; -import de.kentoj.kencommandapi.api.platform.HasPermissionMethod; -import de.kentoj.kencommandapi.api.platform.SendMessageMethod; import lombok.RequiredArgsConstructor; import java.util.Arrays; @@ -28,23 +27,25 @@ public class CommandHandler { suggestionParser = new SuggestionCommandParser<>(parseHelper); } - @SuppressWarnings("CodeBlock2Expr") - public void invoke(RootLiteral rootLiteral, T sender, Stream args) { + public void invoke(Literal rootLiteral, T sender, Stream args) { + // FIXME uses rootLiteral's message style invocationParser.parse(rootLiteral, sender, args.toList().iterator()) - .ifSuccessOrElse(data -> { - var ctx = new CommandContext<>(sender, data.getArguments()); - data.getExecutor().execute(ctx).whenComplete((result, ex) -> { - if (ex != null) - sendMessageMethod.send(sender, rootLiteral.getMessageStyle().exception(ex)); - result.ifFailure(err -> - sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err))); - }); - }, err -> { - sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err)); + .ifSuccessOrElse(data -> exec(rootLiteral, data), + err -> sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err))); + } + + private void exec(Literal rootLiteral, InvocationCommandParser.ExecutionData data) { + var sender = data.getContext().getSender(); + data.getExecutor().execute(data.getContext()) + .whenComplete((result, ex) -> { + if (ex != null) + sendMessageMethod.send(sender, rootLiteral.getMessageStyle().exception(ex)); + result.ifFailure(err -> + sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err))); }); } - public List getSuggestions(RootLiteral rootNode, T sender, String[] args, boolean trailingSpace) { + public List getSuggestions(Literal rootNode, T sender, String[] args, boolean trailingSpace) { return suggestionParser.suggest(rootNode, sender, Arrays.stream(args).iterator(), trailingSpace); } } \ No newline at end of file diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentRequirement.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentRequirement.java new file mode 100644 index 0000000..d43588e --- /dev/null +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentRequirement.java @@ -0,0 +1,14 @@ +package de.kentoj.kencommandapi.api.argument; + +import lombok.Value; + +import java.util.function.Predicate; + +/** + * @param type of the parsed argument value + */ +@Value +public class ArgumentRequirement { + Predicate predicate; + String message; +} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentType.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentType.java index 00cda59..73cb67b 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentType.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/ArgumentType.java @@ -2,14 +2,24 @@ package de.kentoj.kencommandapi.api.argument; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider; +import java.util.function.Predicate; + public interface ArgumentType { /** + * parses the given input and returns it. + * Unlike {@link CommandArgumentSpec#parseInput(String)}, this does not check any additional requirements. + * * @throws IllegalArgumentException if string invalid + * @see CommandArgumentSpec#parseInput(String) + * @see CommandArgumentSpecBuilder#addRequirement(Predicate, String) */ - V parseInput(String string); + V parseInputUnchecked(String input) throws IllegalArgumentException; - SuggestionProvider defaultSuggestionProvider(); + /** + * @see CommandArgumentSpec#getSuggestionProvider() + */ + SuggestionProvider getDefaultSuggestionProvider(); /** * @return whether this argument is greedy. Greedy arguments consume all remaining input instead of a single word. diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgument.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgument.java index 45967db..5c73f35 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgument.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgument.java @@ -1,30 +1,8 @@ package de.kentoj.kencommandapi.api.argument; -import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider; -import de.kentoj.kencommandapi.internal.CommandArgumentImpl; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.function.Function; - public interface CommandArgument { - static CommandArgument arg(String id, ArgumentType type) { - return new CommandArgumentImpl<>(id, type); - } + V getValue(); - String getId(); - - ArgumentType getType(); - - void setDefaultValueProvider(@Nullable Function defaultValueProvider); - - @Nullable Function getDefaultValueProvider(); - - void setSuggestionProvider(@Nullable SuggestionProvider suggestionProvider); - - /** - * @return the explicitly set SuggestionProvider or the default suggestions for the type if unset/null - */ - @NotNull SuggestionProvider getSuggestionProvider(); + CommandArgumentSpec getArgument(); } diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentImpl.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentImpl.java new file mode 100644 index 0000000..52fac15 --- /dev/null +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentImpl.java @@ -0,0 +1,9 @@ +package de.kentoj.kencommandapi.api.argument; + +import lombok.Value; + +@Value +public class CommandArgumentImpl implements CommandArgument { + V value; + CommandArgumentSpec argument; +} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpec.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpec.java new file mode 100644 index 0000000..9fc3709 --- /dev/null +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpec.java @@ -0,0 +1,32 @@ +package de.kentoj.kencommandapi.api.argument; + +import de.kentoj.kencommandapi.api.invocation.CommandContext; +import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; + +public sealed interface CommandArgumentSpec permits CommandArgumentSpecImpl { + + static CommandArgumentSpecBuilder builder(String id, ArgumentType type) { + return new CommandArgumentSpecBuilder<>(id, type); + } + + String getId(); + + ArgumentType getType(); + + /** + * @throws IllegalArgumentException if string invalid or if a requirement is not met + */ + V parseInput(String rawInput); + + @Nullable Function, V> getDefaultValueProvider(); + + /** + * @return the explicitly set SuggestionProvider or the default suggestions for the type if unset/null + */ + /* explicit @NotNull to avoid confusion */ + @NotNull SuggestionProvider getSuggestionProvider(); +} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecBuilder.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecBuilder.java new file mode 100644 index 0000000..64c7c6c --- /dev/null +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecBuilder.java @@ -0,0 +1,47 @@ +package de.kentoj.kencommandapi.api.argument; + +import de.kentoj.kencommandapi.api.invocation.CommandContext; +import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * @param type of the command sender + * @param type of the value + */ +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public class CommandArgumentSpecBuilder { + + private final String id; + private final ArgumentType type; + + private @Nullable Function, V> defaultValueProvider; + private @Nullable SuggestionProvider suggestionProvider; + + private final List> requirements = new ArrayList<>(); + + public CommandArgumentSpecBuilder withRequirement(Predicate predicate, String message) { + requirements.add(new ArgumentRequirement<>(predicate, message)); + return this; + } + + public CommandArgumentSpecBuilder withDefaultValue(@Nullable Function, V> defaultValueProvider) { + this.defaultValueProvider = defaultValueProvider; + return this; + } + + public CommandArgumentSpecBuilder withSuggestionProvider(@Nullable SuggestionProvider suggestionProvider) { + this.suggestionProvider = suggestionProvider; + return this; + } + + public CommandArgumentSpec build() { + return new CommandArgumentSpecImpl<>(id, type, requirements, defaultValueProvider, suggestionProvider); + } +} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecImpl.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecImpl.java new file mode 100644 index 0000000..83bd721 --- /dev/null +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/CommandArgumentSpecImpl.java @@ -0,0 +1,42 @@ +package de.kentoj.kencommandapi.api.argument; + +import de.kentoj.kencommandapi.api.invocation.CommandContext; +import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Function; + +@RequiredArgsConstructor +public final class CommandArgumentSpecImpl implements CommandArgumentSpec { + @Getter + private final String id; + @Getter + private final ArgumentType type; + private final List> requirements; + private final Function, V> defaultValueProvider; + private final @Nullable SuggestionProvider suggestionProvider; + + @Override + public V parseInput(String rawInput) { + var parsed = type.parseInputUnchecked(rawInput); + for (var requirement : requirements) { + if (!requirement.getPredicate().test(parsed)) + throw new IllegalArgumentException(requirement.getMessage()); + } + return parsed; + } + + @Override + public @Nullable Function, V> getDefaultValueProvider() { + return defaultValueProvider; + } + + @Override + public @NotNull SuggestionProvider getSuggestionProvider() { + return suggestionProvider != null ? suggestionProvider : type.getDefaultSuggestionProvider(); + } +} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/IntegerArgumentType.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/IntegerArgumentType.java index 80d6ee8..5f81e03 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/IntegerArgumentType.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/IntegerArgumentType.java @@ -2,17 +2,16 @@ package de.kentoj.kencommandapi.api.argument.types; import de.kentoj.kencommandapi.api.argument.ArgumentType; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider; -import de.kentoj.kencommandapi.api.suggestion.NoSuggestionProvider; public class IntegerArgumentType implements ArgumentType { @Override - public Integer parseInput(String string) { + public Integer parseInputUnchecked(String string) { return Integer.parseInt(string); } @Override - public SuggestionProvider defaultSuggestionProvider() { - return new NoSuggestionProvider<>(); + public SuggestionProvider getDefaultSuggestionProvider() { + return SuggestionProvider.none(); } } diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/StringArgumentType.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/StringArgumentType.java index 5ea49d5..39f8d95 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/StringArgumentType.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/argument/types/StringArgumentType.java @@ -2,7 +2,6 @@ package de.kentoj.kencommandapi.api.argument.types; import de.kentoj.kencommandapi.api.argument.ArgumentType; import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider; -import de.kentoj.kencommandapi.api.suggestion.NoSuggestionProvider; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -15,13 +14,13 @@ public class StringArgumentType implements ArgumentType { } @Override - public String parseInput(String string) { + public String parseInputUnchecked(String string) { return string; } @Override - public SuggestionProvider defaultSuggestionProvider() { - return new NoSuggestionProvider<>(); + public SuggestionProvider getDefaultSuggestionProvider() { + return SuggestionProvider.none(); } @Override diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandContext.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandContext.java index 8d8a248..30d7331 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandContext.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/CommandContext.java @@ -1,37 +1,37 @@ package de.kentoj.kencommandapi.api.invocation; import de.kentoj.kencommandapi.api.argument.CommandArgument; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; import java.util.Map; -@RequiredArgsConstructor -public class CommandContext { - @Getter - private final T sender; - @Getter - private final Map> parsedArguments; +public interface CommandContext { - public ParsedArgument getParsedArgument(String name) { - try { - //noinspection unchecked - return (ParsedArgument) parsedArguments.get(name); - } catch (ClassCastException e) { - throw new RuntimeException("requested argument with wrong type", e); - } - } + T getSender(); + CommandArgument getParsedArgument(String id); + + /** + * @return the value of the argument with the specified id + * @param the type of the value of the argument with the specified id + */ @SuppressWarnings("unchecked") - public V getArg(String name) { - return (V) getParsedArgument(name).getValue(); + default V getArg(String id) { + return (V) getParsedArgument(id).getValue(); } - public V getArg(CommandArgument arg) { + /** + * @param arg the argument whose value to return + * @return the value of the argument + * @param the type of the value of the argument + */ + default V getArg(CommandArgumentSpec arg) { return getArg(arg.getId()); } - public ParsedArgument getParsedArgument(CommandArgument arg) { + default CommandArgument getParsedArgument(CommandArgumentSpec arg) { return getParsedArgument(arg.getId()); } + + Map> getParsedArguments(); } diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/ParsedArgument.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/ParsedArgument.java deleted file mode 100644 index 6412823..0000000 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/invocation/ParsedArgument.java +++ /dev/null @@ -1,10 +0,0 @@ -package de.kentoj.kencommandapi.api.invocation; - -import de.kentoj.kencommandapi.api.argument.CommandArgument; - -public interface ParsedArgument { - - V getValue(); - - CommandArgument getArgument(); -} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java index b017837..1540474 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/Literal.java @@ -1,10 +1,9 @@ package de.kentoj.kencommandapi.api.literal; -import de.kentoj.kencommandapi.api.platform.MessageStyle; -import de.kentoj.kencommandapi.api.argument.CommandArgument; +import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; import de.kentoj.kencommandapi.api.invocation.CommandExecutor; -import de.kentoj.kencommandapi.internal.LiteralImpl; -import de.kentoj.kencommandapi.internal.RootLiteralImpl; +import de.kentoj.kencommandapi.api.invocation.SyncCommandExecutor; +import de.kentoj.kencommandapi.api.platform.MessageStyle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -12,51 +11,26 @@ import java.util.List; import java.util.Set; public interface Literal { - static Literal literal(String... name) { - return new LiteralImpl<>(name); - } - - static RootLiteral rootLiteral(MessageStyle messageStyle, String...name) { - return new RootLiteralImpl<>(messageStyle, name); + static LiteralBuilder builder(String... name) { + return new LiteralBuilder<>(name); } @NotNull String[] getNames(); - void setDescription(@Nullable String description); - + /** + * @return what the literal does + */ @Nullable String getDescription(); - /* - * arguments - */ - - /** - * @throws IllegalArgumentException if there is already an argument with the same id - * @throws IllegalStateException if a greedy argument was added to the literal previously - */ - void addArgument(CommandArgument argument); - - List> getArguments(); - - /* - * literals - */ - - void setExecutor(@Nullable CommandExecutor executor); + List> getArguments(); @Nullable CommandExecutor getExecutor(); - /** - * @throws IllegalArgumentException if there is already a literal with the same name - * @throws IllegalStateException if a greedy argument was added to the literal previously - */ - void addLiteral(@NotNull Literal literal); - @Nullable Literal getLiteral(@NotNull String name); - @NotNull Set> getLiterals(); + @NotNull List> getLiterals(); @Nullable String getPermission(); - void setPermission(@Nullable String permission); + MessageStyle getMessageStyle(); } diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralBuilder.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralBuilder.java new file mode 100644 index 0000000..263bf32 --- /dev/null +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralBuilder.java @@ -0,0 +1,100 @@ +package de.kentoj.kencommandapi.api.literal; + +import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; +import de.kentoj.kencommandapi.api.invocation.CommandExecutor; +import de.kentoj.kencommandapi.api.invocation.SyncCommandExecutor; +import de.kentoj.kencommandapi.api.platform.MessageStyle; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; + +public final class LiteralBuilder { + + private final String[] names; + private @Nullable String description; + private @Nullable CommandExecutor executor; + private @Nullable String permission; + private @Nullable MessageStyle messageStyle; + private final List> arguments = new ArrayList<>(); + private final List> literals = new ArrayList<>(); + + LiteralBuilder(String... names) { + checkArgument(names.length > 0, "at least one name is required"); + this.names = names; + } + + /** + * @param description what the literal does + */ + public LiteralBuilder withDescription(@Nullable String description) { + this.description = description; + return this; + } + + public LiteralBuilder withExecutor(@Nullable CommandExecutor executor) { + this.executor = executor; + return this; + } + + public LiteralBuilder withSyncExecutor(@Nullable SyncCommandExecutor executor) { + return withExecutor(executor); + } + + public LiteralBuilder withPermission(@Nullable String permission) { + this.permission = permission; + return this; + } + + public LiteralBuilder withStyle(MessageStyle messageStyle) { + this.messageStyle = messageStyle; + return this; + } + + /** + * @throws IllegalArgumentException if there is already an argument with the same id + * @throws IllegalStateException if a greedy argument was added to the literal previously + */ + public LiteralBuilder withArgument(CommandArgumentSpec argument) { + checkArgument(!hasGreedyArg(), "cannot add an argument after a greedy argument"); + if (arguments.stream().anyMatch(arg -> arg.getId().equals(argument.getId()))) + throw new IllegalArgumentException("cannot add multiple arguments with the same id"); + + this.arguments.add(argument); + return this; + } + + /** + * @throws IllegalArgumentException if there is already a literal with the same name + * @throws IllegalStateException if a greedy argument was added to the literal previously + */ + public LiteralBuilder withLiteral(Literal literal) { + checkArgument(!hasGreedyArg(), "cannot add a literal after a greedy argument"); + for (String name : literal.getNames()) + for (var l : literals) + if (l.getLiteral(name) != null) + throw new IllegalArgumentException("literal with same name already exists"); + + this.literals.add(literal); + return this; + } + + public Literal build() { + return new LiteralImpl<>( + names, + literals, + arguments, + executor, + permission, + messageStyle == null ? MessageStyle.PLAIN : messageStyle + ); + } + + private boolean hasGreedyArg() { + if (arguments.isEmpty()) return false; + var arg = arguments.getLast(); + return arg != null && arg.getType().isGreedy(); + } +} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralImpl.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralImpl.java new file mode 100644 index 0000000..3dc31ea --- /dev/null +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/LiteralImpl.java @@ -0,0 +1,38 @@ +package de.kentoj.kencommandapi.api.literal; + +import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; +import de.kentoj.kencommandapi.api.invocation.CommandExecutor; +import de.kentoj.kencommandapi.api.platform.MessageStyle; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public class LiteralImpl implements Literal { + private final String[] names; + private final List> literals; + private final List> arguments; + private final CommandExecutor executor; + /** + * what the literal does + */ + private String description; + private final @Nullable String permission; + private final MessageStyle messageStyle; + + @Override + public @Nullable Literal getLiteral(@NotNull String name) { + for (Literal literal : literals) { + for (var literalName : literal.getNames()) { + if (literalName.equalsIgnoreCase(name)) + return literal; + } + } + return null; + } +} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/RootLiteral.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/RootLiteral.java deleted file mode 100644 index cefcbee..0000000 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/literal/RootLiteral.java +++ /dev/null @@ -1,10 +0,0 @@ -package de.kentoj.kencommandapi.api.literal; - -import de.kentoj.kencommandapi.api.platform.MessageStyle; - -public interface RootLiteral extends Literal { - - MessageStyle getMessageStyle(); - - void setMessageStyle(MessageStyle messageStyle); -} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/PlainMessageStyle.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/PlainMessageStyle.java index ef35a0c..88f24da 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/PlainMessageStyle.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/platform/PlainMessageStyle.java @@ -1,8 +1,11 @@ package de.kentoj.kencommandapi.api.platform; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import net.kyori.adventure.text.Component; -public class PlainMessageStyle implements MessageStyle { +@NoArgsConstructor(access = AccessLevel.PACKAGE) +class PlainMessageStyle implements MessageStyle { @Override public Component ok(Component msg) { return msg; diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/NoSuggestionProvider.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/NoSuggestionProvider.java index 89a9ea7..080ae12 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/NoSuggestionProvider.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/NoSuggestionProvider.java @@ -1,10 +1,14 @@ package de.kentoj.kencommandapi.api.suggestion; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; -public class NoSuggestionProvider implements SuggestionProvider { +@NoArgsConstructor(access = AccessLevel.PACKAGE) +class NoSuggestionProvider implements SuggestionProvider { @Override public List suggest(T __) { diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/SuggestionProvider.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/SuggestionProvider.java index 68c7d58..135f3aa 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/SuggestionProvider.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/api/suggestion/SuggestionProvider.java @@ -1,10 +1,13 @@ package de.kentoj.kencommandapi.api.suggestion; import java.util.List; -import java.util.concurrent.CompletableFuture; public interface SuggestionProvider { + static SuggestionProvider none() { + return new NoSuggestionProvider<>(); + } + /** * @return an unfiltered(no permission checks or filtering by input) list of strings that may be used for the argument/literal */ diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandArgumentImpl.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandArgumentImpl.java deleted file mode 100644 index 88fa1f1..0000000 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandArgumentImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -package de.kentoj.kencommandapi.internal; - -import de.kentoj.kencommandapi.api.argument.ArgumentType; -import de.kentoj.kencommandapi.api.argument.CommandArgument; -import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.function.Function; - -@RequiredArgsConstructor -public class CommandArgumentImpl implements CommandArgument { - - @Getter - private final String id; - @Getter - private final ArgumentType type; - - @Getter - @Setter - private @Nullable Function defaultValueProvider = null; - @Setter - private @Nullable SuggestionProvider suggestionProvider; - - @Override - public @NotNull SuggestionProvider getSuggestionProvider() { - if (suggestionProvider == null) - return getType().defaultSuggestionProvider(); - return this.suggestionProvider; - } -} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandContextImpl.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandContextImpl.java new file mode 100644 index 0000000..d8c0bc2 --- /dev/null +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/CommandContextImpl.java @@ -0,0 +1,26 @@ +package de.kentoj.kencommandapi.internal; + +import de.kentoj.kencommandapi.api.argument.CommandArgument; +import de.kentoj.kencommandapi.api.invocation.CommandContext; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Map; + +@RequiredArgsConstructor +public class CommandContextImpl implements CommandContext { + @Getter + private final T sender; + @Getter + private final Map> parsedArguments; + + @Override + public CommandArgument getParsedArgument(String id) { + try { + //noinspection unchecked + return (CommandArgument) parsedArguments.get(id); + } catch (ClassCastException e) { + throw new RuntimeException("requested argument with wrong type", e); + } + } +} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/LiteralImpl.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/LiteralImpl.java deleted file mode 100644 index e2c0169..0000000 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/LiteralImpl.java +++ /dev/null @@ -1,72 +0,0 @@ -package de.kentoj.kencommandapi.internal; - -import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.kencommandapi.api.argument.CommandArgument; -import de.kentoj.kencommandapi.api.invocation.CommandExecutor; -import lombok.Getter; -import lombok.Setter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class LiteralImpl implements Literal { - - @Getter - private final String[] names; - @Getter - private final Set> literals = new HashSet<>(); - @Getter - private final List> arguments = new ArrayList<>(); - - @Getter - @Setter - private @Nullable CommandExecutor executor; - - @Getter - @Setter - private String description; - - @Getter - @Setter - private @Nullable String permission = null; - - public LiteralImpl(String[] names) { - if (names.length == 0) throw new IllegalArgumentException("at least one name is required"); - this.names = names; - } - - @Override - public void addLiteral(@NotNull Literal literal) { - if (isGreedy()) throw new IllegalStateException("can't add literal to a node with a greedy argument"); - for (String name : literal.getNames()) - if (getLiteral(name) != null) - throw new IllegalArgumentException("literal with same name already exists"); - literals.add(literal); - } - - @Override - public Literal getLiteral(@NotNull String name) { - for (var literal : literals) - for (String literalName : literal.getNames()) - if (literalName.equalsIgnoreCase(name)) return literal; - return null; - } - - @Override - public void addArgument(CommandArgument argument) { - if (isGreedy()) throw new IllegalStateException("can't add literal to a node with a greedy argument"); - if (arguments.stream().anyMatch(arg -> arg.getId().equals(argument.getId()))) - throw new IllegalArgumentException("argument with same identifier is already added"); - arguments.add(argument); - } - - private boolean isGreedy() { - if (arguments.isEmpty()) return false; - var arg = arguments.getLast(); - return arg != null && arg.getType().isGreedy(); - } -} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/ParsedArgumentImpl.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/ParsedArgumentImpl.java deleted file mode 100644 index 9d98210..0000000 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/ParsedArgumentImpl.java +++ /dev/null @@ -1,11 +0,0 @@ -package de.kentoj.kencommandapi.internal; - -import de.kentoj.kencommandapi.api.argument.CommandArgument; -import de.kentoj.kencommandapi.api.invocation.ParsedArgument; -import lombok.Value; - -@Value -public class ParsedArgumentImpl implements ParsedArgument { - V value; - CommandArgument argument; -} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/RootLiteralImpl.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/RootLiteralImpl.java deleted file mode 100644 index cdbf772..0000000 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/RootLiteralImpl.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.kentoj.kencommandapi.internal; - -import de.kentoj.kencommandapi.api.platform.MessageStyle; -import de.kentoj.kencommandapi.api.literal.RootLiteral; -import lombok.Getter; -import lombok.Setter; - -public class RootLiteralImpl extends LiteralImpl implements RootLiteral { - @Setter - @Getter - private MessageStyle messageStyle; - - public RootLiteralImpl(MessageStyle messageStyle, String[] names) { - super(names); - this.messageStyle = messageStyle; - } -} diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/InvocationCommandParser.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/InvocationCommandParser.java index 3d06229..7fe1fa5 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/InvocationCommandParser.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/InvocationCommandParser.java @@ -2,12 +2,13 @@ package de.kentoj.kencommandapi.internal.parser; import com.leakyabstractions.result.api.Result; import com.leakyabstractions.result.core.Results; -import de.kentoj.kencommandapi.api.argument.CommandArgument; +import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec; +import de.kentoj.kencommandapi.api.invocation.CommandContext; import de.kentoj.kencommandapi.api.invocation.CommandExecutor; -import de.kentoj.kencommandapi.api.invocation.ParsedArgument; +import de.kentoj.kencommandapi.api.argument.CommandArgument; import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.kencommandapi.api.literal.RootLiteral; -import de.kentoj.kencommandapi.internal.ParsedArgumentImpl; +import de.kentoj.kencommandapi.api.argument.CommandArgumentImpl; +import de.kentoj.kencommandapi.internal.CommandContextImpl; import lombok.RequiredArgsConstructor; import lombok.Value; @@ -20,17 +21,17 @@ public class InvocationCommandParser { private final CommandParseHelper helper; public Result parse( - RootLiteral rootLiteral, + Literal rootLiteral, T sender, Iterator tokenIter ) { Literal currentLiteral = rootLiteral; - Map> parsedArguments = new HashMap<>(); + var ctx = new CommandContextImpl<>(sender, new HashMap<>()); while (true) { { - var result = processArguments(currentLiteral, sender, tokenIter) - .ifSuccess(parsedArguments::putAll); + var result = processArguments(currentLiteral, ctx, tokenIter) + .ifSuccess(ctx.getParsedArguments()::putAll); if (result.hasFailure()) return result.mapSuccess(__ -> null); } @@ -49,35 +50,35 @@ public class InvocationCommandParser { currentLiteral = nextLiteral; } - return Results.success(new ExecutionData(currentLiteral.getExecutor(), parsedArguments)); + return Results.success(new ExecutionData(currentLiteral.getExecutor(), ctx)); } - private Result>, String> processArguments( + private Result>, String> processArguments( Literal literal, - T sender, + CommandContext ctx, Iterator tokenIter ) { - Map> parsedArguments = new HashMap<>(); + Map> parsedArguments = new HashMap<>(); for (var _argument : literal.getArguments()) { @SuppressWarnings("unchecked") - var argument = ((CommandArgument) _argument); + var argument = ((CommandArgumentSpec) _argument); Object value; if (!tokenIter.hasNext()) { if (argument.getDefaultValueProvider() == null) return Results.failure("Argument expected: " + argument.getId()); - value = argument.getDefaultValueProvider().apply(sender); + value = argument.getDefaultValueProvider().apply(ctx); } else { var input = helper.parseInput(tokenIter, argument.getType().isGreedy()); try { - value = argument.getType().parseInput(input); + value = argument.parseInput(input); } catch (IllegalArgumentException ex) { return Results.failure("Illegal argument '" + input + "' for " + argument.getId() + ": " + ex.getMessage()); } } - parsedArguments.put(argument.getId(), new ParsedArgumentImpl<>(value, argument)); + parsedArguments.put(argument.getId(), new CommandArgumentImpl<>(value, argument)); } return Results.success(parsedArguments); @@ -86,6 +87,6 @@ public class InvocationCommandParser { @Value public class ExecutionData { CommandExecutor executor; - Map> arguments; + CommandContext context; } } diff --git a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/SuggestionCommandParser.java b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/SuggestionCommandParser.java index fa84f3e..a83b23f 100644 --- a/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/SuggestionCommandParser.java +++ b/kencommandapi-core/src/main/java/de/kentoj/kencommandapi/internal/parser/SuggestionCommandParser.java @@ -1,7 +1,6 @@ package de.kentoj.kencommandapi.internal.parser; import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.kencommandapi.api.literal.RootLiteral; import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.Nullable; @@ -16,7 +15,7 @@ public class SuggestionCommandParser { private final CommandParseHelper helper; public List suggest( - RootLiteral rootLiteral, + Literal rootLiteral, T sender, Iterator tokenIter, boolean suggestNext diff --git a/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParserTest.java b/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParserTest.java index 0a87380..37c5725 100644 --- a/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParserTest.java +++ b/kencommandapi-core/src/test/java/de/kentoj/kencommandapi/api/parser/SuggestionCommandParserTest.java @@ -3,7 +3,7 @@ package de.kentoj.kencommandapi.api.parser; import de.kentoj.kencommandapi.api.argument.CommandArgument; import de.kentoj.kencommandapi.api.argument.types.StringArgumentType; import de.kentoj.kencommandapi.api.literal.Literal; -import de.kentoj.kencommandapi.api.literal.RootLiteral; +import de.kentoj.kencommandapi.api.literal.Literal; import de.kentoj.kencommandapi.api.platform.MessageStyle; import de.kentoj.kencommandapi.internal.parser.SuggestionCommandParser; import org.junit.jupiter.api.Test; @@ -18,7 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SuggestionCommandParserTest { private final SuggestionCommandParser suggestionParser; - private final RootLiteral rootLiteral; + private final Literal rootLiteral; public SuggestionCommandParserTest() { rootLiteral = Literal.rootLiteral(MessageStyle.PLAIN, "someRootNode"); diff --git a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/CommandAPI.java b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/CommandAPI.java index 3a05c67..cf0aacb 100644 --- a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/CommandAPI.java +++ b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/CommandAPI.java @@ -3,7 +3,7 @@ package de.kentoj.kencommandapi; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.proxy.ProxyServer; import de.kentoj.kencommandapi.api.CommandHandler; -import de.kentoj.kencommandapi.api.literal.RootLiteral; +import de.kentoj.kencommandapi.api.literal.Literal; import lombok.RequiredArgsConstructor; import java.util.Arrays; @@ -15,7 +15,7 @@ public class CommandAPI { private final CommandHandler commandHandler = new CommandHandler<>(CommandSource::sendMessage, CommandSource::hasPermission); - public void register(RootLiteral rootNode) { + public void register(Literal rootNode) { var cm = server.getCommandManager(); var meta = cm.metaBuilder(rootNode.getNames()[0]) .aliases(Arrays.copyOfRange(rootNode.getNames(), 1, rootNode.getNames().length)) diff --git a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java index c002669..6068f0c 100644 --- a/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java +++ b/kencommandapi-velocity/src/main/java/de/kentoj/kencommandapi/VelocityCommand.java @@ -3,7 +3,7 @@ package de.kentoj.kencommandapi; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.RawCommand; import de.kentoj.kencommandapi.api.CommandHandler; -import de.kentoj.kencommandapi.api.literal.RootLiteral; +import de.kentoj.kencommandapi.api.literal.Literal; import lombok.RequiredArgsConstructor; import java.util.Arrays; @@ -13,7 +13,7 @@ import java.util.List; public class VelocityCommand implements RawCommand { private final CommandHandler commandHandler; - private final RootLiteral rootNode; + private final Literal rootNode; @Override public void execute(Invocation invocation) { diff --git a/third_party/BUILD b/third_party/BUILD index 4149a84..bcfec20 100644 --- a/third_party/BUILD +++ b/third_party/BUILD @@ -4,5 +4,4 @@ java_plugin( visibility = ["//visibility:public"], deps = ["@maven//:org_projectlombok_lombok"], processor_class = "lombok.launch.AnnotationProcessorHider$AnnotationProcessor", - generates_api = True, )