This commit is contained in:
commit
7c322707ba
51 changed files with 1488 additions and 0 deletions
7
.bazelrc
Normal file
7
.bazelrc
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
build --java_language_version=25
|
||||||
|
build --java_runtime_version=remotejdk_25
|
||||||
|
build --tool_java_language_version=25
|
||||||
|
build --tool_java_runtime_version=remotejdk_25
|
||||||
|
build --define=maven_repo=https://git.lab0x13.site/api/packages/scrow/maven
|
||||||
|
#build --remote_cache=https://cache.lab0x13.site:443
|
||||||
|
#build --remote_upload_local_results=false
|
||||||
1
.bazelversion
Normal file
1
.bazelversion
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
9.2.0
|
||||||
31
.forgejo/workflows/publish-artifact.yml
Normal file
31
.forgejo/workflows/publish-artifact.yml
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
name: "Build and Deploy artifact"
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master, main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: "git.lab0x13.site/scrow/bazel-java:0.12"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
- uses: actions/cache@v6
|
||||||
|
with:
|
||||||
|
path: /var/cache/bazel
|
||||||
|
key: bazel-${{ env.FORGEJO_REPOSITORY }}
|
||||||
|
- id: deploy
|
||||||
|
env:
|
||||||
|
CACHE_USERNAME: ${{ secrets.CACHE_USERNAME }}
|
||||||
|
CACHE_PASSWORD: ${{ secrets.CACHE_PASSWORD }}
|
||||||
|
MAVEN_USER: ${{ secrets.MAVENREPO_USERNAME }}
|
||||||
|
MAVEN_PASSWORD: ${{ secrets.MAVENREPO_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
for target in //bukkit:bukkit.publish //core:core.publish
|
||||||
|
do
|
||||||
|
bazel \
|
||||||
|
--output_user_root=/var/cache/bazel \
|
||||||
|
run "$target" \
|
||||||
|
--remote_cache="https://${CACHE_USERNAME}:${CACHE_PASSWORD}@cache.lab0x13.site:443" \
|
||||||
|
--remote_upload_local_results \
|
||||||
|
done
|
||||||
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
.bazelbsp/
|
||||||
|
.idea/
|
||||||
|
bazel-bin
|
||||||
|
bazel-genfiles
|
||||||
|
bazel-out
|
||||||
|
bazel-testlogs
|
||||||
0
BUILD
Normal file
0
BUILD
Normal file
39
MODULE.bazel
Normal file
39
MODULE.bazel
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
module(name = "scrow_commands")
|
||||||
|
|
||||||
|
bazel_dep(name = "rules_java", version = "9.7.0")
|
||||||
|
bazel_dep(name = "rules_jvm_external", version = "7.1")
|
||||||
|
bazel_dep(name = "contrib_rules_jvm", version = "0.34.0")
|
||||||
|
bazel_dep(name = "apple_rules_lint", version = "0.4.0")
|
||||||
|
|
||||||
|
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
|
||||||
|
maven.install(
|
||||||
|
name = "maven",
|
||||||
|
artifacts = [
|
||||||
|
"net.kyori:adventure-api:5.2.0",
|
||||||
|
"org.jetbrains:annotations:26.0.2-1",
|
||||||
|
"com.google.guava:guava:33.5.0-jre",
|
||||||
|
"com.leakyabstractions:result-api:1.0.1.0",
|
||||||
|
"com.leakyabstractions:result:1.0.2.0",
|
||||||
|
"io.papermc.paper:paper-api:[26.2.build,)",
|
||||||
|
|
||||||
|
# TESTS
|
||||||
|
"net.kyori:adventure-text-serializer-plain:5.2.0",
|
||||||
|
"org.junit.jupiter:junit-jupiter-engine",
|
||||||
|
"org.junit.jupiter:junit-jupiter-api",
|
||||||
|
"org.junit.platform:junit-platform-launcher",
|
||||||
|
"org.junit.platform:junit-platform-reporting",
|
||||||
|
],
|
||||||
|
boms = [
|
||||||
|
"org.junit:junit-bom:5.14.4",
|
||||||
|
],
|
||||||
|
repositories = [
|
||||||
|
"https://repo1.maven.org/maven2",
|
||||||
|
"https://repo.papermc.io/repository/maven-public/",
|
||||||
|
],
|
||||||
|
known_contributing_modules = ["protobuf"],
|
||||||
|
)
|
||||||
|
use_repo(maven, "maven")
|
||||||
|
|
||||||
|
linter = use_extension("@apple_rules_lint//lint:extensions.bzl", "linter")
|
||||||
|
linter.configure(name = "java-spotbugs", config = "@contrib_rules_jvm//java:spotbugs-default-config")
|
||||||
|
use_repo(linter, "apple_linters")
|
||||||
23
bukkit/BUILD.bazel
Normal file
23
bukkit/BUILD.bazel
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
load("@rules_jvm_external//:defs.bzl", "artifact", "java_export")
|
||||||
|
load("//:defs.bzl", "GROUP", "VERSION")
|
||||||
|
|
||||||
|
java_export(
|
||||||
|
name = "bukkit",
|
||||||
|
maven_coordinates = GROUP + ":commands-bukkit:" + VERSION,
|
||||||
|
srcs = glob(["site/lab0x13/scrow/commands/**/*.java"]),
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//common:common-lib",
|
||||||
|
artifact("org.jetbrains:annotations"),
|
||||||
|
artifact("com.google.guava:guava"),
|
||||||
|
artifact("net.kyori:adventure-api"),
|
||||||
|
artifact("com.leakyabstractions:result-api"),
|
||||||
|
artifact("com.leakyabstractions:result"),
|
||||||
|
artifact("io.papermc.paper:paper-api"),
|
||||||
|
],
|
||||||
|
exports = [
|
||||||
|
"//common:common-lib",
|
||||||
|
artifact("com.leakyabstractions:result-api"),
|
||||||
|
artifact("com.leakyabstractions:result"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package site.lab0x13.scrow.commands.bukkit;
|
||||||
|
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import site.lab0x13.scrow.commands.model.AbstractCommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
|
||||||
|
public class BukkitCommandContext<C extends CommandContext<C>> extends AbstractCommandContext<C> {
|
||||||
|
|
||||||
|
private final CommandSender sender;
|
||||||
|
|
||||||
|
public BukkitCommandContext(CommandSender sender) {
|
||||||
|
this.sender = sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandSender sender() {
|
||||||
|
return sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player player() {
|
||||||
|
return (Player) sender();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package site.lab0x13.scrow.commands.bukkit;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import site.lab0x13.scrow.commands.PlatformAdapter;
|
||||||
|
|
||||||
|
class BukkitPlatform<C extends BukkitCommandContext<C>> implements PlatformAdapter<C> {
|
||||||
|
@Override
|
||||||
|
public boolean checkPermission(C ctx, String permission) {
|
||||||
|
return ctx.sender().hasPermission(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(C ctx, Component message) {
|
||||||
|
ctx.sender().sendMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package site.lab0x13.scrow.commands.bukkit;
|
||||||
|
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import site.lab0x13.scrow.commands.CommandHandler;
|
||||||
|
import site.lab0x13.scrow.commands.model.literal.RootLiteral;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class BukkitScrowCommand<C extends BukkitCommandContext<C>> extends Command {
|
||||||
|
|
||||||
|
private final RootLiteral<C> rootLiteral;
|
||||||
|
private final CommandContextFactory<C> commandContextFactory;
|
||||||
|
private final CommandHandler<C> commandHandler;
|
||||||
|
|
||||||
|
protected BukkitScrowCommand(CommandHandler<C> commandHandler, CommandContextFactory<C> commandContextFactory, RootLiteral<C> rootLiteral) {
|
||||||
|
super(rootLiteral.names().getFirst());
|
||||||
|
this.commandHandler = commandHandler;
|
||||||
|
this.commandContextFactory = commandContextFactory;
|
||||||
|
this.rootLiteral = rootLiteral;
|
||||||
|
|
||||||
|
this.setAliases(rootLiteral.names());
|
||||||
|
this.setPermission(rootLiteral.permission());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(@NotNull CommandSender commandSender, @NotNull String commandLabel, String @NotNull [] args) {
|
||||||
|
commandHandler.invoke(
|
||||||
|
rootLiteral,
|
||||||
|
commandContextFactory.createContext(commandSender),
|
||||||
|
Stream.of(args).filter(str -> !str.isEmpty())
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package site.lab0x13.scrow.commands.bukkit;
|
||||||
|
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface CommandContextFactory<C extends BukkitCommandContext<C>> {
|
||||||
|
|
||||||
|
C createContext(CommandSender sender);
|
||||||
|
}
|
||||||
50
bukkit/site/lab0x13/scrow/commands/bukkit/ScrowCommands.java
Normal file
50
bukkit/site/lab0x13/scrow/commands/bukkit/ScrowCommands.java
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
package site.lab0x13.scrow.commands.bukkit;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.SimpleCommandMap;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.CommandHandler;
|
||||||
|
import site.lab0x13.scrow.commands.IScrowCommands;
|
||||||
|
import site.lab0x13.scrow.commands.model.literal.RootLiteral;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ScrowCommands<C extends BukkitCommandContext<C>> implements IScrowCommands<C> {
|
||||||
|
|
||||||
|
private final CommandContextFactory<C> commandContextFactory;
|
||||||
|
private final List<RootLiteral<C>> rootLiterals = new ArrayList<>();
|
||||||
|
private final SimpleCommandMap commandMap;
|
||||||
|
private final CommandHandler<C> commandHandler;
|
||||||
|
|
||||||
|
public ScrowCommands(Plugin plugin, CommandContextFactory<C> commandContextFactory) {
|
||||||
|
try {
|
||||||
|
Field field = Bukkit.getPluginManager().getClass().getDeclaredField("commandMap");
|
||||||
|
field.setAccessible(true);
|
||||||
|
commandMap = (SimpleCommandMap) field.get(Bukkit.getPluginManager());
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||||
|
throw new IllegalStateException("failed getting command map", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.commandContextFactory = commandContextFactory;
|
||||||
|
this.commandHandler = new CommandHandler<C>(new BukkitPlatform<>());
|
||||||
|
Bukkit.getPluginManager().registerEvents(new TabCompleteListener<>(this, commandHandler, commandContextFactory), plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void register(RootLiteral<C> rootLiteral) {
|
||||||
|
rootLiterals.add(rootLiteral);
|
||||||
|
commandMap.register(rootLiteral.names().getFirst(),
|
||||||
|
new BukkitScrowCommand<>(commandHandler, commandContextFactory, rootLiteral));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable RootLiteral<C> getRootLiteral(String name) {
|
||||||
|
return rootLiterals.stream()
|
||||||
|
.filter(literal -> literal.names().contains(name))
|
||||||
|
.findAny()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package site.lab0x13.scrow.commands.bukkit;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import site.lab0x13.scrow.commands.CommandHandler;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class TabCompleteListener<C extends BukkitCommandContext<C>> implements Listener {
|
||||||
|
|
||||||
|
private final ScrowCommands<C> commandAPI;
|
||||||
|
private final CommandHandler<C> commandHandler;
|
||||||
|
private final CommandContextFactory<C> commandContextFactory;
|
||||||
|
|
||||||
|
public TabCompleteListener(ScrowCommands<C> commandAPI, CommandHandler<C> commandHandler, CommandContextFactory<C> commandContextFactory) {
|
||||||
|
this.commandAPI = commandAPI;
|
||||||
|
this.commandHandler = commandHandler;
|
||||||
|
this.commandContextFactory = commandContextFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true)
|
||||||
|
public void onAsyncTabComplete(AsyncTabCompleteEvent ev) {
|
||||||
|
if (!ev.isCommand()) return;
|
||||||
|
var args = ev.getBuffer().substring(1).split(" ");
|
||||||
|
if (args.length == 0) return;
|
||||||
|
|
||||||
|
var label = args[0];
|
||||||
|
{
|
||||||
|
// commands can be called with a '<plugin name>:' prefix. strip it.
|
||||||
|
int l = label.indexOf(':');
|
||||||
|
if (l >= 0)
|
||||||
|
label = label.substring(l + 1);
|
||||||
|
}
|
||||||
|
var literal = commandAPI.getRootLiteral(label);
|
||||||
|
if (literal == null) return;
|
||||||
|
|
||||||
|
ev.setHandled(true);
|
||||||
|
ev.setCompletions(commandHandler.suggest(
|
||||||
|
literal,
|
||||||
|
commandContextFactory.createContext(ev.getSender()),
|
||||||
|
Arrays.copyOfRange(args, 1, args.length),
|
||||||
|
ev.getBuffer().endsWith(" ")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
12
common/BUILD.bazel
Normal file
12
common/BUILD.bazel
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
load("@rules_jvm_external//:defs.bzl", "java_export")
|
||||||
|
load("//common:deps.bzl", "COMMON_DEPS", "COMMON_DEPS_EXPORT")
|
||||||
|
load("//:defs.bzl", "GROUP", "VERSION")
|
||||||
|
|
||||||
|
java_export(
|
||||||
|
name = "common",
|
||||||
|
maven_coordinates = GROUP + ":commands-common:" + VERSION,
|
||||||
|
srcs = glob(["site/lab0x13/scrow/**/*.java"]),
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = COMMON_DEPS,
|
||||||
|
exports = COMMON_DEPS_EXPORT,
|
||||||
|
)
|
||||||
14
common/deps.bzl
Normal file
14
common/deps.bzl
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
load("@rules_jvm_external//:defs.bzl", "artifact")
|
||||||
|
|
||||||
|
COMMON_DEPS = [
|
||||||
|
artifact("org.jetbrains:annotations"),
|
||||||
|
artifact("com.google.guava:guava"),
|
||||||
|
artifact("net.kyori:adventure-api"),
|
||||||
|
artifact("com.leakyabstractions:result-api"),
|
||||||
|
artifact("com.leakyabstractions:result"),
|
||||||
|
]
|
||||||
|
|
||||||
|
COMMON_DEPS_EXPORT = [
|
||||||
|
artifact("com.leakyabstractions:result-api"),
|
||||||
|
artifact("com.leakyabstractions:result"),
|
||||||
|
]
|
||||||
67
common/site/lab0x13/scrow/commands/CommandHandler.java
Normal file
67
common/site/lab0x13/scrow/commands/CommandHandler.java
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
package site.lab0x13.scrow.commands;
|
||||||
|
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.literal.RootLiteral;
|
||||||
|
import site.lab0x13.scrow.commands.parser.NodeTreeWalker;
|
||||||
|
import site.lab0x13.scrow.commands.parser.WalkResult;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
public class CommandHandler<C extends CommandContext<C>> {
|
||||||
|
|
||||||
|
private final NodeTreeWalker<C> nodeTreeWalker;
|
||||||
|
private final PlatformAdapter<C> platformAdapter;
|
||||||
|
|
||||||
|
public CommandHandler(PlatformAdapter<C> platformAdapter) {
|
||||||
|
this.nodeTreeWalker = new NodeTreeWalker<>(platformAdapter);
|
||||||
|
this.platformAdapter = platformAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invoke(RootLiteral<C> rootLiteral, C ctx, Stream<String> args) {
|
||||||
|
var walkResult = nodeTreeWalker.walk(ctx, rootLiteral, args.iterator());
|
||||||
|
if (!walkResult.isValidUsage()) {
|
||||||
|
platformAdapter.sendMessage(ctx, rootLiteral.style().err(walkResult.message()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requireNonNull(walkResult.lastLiteral().executor())
|
||||||
|
.execute(walkResult.ctx())
|
||||||
|
.exceptionally(ex -> {
|
||||||
|
platformAdapter.sendMessage(ctx, rootLiteral.style().exception(ex));
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> suggest(RootLiteral<C> rootLiteral, C ctx, String[] args, boolean trailingSpace) {
|
||||||
|
var walkResult = nodeTreeWalker.walk(ctx, rootLiteral, Arrays.stream(args).iterator());
|
||||||
|
Stream<String> stream;
|
||||||
|
String input = null;
|
||||||
|
switch (walkResult) {
|
||||||
|
case WalkResult.LiteralUnknown<C> res -> {
|
||||||
|
stream = res.lastLiteral().subLiteralNames().stream();
|
||||||
|
input = res.input();
|
||||||
|
}
|
||||||
|
case WalkResult.ArgumentIllegal<C> res -> {
|
||||||
|
stream = res.arg().suggestionProvider().suggest(ctx, res.input()).stream();
|
||||||
|
input = res.input();
|
||||||
|
}
|
||||||
|
case WalkResult.LiteralExpected<C> res when trailingSpace ->
|
||||||
|
stream = res.lastLiteral().subLiteralNames().stream();
|
||||||
|
case WalkResult.ArgumentExpected<C> res when trailingSpace ->
|
||||||
|
stream = res.arg().suggestionProvider().suggest(ctx, "").stream();
|
||||||
|
default -> {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input == null) return stream.toList();
|
||||||
|
final var finalInput = input;
|
||||||
|
return stream
|
||||||
|
.filter(s -> s.startsWith(finalInput))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
12
common/site/lab0x13/scrow/commands/IScrowCommands.java
Normal file
12
common/site/lab0x13/scrow/commands/IScrowCommands.java
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
package site.lab0x13.scrow.commands;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.literal.RootLiteral;
|
||||||
|
|
||||||
|
public interface IScrowCommands<C extends CommandContext<C>> {
|
||||||
|
|
||||||
|
void register(RootLiteral<C> rootLiteral);
|
||||||
|
|
||||||
|
@Nullable RootLiteral<C> getRootLiteral(String name);
|
||||||
|
}
|
||||||
10
common/site/lab0x13/scrow/commands/PlatformAdapter.java
Normal file
10
common/site/lab0x13/scrow/commands/PlatformAdapter.java
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
package site.lab0x13.scrow.commands;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
|
||||||
|
public interface PlatformAdapter<C> {
|
||||||
|
|
||||||
|
boolean checkPermission(C ctx, String permission);
|
||||||
|
|
||||||
|
void sendMessage(C ctx, Component message);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package site.lab0x13.scrow.commands.model;
|
||||||
|
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.Argument;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.ParsedArgument;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public abstract class AbstractCommandContext<C extends CommandContext<C>> implements CommandContext<C> {
|
||||||
|
|
||||||
|
private final Map<String, ParsedArgument<?, ?>> parsedArguments = new HashMap<>();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <V> ParsedArgument<C, V> getArgument(String id) {
|
||||||
|
return (ParsedArgument<C, V>) parsedArguments.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> void addParsedArgument(Argument<?, V> argument, String rawValue, V value) {
|
||||||
|
parsedArguments.put(argument.id(), new ParsedArgument<>(argument, rawValue, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
27
common/site/lab0x13/scrow/commands/model/CommandContext.java
Normal file
27
common/site/lab0x13/scrow/commands/model/CommandContext.java
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
package site.lab0x13.scrow.commands.model;
|
||||||
|
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.Argument;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.ParsedArgument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <C> argument of this
|
||||||
|
*/
|
||||||
|
public interface CommandContext<C extends CommandContext<C>> {
|
||||||
|
|
||||||
|
<V> ParsedArgument<C, V> getArgument(String id);
|
||||||
|
|
||||||
|
default <V> V getArg(String id) {
|
||||||
|
ParsedArgument<C, V> arg = getArgument(id);
|
||||||
|
return arg.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
default <V> V getArg(Argument<C, V> arg) {
|
||||||
|
return getArg(arg.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
default <V> ParsedArgument<C, V> getArgument(Argument<C, V> arg) {
|
||||||
|
return getArgument(arg.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
<V> void addParsedArgument(Argument<?, V> argument, String rawValue, V value);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package site.lab0x13.scrow.commands.model;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <C> argument of command context
|
||||||
|
*/
|
||||||
|
public interface CommandExecutor<C extends CommandContext<C>> {
|
||||||
|
|
||||||
|
CompletableFuture<?> execute(C ctx);
|
||||||
|
|
||||||
|
interface Sync<C extends CommandContext<C>> extends CommandExecutor<C> {
|
||||||
|
|
||||||
|
void executeSync(C ctx);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default CompletableFuture<?> execute(C ctx) {
|
||||||
|
executeSync(ctx);
|
||||||
|
return CompletableFuture.completedFuture(Void.TYPE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
common/site/lab0x13/scrow/commands/model/Node.java
Normal file
6
common/site/lab0x13/scrow/commands/model/Node.java
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
package site.lab0x13.scrow.commands.model;
|
||||||
|
|
||||||
|
public interface Node<C extends CommandContext<C>> {
|
||||||
|
|
||||||
|
SuggestionProvider<C> suggestionProvider();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package site.lab0x13.scrow.commands.model;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <C> argument of command context
|
||||||
|
*/
|
||||||
|
public interface SuggestionProvider<C extends CommandContext<C>> {
|
||||||
|
|
||||||
|
static <C extends CommandContext<C>> SuggestionProvider<C> none() {
|
||||||
|
return (_, _) -> Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> suggest(C ctx, String input);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.argument;
|
||||||
|
|
||||||
|
import com.leakyabstractions.result.api.Result;
|
||||||
|
import com.leakyabstractions.result.core.Results;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.Node;
|
||||||
|
import site.lab0x13.scrow.commands.model.SuggestionProvider;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <C> argument of command context
|
||||||
|
* @param <V> argument of argument value
|
||||||
|
*/
|
||||||
|
public record Argument<C extends CommandContext<C>, V>(
|
||||||
|
String id,
|
||||||
|
ArgumentType<C, V> type,
|
||||||
|
List<ArgumentRequirement<C, V>> requirements,
|
||||||
|
SuggestionProvider<C> suggestionProvider,
|
||||||
|
DefaultValueProvider<C, V> defaultValueProvider
|
||||||
|
) implements Node<C> {
|
||||||
|
|
||||||
|
public static <C extends CommandContext<C>, V> ArgumentBuilder<C, V> builder(String id, ArgumentType<C, V> type) {
|
||||||
|
return new ArgumentBuilder<>(id, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<ParsedArgument<C, V>, String> parse(C ctx, String rawInput) {
|
||||||
|
var parseResult = type.parseInput(rawInput);
|
||||||
|
if (parseResult.hasFailure())
|
||||||
|
return parseResult.mapSuccess(_ -> null);
|
||||||
|
var value = parseResult.getSuccess().orElseThrow();
|
||||||
|
|
||||||
|
for (var requirement : requirements) {
|
||||||
|
var isAllowed = requirement.predicate().check(ctx, value);
|
||||||
|
if (!isAllowed)
|
||||||
|
return Results.failure(requirement.errorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Results.success(new ParsedArgument<>(this, rawInput, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.argument;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.SuggestionProvider;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <C> argument of command context
|
||||||
|
* @param <V> argument of argument value
|
||||||
|
*/
|
||||||
|
public final class ArgumentBuilder<C extends CommandContext<C>, V> {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final ArgumentType<C, V> type;
|
||||||
|
private DefaultValueProvider<C, V> defaultValueProvider = DefaultValueProvider.none();
|
||||||
|
private @Nullable SuggestionProvider<C> suggestionProvider = null;
|
||||||
|
|
||||||
|
private final List<ArgumentRequirement<C, V>> requirements = new ArrayList<>();
|
||||||
|
|
||||||
|
ArgumentBuilder(String id, ArgumentType<C, V> type) {
|
||||||
|
this.id = id;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgumentBuilder<C, V> withRequirement(ArgumentPredicate<C, V> predicate, String errorMessage) {
|
||||||
|
this.requirements.add(new ArgumentRequirement<>(predicate, errorMessage));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgumentBuilder<C, V> withDefaultValue(@Nullable DefaultValueProvider<C, V> defaultValue) {
|
||||||
|
this.defaultValueProvider = defaultValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param suggestionProvider if null, use the argument's suggestion provider
|
||||||
|
*/
|
||||||
|
public ArgumentBuilder<C, V> withSuggestionProvider(@Nullable SuggestionProvider<C> suggestionProvider) {
|
||||||
|
this.suggestionProvider = suggestionProvider;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Argument<C, V> build() {
|
||||||
|
return new Argument<>(
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
new ArrayList<>(requirements),
|
||||||
|
suggestionProvider != null ? suggestionProvider : type.defaultSuggestionProvider(),
|
||||||
|
defaultValueProvider
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.argument;
|
||||||
|
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <C> argument of command context
|
||||||
|
* @param <V> argument of argument value
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ArgumentPredicate<C extends CommandContext<C>, V> {
|
||||||
|
boolean check(C ctx, V value);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.argument;
|
||||||
|
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <C> argument of command context
|
||||||
|
* @param <V> argument of argument value
|
||||||
|
*/
|
||||||
|
public record ArgumentRequirement<C extends CommandContext<C>, V>(
|
||||||
|
ArgumentPredicate<C, V> predicate,
|
||||||
|
String errorMessage
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.argument;
|
||||||
|
|
||||||
|
import com.leakyabstractions.result.api.Result;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.SuggestionProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <C> argument of command context
|
||||||
|
* @param <V> argument of parsed argument value
|
||||||
|
*/
|
||||||
|
public interface ArgumentType<C extends CommandContext<C>, V> {
|
||||||
|
|
||||||
|
Result<V, String> parseInput(String input);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return default suggestion provider for the argument, if not overwritten by the argument
|
||||||
|
*/
|
||||||
|
SuggestionProvider<C> defaultSuggestionProvider();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this argument is greedy. Greedy arguments consume all remaining input instead of a single word.
|
||||||
|
* For example, given the 2 words "hello" "world" as input, a greedy argument parses "hello world" as a single value.
|
||||||
|
* Greedy arguments may not be followed by another argument or a literal
|
||||||
|
*/
|
||||||
|
default boolean greedy() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.argument;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface DefaultValueProvider<C extends CommandContext<C>, V> {
|
||||||
|
|
||||||
|
static <V, C extends CommandContext<C>> DefaultValueProvider<C, V> none() {
|
||||||
|
return _ -> null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable V getDefault(C ctx);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.argument;
|
||||||
|
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <C> argument of command context
|
||||||
|
* @param <V> argument of argument value
|
||||||
|
*/
|
||||||
|
public record ParsedArgument<C extends CommandContext<C>, V>(
|
||||||
|
Argument<C, V> argument,
|
||||||
|
String rawValue,
|
||||||
|
V value
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.argument.types;
|
||||||
|
|
||||||
|
import com.leakyabstractions.result.api.Result;
|
||||||
|
import com.leakyabstractions.result.core.Results;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.SuggestionProvider;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.ArgumentType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BooleanArgumentType<C extends CommandContext<C>> implements ArgumentType<C, Boolean> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<Boolean, String> parseInput(String input) {
|
||||||
|
return Results.ofCallable(() -> Boolean.parseBoolean(input))
|
||||||
|
.mapFailure(_ -> "Must be true or false.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SuggestionProvider<C> defaultSuggestionProvider() {
|
||||||
|
return (_, _) -> List.of("true", "false");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.argument.types;
|
||||||
|
|
||||||
|
import com.leakyabstractions.result.api.Result;
|
||||||
|
import com.leakyabstractions.result.core.Results;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.SuggestionProvider;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.ArgumentType;
|
||||||
|
|
||||||
|
public class DoubleArgumentType<C extends CommandContext<C>> implements ArgumentType<C, Double> {
|
||||||
|
@Override
|
||||||
|
public Result<Double, String> parseInput(String input) {
|
||||||
|
return Results.ofCallable(() -> Double.parseDouble(input))
|
||||||
|
.mapFailure(_ -> "Not a valid double.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SuggestionProvider<C> defaultSuggestionProvider() {
|
||||||
|
return SuggestionProvider.none();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.argument.types;
|
||||||
|
|
||||||
|
import com.leakyabstractions.result.api.Result;
|
||||||
|
import com.leakyabstractions.result.core.Results;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.SuggestionProvider;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.ArgumentType;
|
||||||
|
|
||||||
|
public class IntegerArgumentType<C extends CommandContext<C>> implements ArgumentType<C, Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<Integer, String> parseInput(String string) {
|
||||||
|
return Results.ofCallable(() -> Integer.parseInt(string))
|
||||||
|
.mapFailure(_ -> "Not a number.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SuggestionProvider<C> defaultSuggestionProvider() {
|
||||||
|
return SuggestionProvider.none();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.argument.types;
|
||||||
|
|
||||||
|
import com.leakyabstractions.result.api.Result;
|
||||||
|
import com.leakyabstractions.result.core.Results;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.SuggestionProvider;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.ArgumentType;
|
||||||
|
|
||||||
|
public record StringArgumentType<C extends CommandContext<C>>(boolean greedy)
|
||||||
|
implements ArgumentType<C, String> {
|
||||||
|
|
||||||
|
public StringArgumentType() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<String, String> parseInput(String string) {
|
||||||
|
return Results.success(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SuggestionProvider<C> defaultSuggestionProvider() {
|
||||||
|
return SuggestionProvider.none();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.literal;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandExecutor;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.Argument;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
public abstract class DynamicLiteral<C extends CommandContext<C>> implements Literal<C>{
|
||||||
|
|
||||||
|
private final List<String> names;
|
||||||
|
private final @Nullable CommandExecutor<C> executor;
|
||||||
|
private final List<Argument<C, ?>> arguments;
|
||||||
|
private final String permission;
|
||||||
|
|
||||||
|
public DynamicLiteral(List<String> names, @Nullable CommandExecutor<C> executor, List<Argument<C, ?>> arguments, String permission) {
|
||||||
|
checkArgument(!names.isEmpty(), "at least one name expected");
|
||||||
|
this.names = names;
|
||||||
|
this.executor = executor;
|
||||||
|
this.arguments = arguments;
|
||||||
|
this.permission = permission;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> names() {
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable CommandExecutor<C> executor() {
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Argument<C, ?>> arguments() {
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String permission() {
|
||||||
|
return permission;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.literal;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public final class DynamicLiteralBuilder<C extends CommandContext<C>> extends LiteralBuilder<C, DynamicLiteralBuilder<C>> {
|
||||||
|
|
||||||
|
private Supplier<List<String>> subLiteralNamesSupplier = Collections::emptyList;
|
||||||
|
private Function<String, @Nullable Literal<C>> subLiteralProvider = _ -> null;
|
||||||
|
|
||||||
|
DynamicLiteralBuilder(String... names) {
|
||||||
|
super(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DynamicLiteralBuilder<C> getThis() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Literal<C> build() {
|
||||||
|
return new DynamicLiteral<>(
|
||||||
|
Arrays.asList(this.names),
|
||||||
|
this.executor,
|
||||||
|
this.arguments,
|
||||||
|
this.permission
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
public @Nullable Literal<C> getSubLiteral(String name) {
|
||||||
|
return subLiteralProvider.apply(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> subLiteralNames() {
|
||||||
|
return subLiteralNamesSupplier.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicLiteralBuilder<C> withSubLiteralNames(Supplier<List<String>> names) {
|
||||||
|
subLiteralNamesSupplier = names;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicLiteralBuilder<C> withSubLiteralProvider(Function<String, @Nullable Literal<C>> literalProvider) {
|
||||||
|
this.subLiteralProvider = literalProvider;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.literal;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandExecutor;
|
||||||
|
import site.lab0x13.scrow.commands.model.Node;
|
||||||
|
import site.lab0x13.scrow.commands.model.SuggestionProvider;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.Argument;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <C> argument of the command context
|
||||||
|
*/
|
||||||
|
public interface Literal<C extends CommandContext<C>> extends Node<C> {
|
||||||
|
|
||||||
|
static <C extends CommandContext<C>> StaticLiteralBuilder<C> builder(String... names) {
|
||||||
|
return new StaticLiteralBuilder<>(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <C extends CommandContext<C>> DynamicLiteralBuilder<C> dynamic(String... names) {
|
||||||
|
return new DynamicLiteralBuilder<>(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> names();
|
||||||
|
|
||||||
|
@Nullable CommandExecutor<C> executor();
|
||||||
|
|
||||||
|
@Nullable Literal<C> getSubLiteral(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return aliases/names of literals
|
||||||
|
*/
|
||||||
|
List<String> subLiteralNames();
|
||||||
|
|
||||||
|
List<Argument<C, ?>> arguments();
|
||||||
|
|
||||||
|
String permission();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default SuggestionProvider<C> suggestionProvider() {
|
||||||
|
return (_, _) -> subLiteralNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.literal;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandExecutor;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.Argument;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <C> argument of command context
|
||||||
|
* @param <B> argument of this/builder
|
||||||
|
*/
|
||||||
|
public abstract class LiteralBuilder<C extends CommandContext<C>,B extends LiteralBuilder<C, B>> {
|
||||||
|
|
||||||
|
protected final String[] names;
|
||||||
|
protected @Nullable CommandExecutor<C> executor;
|
||||||
|
protected @Nullable String permission;
|
||||||
|
protected final List<Argument<C, ?>> arguments = new ArrayList<>();
|
||||||
|
|
||||||
|
protected abstract B getThis();
|
||||||
|
|
||||||
|
public abstract Literal<C> build();
|
||||||
|
|
||||||
|
protected LiteralBuilder(String... names) {
|
||||||
|
this.names = names;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final RootLiteral<C> asRootLiteral(MessageStyle style) {
|
||||||
|
return new RootLiteralWrapper<>(build(), style);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final B withExecutor(@Nullable CommandExecutor<C> executor) {
|
||||||
|
this.executor = executor;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final B withSyncExecutor(@Nullable CommandExecutor.Sync<C> executor) {
|
||||||
|
this.executor = executor;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final B withPermission(@Nullable String permission) {
|
||||||
|
this.permission = permission;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final B withArgument(Argument<C, ?> argument) {
|
||||||
|
arguments.add(argument);
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.literal;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
|
||||||
|
public interface MessageStyle {
|
||||||
|
|
||||||
|
MessageStyle PLAIN = new PlainMessageStyle();
|
||||||
|
|
||||||
|
Component ok(Component msg);
|
||||||
|
|
||||||
|
Component err(Component msg);
|
||||||
|
|
||||||
|
Component exception(Component msg);
|
||||||
|
|
||||||
|
default Component ok(String msg) {
|
||||||
|
return ok(Component.text(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
default Component err(String msg) {
|
||||||
|
return err(Component.text(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
default Component exception(String msg) {
|
||||||
|
return exception(Component.text(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
default Component exception(Throwable ex) {
|
||||||
|
while (ex.getMessage() == null && ex.getCause() != null)
|
||||||
|
ex = ex.getCause();
|
||||||
|
return exception(ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.literal;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
|
||||||
|
class PlainMessageStyle implements MessageStyle {
|
||||||
|
@Override
|
||||||
|
public Component ok(Component msg) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component err(Component msg) {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component exception(Component msg) {
|
||||||
|
return Component.text("ERROR: ").append(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.literal;
|
||||||
|
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
|
||||||
|
public interface RootLiteral<C extends CommandContext<C>> extends Literal<C>{
|
||||||
|
|
||||||
|
MessageStyle style();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.literal;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandExecutor;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.Argument;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RootLiteralWrapper<C extends CommandContext<C>> implements RootLiteral<C> {
|
||||||
|
private final Literal<C> literal;
|
||||||
|
private final MessageStyle messageStyle;
|
||||||
|
|
||||||
|
public RootLiteralWrapper(Literal<C> literal, MessageStyle messageStyle) {
|
||||||
|
this.literal = literal;
|
||||||
|
this.messageStyle = messageStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageStyle style() {
|
||||||
|
return messageStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> names() {
|
||||||
|
return literal.names();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable CommandExecutor<C> executor() {
|
||||||
|
return literal.executor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Literal<C> getSubLiteral(String name) {
|
||||||
|
return literal.getSubLiteral(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> subLiteralNames() {
|
||||||
|
return literal.subLiteralNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Argument<C, ?>> arguments() {
|
||||||
|
return literal.arguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String permission() {
|
||||||
|
return literal.permission();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.literal;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandExecutor;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.Argument;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class StaticLiteral<C extends CommandContext<C>> extends DynamicLiteral<C> {
|
||||||
|
|
||||||
|
private final List<Literal<C>> subLiterals;
|
||||||
|
|
||||||
|
public StaticLiteral(
|
||||||
|
List<String> names,
|
||||||
|
@Nullable CommandExecutor<C> executor,
|
||||||
|
List<Argument<C, ?>> arguments,
|
||||||
|
String permission, List<Literal<C>> subLiterals
|
||||||
|
) {
|
||||||
|
super(names, executor, arguments, permission);
|
||||||
|
this.subLiterals = subLiterals;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Literal<C> getSubLiteral(String name) {
|
||||||
|
return subLiterals.stream()
|
||||||
|
.filter(literal -> literal.names().contains(name))
|
||||||
|
.findAny()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> subLiteralNames() {
|
||||||
|
return subLiterals.stream()
|
||||||
|
.flatMap(literal -> literal.names().stream())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package site.lab0x13.scrow.commands.model.literal;
|
||||||
|
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class StaticLiteralBuilder<C extends CommandContext<C>> extends LiteralBuilder<C, StaticLiteralBuilder<C>> {
|
||||||
|
|
||||||
|
private final List<Literal<C>> subLiterals = new ArrayList<>();
|
||||||
|
|
||||||
|
StaticLiteralBuilder(String... names) {
|
||||||
|
super(names);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected StaticLiteralBuilder<C> getThis() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Literal<C> build() {
|
||||||
|
return new StaticLiteral<>(
|
||||||
|
Arrays.asList(this.names),
|
||||||
|
this.executor,
|
||||||
|
this.arguments,
|
||||||
|
this.permission,
|
||||||
|
this.subLiterals
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StaticLiteralBuilder<C> withSubLiteral(Literal<C> subLiteral) {
|
||||||
|
subLiterals.add(subLiteral);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
100
common/site/lab0x13/scrow/commands/parser/NodeTreeWalker.java
Normal file
100
common/site/lab0x13/scrow/commands/parser/NodeTreeWalker.java
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
package site.lab0x13.scrow.commands.parser;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.PlatformAdapter;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.Argument;
|
||||||
|
import site.lab0x13.scrow.commands.model.literal.Literal;
|
||||||
|
import site.lab0x13.scrow.commands.model.literal.RootLiteral;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
public class NodeTreeWalker<C extends CommandContext<C>> {
|
||||||
|
|
||||||
|
private final PlatformAdapter<C> platformAdapter;
|
||||||
|
|
||||||
|
public NodeTreeWalker(PlatformAdapter<C> platformAdapter) {
|
||||||
|
this.platformAdapter = platformAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WalkResult<C> walk(
|
||||||
|
C ctx,
|
||||||
|
RootLiteral<C> rootLiteral,
|
||||||
|
Iterator<String> iter
|
||||||
|
) {
|
||||||
|
var cursor = new Cursor(iter);
|
||||||
|
Literal<C> literal = rootLiteral;
|
||||||
|
String token;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
for (var arg : literal.arguments()) {
|
||||||
|
//noinspection unchecked
|
||||||
|
var res = walkArgument(ctx, cursor, literal, (Argument<C, Object>) arg);
|
||||||
|
if (res != null)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = cursor.nextToken();
|
||||||
|
if (token == null)
|
||||||
|
break;
|
||||||
|
var nextLiteral = literal.getSubLiteral(token);
|
||||||
|
if (nextLiteral == null)
|
||||||
|
return new WalkResult.LiteralUnknown<>(ctx, literal, token);
|
||||||
|
if (nextLiteral.permission() != null && platformAdapter.checkPermission(ctx, nextLiteral.permission()))
|
||||||
|
return new WalkResult.LiteralUnknown<>(ctx, literal, token);
|
||||||
|
literal = nextLiteral;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!literal.subLiteralNames().isEmpty()) {
|
||||||
|
var isOptional = literal.executor() != null;
|
||||||
|
return new WalkResult.LiteralExpected<>(ctx, literal, isOptional);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new WalkResult.Complete<>(ctx, literal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable WalkResult<C> walkArgument(
|
||||||
|
C ctx,
|
||||||
|
Cursor cursor,
|
||||||
|
Literal<C> literal,
|
||||||
|
Argument<C, Object> arg
|
||||||
|
) {
|
||||||
|
var rawInput = arg.type().greedy() ? cursor.remainingTokens() : cursor.nextToken();
|
||||||
|
if (rawInput == null) {
|
||||||
|
var value = requireNonNull(arg.defaultValueProvider()).getDefault(ctx);
|
||||||
|
if (value == null)
|
||||||
|
return new WalkResult.ArgumentExpected<>(ctx, literal, false, arg);
|
||||||
|
ctx.addParsedArgument(arg, "", value);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parseResult = arg.parse(ctx, rawInput);
|
||||||
|
if (parseResult.hasFailure())
|
||||||
|
return new WalkResult.ArgumentIllegal<>(ctx, literal, false,
|
||||||
|
parseResult.getFailure().orElseThrow(), arg, rawInput);
|
||||||
|
ctx.addParsedArgument(arg, rawInput, parseResult.getSuccess().orElseThrow().value());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Cursor {
|
||||||
|
private final Iterator<String> tokenIter;
|
||||||
|
|
||||||
|
public Cursor(Iterator<String> tokenIter) {
|
||||||
|
this.tokenIter = tokenIter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String nextToken() {
|
||||||
|
if (!tokenIter.hasNext())
|
||||||
|
return null;
|
||||||
|
return tokenIter.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable String remainingTokens() {
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
tokenIter.forEachRemaining(s -> builder.append(' ').append(s));
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
80
common/site/lab0x13/scrow/commands/parser/WalkResult.java
Normal file
80
common/site/lab0x13/scrow/commands/parser/WalkResult.java
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
package site.lab0x13.scrow.commands.parser;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import site.lab0x13.scrow.commands.model.CommandContext;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.Argument;
|
||||||
|
import site.lab0x13.scrow.commands.model.literal.Literal;
|
||||||
|
|
||||||
|
public sealed interface WalkResult<C extends CommandContext<C>> {
|
||||||
|
C ctx();
|
||||||
|
|
||||||
|
Literal<C> lastLiteral();
|
||||||
|
|
||||||
|
boolean isValidUsage();
|
||||||
|
|
||||||
|
@Nullable String message();
|
||||||
|
|
||||||
|
record ArgumentIllegal<C extends CommandContext<C>>(
|
||||||
|
C ctx,
|
||||||
|
Literal<C> lastLiteral,
|
||||||
|
boolean isValidUsage,
|
||||||
|
String message,
|
||||||
|
Argument<C, ?> arg,
|
||||||
|
String input
|
||||||
|
) implements WalkResult<C> {
|
||||||
|
}
|
||||||
|
|
||||||
|
record ArgumentExpected<C extends CommandContext<C>>(
|
||||||
|
C ctx,
|
||||||
|
Literal<C> lastLiteral,
|
||||||
|
boolean isValidUsage,
|
||||||
|
Argument<C, ?> arg
|
||||||
|
) implements WalkResult<C> {
|
||||||
|
@Override
|
||||||
|
public String message() {
|
||||||
|
return "Argument expected.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record LiteralExpected<C extends CommandContext<C>>(
|
||||||
|
C ctx,
|
||||||
|
Literal<C> lastLiteral,
|
||||||
|
boolean isValidUsage
|
||||||
|
) implements WalkResult<C> {
|
||||||
|
@Override
|
||||||
|
public String message() {
|
||||||
|
return "Literal expected.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record LiteralUnknown<C extends CommandContext<C>>(
|
||||||
|
C ctx,
|
||||||
|
Literal<C> lastLiteral,
|
||||||
|
String input
|
||||||
|
) implements WalkResult<C> {
|
||||||
|
@Override
|
||||||
|
public boolean isValidUsage() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String message() {
|
||||||
|
return "Unknown literal \"" + input + "\".";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record Complete<C extends CommandContext<C>>(
|
||||||
|
C ctx,
|
||||||
|
Literal<C> lastLiteral
|
||||||
|
) implements WalkResult<C> {
|
||||||
|
@Override
|
||||||
|
public boolean isValidUsage() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String message() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
common/tests/BUILD.bazel
Normal file
16
common/tests/BUILD.bazel
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
load("@contrib_rules_jvm//java:defs.bzl", "java_test_suite", "JUNIT5_DEPS")
|
||||||
|
load("@rules_jvm_external//:defs.bzl", "artifact")
|
||||||
|
load("//common:deps.bzl", "COMMON_DEPS")
|
||||||
|
|
||||||
|
java_test_suite(
|
||||||
|
name = "all_tests",
|
||||||
|
srcs = glob(["**/*.java"]),
|
||||||
|
test_suffixes = ["Test.java"],
|
||||||
|
runner = "junit5",
|
||||||
|
size = "small",
|
||||||
|
deps = [
|
||||||
|
"//common",
|
||||||
|
artifact("net.kyori:adventure-text-serializer-plain"),
|
||||||
|
artifact("org.junit.jupiter:junit-jupiter-api"),
|
||||||
|
] + JUNIT5_DEPS + COMMON_DEPS,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package site.lab0x13.scrow.commands;
|
||||||
|
|
||||||
|
import site.lab0x13.scrow.commands.model.AbstractCommandContext;
|
||||||
|
|
||||||
|
public class CommandContextImpl extends AbstractCommandContext<CommandContextImpl> {
|
||||||
|
}
|
||||||
108
common/tests/site/lab0x13/scrow/commands/CommandHandlerTest.java
Normal file
108
common/tests/site/lab0x13/scrow/commands/CommandHandlerTest.java
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
package site.lab0x13.scrow.commands;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterAll;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.Argument;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.types.IntegerArgumentType;
|
||||||
|
import site.lab0x13.scrow.commands.model.argument.types.StringArgumentType;
|
||||||
|
import site.lab0x13.scrow.commands.model.literal.Literal;
|
||||||
|
import site.lab0x13.scrow.commands.model.literal.MessageStyle;
|
||||||
|
import site.lab0x13.scrow.commands.model.literal.RootLiteral;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class CommandHandlerTest {
|
||||||
|
|
||||||
|
private static final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
|
||||||
|
private static final PrintStream originalOut = System.out;
|
||||||
|
|
||||||
|
private final CommandHandler<CommandContextImpl> commandHandler = new CommandHandler<>(new TestPlatform(Collections.emptyList()));
|
||||||
|
private final Argument<CommandContextImpl, String> playerArg =
|
||||||
|
Argument.builder("player", new StringArgumentType<CommandContextImpl>())
|
||||||
|
.build();
|
||||||
|
private final Argument<CommandContextImpl, Integer> amountArg =
|
||||||
|
Argument.builder("amount", new IntegerArgumentType<CommandContextImpl>())
|
||||||
|
.build();
|
||||||
|
private final RootLiteral<CommandContextImpl> rootLiteral = Literal.<CommandContextImpl>builder("coins")
|
||||||
|
.withSyncExecutor(_ -> {
|
||||||
|
System.out.println("default executor");
|
||||||
|
})
|
||||||
|
.withSubLiteral(Literal.<CommandContextImpl>builder("get")
|
||||||
|
.withArgument(playerArg)
|
||||||
|
.withSyncExecutor(ctx -> {
|
||||||
|
var player = ctx.getArg(playerArg);
|
||||||
|
System.out.println("get executor: player=" + player);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withSubLiteral(Literal.<CommandContextImpl>builder("set")
|
||||||
|
.withArgument(playerArg)
|
||||||
|
.withArgument(amountArg)
|
||||||
|
.withSyncExecutor(ctx -> {
|
||||||
|
var player = ctx.getArg(playerArg);
|
||||||
|
var amount = ctx.getArg(amountArg);
|
||||||
|
System.out.println("set executor: player=" + player + " amount=" + amount);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.asRootLiteral(MessageStyle.PLAIN);
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void init() {
|
||||||
|
System.setOut(new PrintStream(outContent));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterAll
|
||||||
|
static void cleanup() {
|
||||||
|
System.setOut(originalOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void resetOut() {
|
||||||
|
outContent.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDefaultExecutor() {
|
||||||
|
commandHandler.invoke(rootLiteral, new CommandContextImpl(), Stream.of());
|
||||||
|
assertEquals("default executor\n", outContent.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSubLiteralNoArgs() {
|
||||||
|
commandHandler.invoke(rootLiteral, new CommandContextImpl(), Stream.of("get", "foo"));
|
||||||
|
assertEquals("get executor: player=foo\n", outContent.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSubLiteralWithArgs() {
|
||||||
|
commandHandler.invoke(rootLiteral, new CommandContextImpl(), Stream.of("set", "foo", "10"));
|
||||||
|
assertEquals("set executor: player=foo amount=10\n", outContent.toString());
|
||||||
|
outContent.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailOnBadLiteral() {
|
||||||
|
commandHandler.invoke(rootLiteral, new CommandContextImpl(), Stream.of("BAD_LITERAL"));
|
||||||
|
assertEquals("Unknown literal \"BAD_LITERAL\".\n", outContent.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailOnBadArgument() {
|
||||||
|
commandHandler.invoke(rootLiteral, new CommandContextImpl(), Stream.of("set", "foo", "bar"));
|
||||||
|
assertEquals("Not a number.\n", outContent.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFailOnMissingArgument() {
|
||||||
|
commandHandler.invoke(rootLiteral, new CommandContextImpl(), Stream.of("set"));
|
||||||
|
assertEquals("Argument expected.\n", outContent.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
27
common/tests/site/lab0x13/scrow/commands/TestPlatform.java
Normal file
27
common/tests/site/lab0x13/scrow/commands/TestPlatform.java
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
package site.lab0x13.scrow.commands;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TestPlatform implements PlatformAdapter<CommandContextImpl> {
|
||||||
|
|
||||||
|
private final PlainTextComponentSerializer plainText = PlainTextComponentSerializer.plainText();
|
||||||
|
|
||||||
|
private final List<String> permissions;
|
||||||
|
|
||||||
|
public TestPlatform(List<String> permissions) {
|
||||||
|
this.permissions = permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkPermission(CommandContextImpl ctx, String permission) {
|
||||||
|
return permissions.contains(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessage(CommandContextImpl ctx, Component message) {
|
||||||
|
System.out.println(plainText.serialize(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
2
defs.bzl
Normal file
2
defs.bzl
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
GROUP = "site.lab0x13.scrow"
|
||||||
|
VERSION = "0.1-SNAPSHOT"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue