reworked parsing

This commit is contained in:
kento2 2026-07-06 22:08:48 +02:00
parent 8897579a12
commit ac6a95232e
22 changed files with 360 additions and 441 deletions

View file

@ -1,66 +1,46 @@
package de.kentoj.kencommandapi.api;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.parser.CommandParser;
import de.kentoj.kencommandapi.api.parser.ParseResult;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import de.kentoj.kencommandapi.api.parser2.InvocationCommandParser;
import de.kentoj.kencommandapi.api.parser2.SuggestionCommandParser;
import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
import de.kentoj.kencommandapi.api.platform.SendMessageMethod;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
@RequiredArgsConstructor
public class CommandHandler<T> {
private final CommandParser<T> commandParser;
private final SendMessageMethod<T> sendMessageMethod;
private final HasPermissionMethod<T> hasPermissionMethod;
public void invoke(RootCommandNode<T> rootNode, T sender, String[] args) {
var parseResult = commandParser.parseLiteral(rootNode, sender, args, true);
if (!(parseResult instanceof ParseResult.Success<T>)) {
sendMessageMethod.send(sender, rootNode.getMessageStyle().err(parseResult.getMessage()));
return;
}
private final InvocationCommandParser<T> invocationParser;
private final SuggestionCommandParser<T> suggestionParser;
var ctx = new CommandContext<>(sender, ((ParseResult.Success<T>) parseResult).getParsedArguments());
assert parseResult.getLiteral().getExecutor() != null;
parseResult.getLiteral().getExecutor().execute(ctx).whenComplete((result, ex) -> {
if (ex != null)
sendMessageMethod.send(sender, rootNode.getMessageStyle().exception(ex));
result.ifFailure(failure ->
sendMessageMethod.send(sender, rootNode.getMessageStyle().err(failure)));
});
public CommandHandler(SendMessageMethod<T> sendMessageMethod, HasPermissionMethod<T> hasPermissionMethod) {
this.sendMessageMethod = sendMessageMethod;
invocationParser = new InvocationCommandParser<>(hasPermissionMethod);
suggestionParser = new SuggestionCommandParser<>(hasPermissionMethod);
}
public List<String> getSuggestions(RootCommandNode<T> rootNode, T sender, String[] args) {
var parseResult = commandParser.parseLiteral(rootNode, sender, args, false);
if (parseResult instanceof ParseResult.ArgumentExpected<T>) {
var arg = ((ParseResult.ArgumentExpected<T>) parseResult).getArgument();
return arg.getSuggestionProvider().suggest(sender);
}
String filter = null;
if (parseResult instanceof ParseResult.UnknownLiteral<T>)
filter = ((ParseResult.UnknownLiteral<T>) parseResult).getInput();
else if (parseResult instanceof ParseResult.NoPermission<T>)
filter = ((ParseResult.NoPermission<T>) parseResult).getInput();
return getSuggestions(sender, parseResult.getLiteral(), filter);
@SuppressWarnings("CodeBlock2Expr")
public void invoke(RootLiteral<T> rootLiteral, T sender, String[] args) {
invocationParser.invoke(rootLiteral, sender, args)
.ifSuccessOrElse(data -> {
var ctx = new CommandContext<>(sender, data.getArguments());
data.getExecutor().execute(ctx).whenComplete((result, ex) -> {
if (ex != null)
sendMessageMethod.send(sender, rootLiteral.getMessageStyle().exception(ex));
result.ifFailure(err ->
sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err)));
});
}, err -> {
sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err));
});
}
private List<String> getSuggestions(T sender, CommandNode<T> literal, @Nullable String filter) {
var stream = literal.getLiterals().stream()
.filter(lit -> lit.getPermission() != null && hasPermissionMethod.hasPermission(sender, lit.getPermission()))
.map(CommandNode::getNames)
.flatMap(Arrays::stream);
if (filter != null)
stream = stream.filter(f -> f.toLowerCase().contains(filter.toLowerCase()));
return stream.toList();
public List<String> getSuggestions(RootLiteral<T> rootNode, T sender, String[] args) {
return suggestionParser.getSuggestions(rootNode, sender, args);
}
}
}

View file

@ -1,23 +1,23 @@
package de.kentoj.kencommandapi.api.node;
package de.kentoj.kencommandapi.api.literal;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.internal.CommandNodeImpl;
import de.kentoj.kencommandapi.internal.RootCommandNodeImpl;
import de.kentoj.kencommandapi.internal.LiteralImpl;
import de.kentoj.kencommandapi.internal.RootLiteralImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Set;
public interface CommandNode<T> {
static <T> CommandNode<T> node(String... name) {
return new CommandNodeImpl<>(name);
public interface Literal<T> {
static <T> Literal<T> literal(String... name) {
return new LiteralImpl<>(name);
}
static <T> RootCommandNode<T> rootNode(MessageStyle messageStyle, String...name) {
return new RootCommandNodeImpl<>(messageStyle, name);
static <T> RootLiteral<T> rootLiteral(MessageStyle messageStyle, String...name) {
return new RootLiteralImpl<>(messageStyle, name);
}
@NotNull String[] getNames();
@ -32,7 +32,8 @@ public interface CommandNode<T> {
/**
* @throws IllegalArgumentException if there is already an argument with the same id
* @throws IllegalStateException if a greedy argument was added to the node previously
* @throws IllegalStateException if a greedy argument was added to the literal previously
* @throws IllegalStateException if the literal already has a child literal assigned to it
*/
<V> void addArgument(CommandArgument<T, V> argument);
@ -48,13 +49,14 @@ public interface CommandNode<T> {
/**
* @throws IllegalArgumentException if there is already a literal with the same name
* @throws IllegalStateException if a greedy argument was added to the node previously
* @throws IllegalStateException if a greedy argument was added to the literal previously
* @throws IllegalStateException if the literal already has an argument assigned to it
*/
void addLiteral(@NotNull CommandNode<T> commandNode);
void addLiteral(@NotNull Literal<T> literal);
@Nullable CommandNode<T> getLiteral(@NotNull String name);
@Nullable Literal<T> getLiteral(@NotNull String name);
@NotNull Set<CommandNode<T>> getLiterals();
@NotNull Set<Literal<T>> getLiterals();
@Nullable String getPermission();

View file

@ -1,8 +1,8 @@
package de.kentoj.kencommandapi.api.node;
package de.kentoj.kencommandapi.api.literal;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
public interface RootCommandNode<T> extends CommandNode<T> {
public interface RootLiteral<T> extends Literal<T> {
MessageStyle getMessageStyle();

View file

@ -1,103 +0,0 @@
package de.kentoj.kencommandapi.api.parser;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
import de.kentoj.kencommandapi.internal.ParsedArgumentImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@RequiredArgsConstructor
public class CommandParser<T> {
private final HasPermissionMethod<T> hasPermissionMethod;
public ParseResult<T> parseLiteral(RootCommandNode<T> rootNode, T sender, String[] args, boolean parseArgs) {
CommandNode<T> cur = rootNode;
final Iterator<String> argStrIter = Arrays.stream(args).iterator();
final Map<String, ParsedArgument<T, ?>> parsedArguments = new HashMap<>();
while (true) {
for (var nodeArg : cur.getArguments()) {
var parsed = parseArgument(rootNode, nodeArg, argStrIter, sender, parseArgs);
if (parsed.hasFailure())
return parsed.getFailure().orElseThrow();
if (parseArgs)
parsedArguments.put(nodeArg.getId(), parsed.getSuccess().orElseThrow());
}
if (!argStrIter.hasNext()) {
if (cur.getExecutor() == null)
return new ParseResult.LiteralExpected<>(cur);
return new ParseResult.Success<>(cur, parsedArguments);
}
var argStr = argStrIter.next();
var nextNode = cur.getLiteral(argStr);
if (nextNode == null)
return new ParseResult.UnknownLiteral<>(cur, argStr);
if (nextNode.getPermission() != null && !hasPermissionMethod.hasPermission(sender, nextNode.getPermission()))
return new ParseResult.NoPermission<>(cur, argStr);
cur = nextNode;
}
}
@SuppressWarnings("unchecked")
private Result<ParsedArgument<T, Object>, ParseResult<T>> parseArgument(
CommandNode<T> node,
CommandArgument<T, ?> nodeArg,
Iterator<String> args,
T sender,
boolean parseArgs
) {
if (!args.hasNext()) {
if (nodeArg.getDefaultValueProvider() == null)
return Results.failure(new ParseResult.ArgumentExpected<>(node, nodeArg));
var value = nodeArg.getDefaultValueProvider().apply(sender);
return Results.success(toParsed(value, nodeArg));
}
Object value = null;
var argStr = getRawArgument(args, nodeArg);
if (parseArgs) {
try {
value = nodeArg.getType().parseInput(argStr);
} catch (IllegalArgumentException ex) {
return Results.failure(new ParseResult.IllegalArgument<>(node, nodeArg, ex.getMessage(), argStr));
}
}
var parsedArg = new ParsedArgumentImpl<>(value, (CommandArgument<T, Object>) nodeArg);
return Results.success(parsedArg);
}
@SuppressWarnings("unchecked")
private ParsedArgument<T, Object> toParsed(Object value, CommandArgument<T, ?> arg) {
return new ParsedArgumentImpl<>(value, (CommandArgument<T, Object>) arg);
}
private String getRawArgument(Iterator<String> args, CommandArgument<T, ?> arg) {
StringBuilder rawArgument = new StringBuilder();
if (arg.getType().isGreedy()) {
while (args.hasNext()) {
rawArgument.append(" ");
rawArgument.append(args.next());
}
rawArgument.deleteCharAt(0);
} else {
rawArgument = new StringBuilder(args.next());
}
return rawArgument.toString();
}
}

View file

@ -1,114 +0,0 @@
package de.kentoj.kencommandapi.api.parser;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
import de.kentoj.kencommandapi.api.node.CommandNode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Map;
public abstract sealed class ParseResult<T> permits ParseResult.IllegalArgument, ParseResult.ArgumentExpected,
ParseResult.LiteralExpected, ParseResult.NoPermission, ParseResult.Success, ParseResult.UnknownLiteral {
/**
* @return last successfully literal
*/
public abstract CommandNode<T> getLiteral();
public abstract String getMessage();
@RequiredArgsConstructor
public static final class ArgumentExpected<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
@Getter
private final CommandArgument<T, ?> argument;
@Override
public String getMessage() {
return "Argument expected: " + argument.getId();
}
}
@RequiredArgsConstructor
public static final class UnknownLiteral<T> extends ParseResult<T> {
private final CommandNode<T> previousLiteral;
@Getter
private final String input;
@Override
public String getMessage() {
return "Unknown subcommand: " + input;
}
@Override
public CommandNode<T> getLiteral() {
return previousLiteral;
}
}
@RequiredArgsConstructor
public static final class LiteralExpected<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
@Override
public String getMessage() {
return "Subcommand expected";
}
}
@RequiredArgsConstructor
public static final class Success<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
@Getter
private final Map<String, ParsedArgument<T, ?>> parsedArguments;
@Override
public String getMessage() {
return "";
}
}
@RequiredArgsConstructor
public static final class NoPermission<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
@Getter
private final String input;
@Override
public String getMessage() {
return "Unknown subcommand: " + input;
}
}
@RequiredArgsConstructor
public static final class IllegalArgument<T> extends ParseResult<T> {
@Getter
private final CommandNode<T> literal;
private final CommandArgument<T, ?> argument;
private final String message;
/**
* Input passed to the parser
*/
@Getter
private final String argStr;
public IllegalArgument(CommandNode<T> literal, CommandArgument<T, ?> argument, Throwable throwable, String argStr) {
this.literal = literal;
this.argument = argument;
this.argStr = argStr;
while (throwable.getMessage() == null && throwable.getCause() != null)
throwable = throwable.getCause();
message = throwable.getMessage();
}
@Override
public String getMessage() {
return "Illegal argument '" + argStr + "' for " + argument.getId() + ": " + message;
}
}
}

View file

@ -0,0 +1,39 @@
package de.kentoj.kencommandapi.api.parser2;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
@RequiredArgsConstructor
public abstract class AbstractCommandParser<T> {
private final HasPermissionMethod<T> hasPermission;
protected boolean hasPermission(T sender, Literal<T> literal) {
if (literal.getPermission() == null) return true;
return hasPermission.test(sender, literal.getPermission());
}
protected String parseInput(String[] args, AtomicInteger index, boolean isGreedy) {
if (!isGreedy)
return args[index.getAndIncrement()];
var ret = String.join(" ", Arrays.copyOfRange(args, index.get(), args.length));
index.set(args.length);
return ret;
}
protected Result<Object, String> parseArgument(CommandArgument<T, Object> argument, String input) {
try {
return Results.success(argument.getType().parseInput(input));
} catch (IllegalArgumentException ex) {
return Results.failure("Illegal argument '" + input + "' for " + argument.getId() + ": " + ex.getMessage());
}
}
}

View file

@ -0,0 +1,74 @@
package de.kentoj.kencommandapi.api.parser2;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
import de.kentoj.kencommandapi.internal.ParsedArgumentImpl;
import lombok.Value;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
public class InvocationCommandParser<T> extends AbstractCommandParser<T> {
public InvocationCommandParser(HasPermissionMethod<T> hasPermission) {
super(hasPermission);
}
public Result<ExecutionData, String> invoke(
RootLiteral<T> rootLiteral,
T sender,
String[] args
) {
Map<String, ParsedArgument<T, ?>> parsedArguments = new HashMap<>();
Literal<T> currentLiteral = rootLiteral;
AtomicInteger index = new AtomicInteger(0);
while (true) {
for (var _argument : currentLiteral.getArguments()) {
@SuppressWarnings("unchecked")
var argument = ((CommandArgument<T, Object>) _argument);
Object value;
if (index.get() >= args.length) {
if (argument.getDefaultValueProvider() == null)
return Results.failure("Argument expected: " + argument.getId());
value = argument.getDefaultValueProvider().apply(sender);
} else {
var input = parseInput(args, index, argument.getType().isGreedy());
var parseResult = parseArgument(argument, input);
if (parseResult.hasFailure()) return parseResult.mapSuccess(__ -> null);
value = parseResult.getSuccess().orElseThrow();
}
parsedArguments.put(argument.getId(), new ParsedArgumentImpl<>(value, argument));
}
if (index.get() >= args.length) {
if (currentLiteral.getExecutor() == null)
return Results.failure("Literal expected");
break;
}
var literalName = args[index.getAndIncrement()];
var nextLiteral = currentLiteral.getLiteral(literalName);
if (nextLiteral == null || !hasPermission(sender, nextLiteral))
return Results.failure("Unknown literal: " + literalName);
currentLiteral = nextLiteral;
}
return Results.success(new ExecutionData(currentLiteral.getExecutor(), parsedArguments));
}
@Value
public class ExecutionData {
CommandExecutor<T> executor;
Map<String, ParsedArgument<T, ?>> arguments;
}
}

View file

@ -0,0 +1,60 @@
package de.kentoj.kencommandapi.api.parser2;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
public class SuggestionCommandParser<T> extends AbstractCommandParser<T> {
public SuggestionCommandParser(HasPermissionMethod<T> hasPermission) {
super(hasPermission);
}
public List<String> getSuggestions(
RootLiteral<T> rootNode,
T sender,
String[] args
) {
Literal<T> currentLiteral = rootNode;
AtomicInteger index = new AtomicInteger(0);
while (true) {
for (var argument : currentLiteral.getArguments()) {
// TODO somewhere suggest next argument at trailing space
if (index.getAndIncrement() >= args.length)
return argument.getSuggestionProvider().suggest(sender);
if (argument.getType().isGreedy())
index.set(args.length);
}
if (index.get() >= args.length) {
// TODO suggest next argument at trailing space
if (currentLiteral.getExecutor() == null)
return suggestions(currentLiteral, sender).toList();
}
var input = args[index.getAndIncrement()];
var nextLiteral = currentLiteral.getLiteral(input);
if (nextLiteral == null || !hasPermission(sender, nextLiteral)) {
var finalInput = input.toLowerCase();
return suggestions(currentLiteral, sender)
.filter(name -> name.toLowerCase().startsWith(finalInput))
.toList();
}
currentLiteral = nextLiteral;
}
}
private Stream<String> suggestions(Literal<T> literal, T sender) {
return literal.getLiterals().stream()
.filter(lit -> hasPermission(sender, lit))
.map(Literal::getNames)
.flatMap(Arrays::stream);
}
}

View file

@ -2,5 +2,5 @@ package de.kentoj.kencommandapi.api.platform;
@FunctionalInterface
public interface HasPermissionMethod<T> {
boolean hasPermission(T sender, String permission);
boolean test(T sender, String permission);
}

View file

@ -1,6 +1,6 @@
package de.kentoj.kencommandapi.internal;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import lombok.Getter;
@ -13,12 +13,12 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class CommandNodeImpl<T> implements CommandNode<T> {
public class LiteralImpl<T> implements Literal<T> {
@Getter
private final String[] names;
@Getter
private final Set<CommandNode<T>> literals = new HashSet<>();
private final Set<Literal<T>> literals = new HashSet<>();
@Getter
private final List<CommandArgument<T, ?>> arguments = new ArrayList<>();
@ -34,22 +34,22 @@ public class CommandNodeImpl<T> implements CommandNode<T> {
@Setter
private @Nullable String permission = null;
public CommandNodeImpl(String[] names) {
public LiteralImpl(String[] names) {
if (names.length == 0) throw new IllegalArgumentException("at least one name is required");
this.names = names;
}
@Override
public void addLiteral(@NotNull CommandNode<T> commandNode) {
public void addLiteral(@NotNull Literal<T> literal) {
if (isGreedy()) throw new IllegalStateException("can't add literal to a node with a greedy argument");
for (String name : commandNode.getNames())
for (String name : literal.getNames())
if (getLiteral(name) != null)
throw new IllegalArgumentException("literal with same name already exists");
literals.add(commandNode);
literals.add(literal);
}
@Override
public CommandNode<T> getLiteral(@NotNull String name) {
public Literal<T> getLiteral(@NotNull String name) {
for (var literal : literals)
for (String literalName : literal.getNames())
if (literalName.equalsIgnoreCase(name)) return literal;

View file

@ -1,16 +1,16 @@
package de.kentoj.kencommandapi.internal;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import lombok.Getter;
import lombok.Setter;
public class RootCommandNodeImpl<T> extends CommandNodeImpl<T> implements RootCommandNode<T> {
public class RootLiteralImpl<T> extends LiteralImpl<T> implements RootLiteral<T> {
@Setter
@Getter
private MessageStyle messageStyle;
public RootCommandNodeImpl(MessageStyle messageStyle, String[] names) {
public RootLiteralImpl(MessageStyle messageStyle, String[] names) {
super(names);
this.messageStyle = messageStyle;
}

View file

@ -3,7 +3,7 @@ package de.kentoj.kencommandapi.api.parser;
import de.kentoj.kencommandapi.api.CommandHandler;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.argument.types.StringArgumentType;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import org.junit.jupiter.api.Test;
@ -14,19 +14,28 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class CommandHandlerTest {
@Test
void testSuggestions() {
var rootNode = CommandNode.rootNode(MessageStyle.PLAIN, "someRootNode");
void suggestArgument() {
var rootNode = Literal.rootLiteral(MessageStyle.PLAIN, "someRootNode");
var arg = CommandArgument.arg("someArg", new StringArgumentType<>());
arg.setSuggestionProvider(__ -> List.of("a", "b", "c"));
rootNode.addArgument(arg);
rootNode.setExecutor(ENV.EMPTY_EXECUTOR);
var handler = new CommandHandler<>(
new CommandParser<>((__, ___) -> true),
(__, ___) -> {},
(__, ___) -> true
);
var handler = new CommandHandler<>((__, ___) -> {}, (__, ___) -> true);
var suggestions = handler.getSuggestions(rootNode, ENV.SENDER, new String[0]);
assertEquals(List.of("a", "b", "c"), suggestions);
}
@Test
void suggestLiterals() {
var rootNode = Literal.rootLiteral(MessageStyle.PLAIN, "someRootNode");
var literal1 = Literal.literal("lit1");
var literal2 = Literal.literal("lit2");
rootNode.addLiteral(literal1);
rootNode.addLiteral(literal2);
var handler = new CommandHandler<>((__, ___) -> {}, (__, ___) -> true);
var suggestions = handler.getSuggestions(rootNode, ENV.SENDER, new String[0]);
assertEquals(List.of("lit1", "lit2"), suggestions.stream().sorted().toList());
}
}

View file

@ -1,43 +0,0 @@
package de.kentoj.kencommandapi.api.parser;
import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.argument.types.StringArgumentType;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
import de.kentoj.kencommandapi.api.node.CommandNode;
import de.kentoj.kencommandapi.api.node.RootCommandNode;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import org.junit.jupiter.api.Test;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
public class CommandParserTest {
@Test
void checkArgumentExpected() {
RootCommandNode<Object> node = CommandNode.rootNode(MessageStyle.PLAIN, "someRootNode");
var arg = CommandArgument.arg("someArg", new StringArgumentType<>(false));
node.addArgument(arg);
var parsed = ENV.PARSER.parseLiteral(node, ENV.SENDER, new String[]{}, false);
assertInstanceOf(ParseResult.ArgumentExpected.class, parsed);
}
@Test
void checkGreedyStrings() {
RootCommandNode<Object> node = CommandNode.rootNode(MessageStyle.PLAIN, "someRootNode");
var arg = CommandArgument.arg("someArg", new StringArgumentType<>(true));
node.addArgument(arg);
node.setExecutor(ENV.EMPTY_EXECUTOR);
var parsed = ENV.PARSER.parseLiteral(node, ENV.SENDER, new String[]{"part1", "part2"}, true);
Map<String, ParsedArgument<Object, ?>> parsedStr = ((ParseResult.Success<Object>) parsed).getParsedArguments();
String str = (String) parsedStr.get("someArg").getValue();
assertEquals("part1 part2", str);
}
}

View file

@ -1,12 +1,14 @@
package de.kentoj.kencommandapi.api.parser;
import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.CommandHandler;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import java.util.concurrent.CompletableFuture;
public class ENV {
public static final CommandParser<Object> PARSER = new CommandParser<>((__, ___) -> true);
public static final CommandHandler<Object> PARSER =
new CommandHandler<>((__, ___) -> {}, (__, ___) -> true);
public static final Object SENDER = new Object();
public static final CommandExecutor<Object> EMPTY_EXECUTOR = __ ->
CompletableFuture.completedFuture(Results.success(new Object()));