This commit is contained in:
commit
7c322707ba
51 changed files with 1488 additions and 0 deletions
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));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue