BAZEL + some code

This commit is contained in:
kento2 2026-08-02 21:23:36 +02:00
parent dd25337ed6
commit 4db84feb43
44 changed files with 627 additions and 389 deletions

View file

@ -6,13 +6,14 @@ java_library(
srcs = glob(["src/main/**/*.java"]),
visibility = ["//visibility:public"],
deps = [
artifact("org.projectlombok:lombok"),
artifact("com.leakyabstractions:result-api"),
artifact("com.leakyabstractions:result"),
artifact("org.jetbrains:annotations"),
artifact("com.google.guava:guava"),
artifact("org.slf4j:slf4j-api"),
artifact("net.kyori:adventure-api"),
artifact("org.projectlombok:lombok"),
],
plugins = [ "//third_party:lombok_plugin" ],
)
)
load("@rules_java//toolchains:default_java_toolchain.bzl", "default_java_toolchain")

View file

@ -1,12 +1,11 @@
package de.kentoj.kencommandapi.api;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
import de.kentoj.kencommandapi.api.platform.SendMessageMethod;
import de.kentoj.kencommandapi.internal.parser.CommandParseHelper;
import de.kentoj.kencommandapi.internal.parser.InvocationCommandParser;
import de.kentoj.kencommandapi.internal.parser.SuggestionCommandParser;
import de.kentoj.kencommandapi.api.platform.HasPermissionMethod;
import de.kentoj.kencommandapi.api.platform.SendMessageMethod;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
@ -28,23 +27,25 @@ public class CommandHandler<T> {
suggestionParser = new SuggestionCommandParser<>(parseHelper);
}
@SuppressWarnings("CodeBlock2Expr")
public void invoke(RootLiteral<T> rootLiteral, T sender, Stream<String> args) {
public void invoke(Literal<T> rootLiteral, T sender, Stream<String> args) {
// FIXME uses rootLiteral's message style
invocationParser.parse(rootLiteral, sender, args.toList().iterator())
.ifSuccessOrElse(data -> {
var ctx = new CommandContext<>(sender, data.getArguments());
data.getExecutor().execute(ctx).whenComplete((result, ex) -> {
if (ex != null)
sendMessageMethod.send(sender, rootLiteral.getMessageStyle().exception(ex));
result.ifFailure(err ->
sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err)));
});
}, err -> {
sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err));
.ifSuccessOrElse(data -> exec(rootLiteral, data),
err -> sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err)));
}
private void exec(Literal<T> rootLiteral, InvocationCommandParser<T>.ExecutionData data) {
var sender = data.getContext().getSender();
data.getExecutor().execute(data.getContext())
.whenComplete((result, ex) -> {
if (ex != null)
sendMessageMethod.send(sender, rootLiteral.getMessageStyle().exception(ex));
result.ifFailure(err ->
sendMessageMethod.send(sender, rootLiteral.getMessageStyle().err(err)));
});
}
public List<String> getSuggestions(RootLiteral<T> rootNode, T sender, String[] args, boolean trailingSpace) {
public List<String> getSuggestions(Literal<T> rootNode, T sender, String[] args, boolean trailingSpace) {
return suggestionParser.suggest(rootNode, sender, Arrays.stream(args).iterator(), trailingSpace);
}
}

View file

@ -0,0 +1,14 @@
package de.kentoj.kencommandapi.api.argument;
import lombok.Value;
import java.util.function.Predicate;
/**
* @param <V> type of the parsed argument value
*/
@Value
public class ArgumentRequirement<V> {
Predicate<V> predicate;
String message;
}

View file

@ -2,14 +2,24 @@ package de.kentoj.kencommandapi.api.argument;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import java.util.function.Predicate;
public interface ArgumentType<T, V> {
/**
* parses the given input and returns it.
* Unlike {@link CommandArgumentSpec#parseInput(String)}, this does not check any additional requirements.
*
* @throws IllegalArgumentException if string invalid
* @see CommandArgumentSpec#parseInput(String)
* @see CommandArgumentSpecBuilder#addRequirement(Predicate, String)
*/
V parseInput(String string);
V parseInputUnchecked(String input) throws IllegalArgumentException;
SuggestionProvider<T> defaultSuggestionProvider();
/**
* @see CommandArgumentSpec#getSuggestionProvider()
*/
SuggestionProvider<T> getDefaultSuggestionProvider();
/**
* @return whether this argument is greedy. Greedy arguments consume all remaining input instead of a single word.

View file

@ -1,30 +1,8 @@
package de.kentoj.kencommandapi.api.argument;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import de.kentoj.kencommandapi.internal.CommandArgumentImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
public interface CommandArgument<T, V> {
static <T, V> CommandArgument<T, V> arg(String id, ArgumentType<T, V> type) {
return new CommandArgumentImpl<>(id, type);
}
V getValue();
String getId();
ArgumentType<T, V> getType();
void setDefaultValueProvider(@Nullable Function<T, V> defaultValueProvider);
@Nullable Function<T, V> getDefaultValueProvider();
void setSuggestionProvider(@Nullable SuggestionProvider<T> suggestionProvider);
/**
* @return the explicitly set SuggestionProvider or the default suggestions for the type if unset/null
*/
@NotNull SuggestionProvider<T> getSuggestionProvider();
CommandArgumentSpec<T, V> getArgument();
}

View file

@ -0,0 +1,9 @@
package de.kentoj.kencommandapi.api.argument;
import lombok.Value;
@Value
public class CommandArgumentImpl<T, V> implements CommandArgument<T, V> {
V value;
CommandArgumentSpec<T, V> argument;
}

View file

@ -0,0 +1,32 @@
package de.kentoj.kencommandapi.api.argument;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
public sealed interface CommandArgumentSpec<T, V> permits CommandArgumentSpecImpl {
static <T, V> CommandArgumentSpecBuilder<T, V> builder(String id, ArgumentType<T, V> type) {
return new CommandArgumentSpecBuilder<>(id, type);
}
String getId();
ArgumentType<T, V> getType();
/**
* @throws IllegalArgumentException if string invalid or if a requirement is not met
*/
V parseInput(String rawInput);
@Nullable Function<CommandContext<T>, V> getDefaultValueProvider();
/**
* @return the explicitly set SuggestionProvider or the default suggestions for the type if unset/null
*/
/* explicit @NotNull to avoid confusion */
@NotNull SuggestionProvider<T> getSuggestionProvider();
}

View file

@ -0,0 +1,47 @@
package de.kentoj.kencommandapi.api.argument;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* @param <T> type of the command sender
* @param <V> type of the value
*/
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class CommandArgumentSpecBuilder<T, V> {
private final String id;
private final ArgumentType<T, V> type;
private @Nullable Function<CommandContext<T>, V> defaultValueProvider;
private @Nullable SuggestionProvider<T> suggestionProvider;
private final List<ArgumentRequirement<V>> requirements = new ArrayList<>();
public CommandArgumentSpecBuilder<T, V> withRequirement(Predicate<V> predicate, String message) {
requirements.add(new ArgumentRequirement<>(predicate, message));
return this;
}
public CommandArgumentSpecBuilder<T, V> withDefaultValue(@Nullable Function<CommandContext<T>, V> defaultValueProvider) {
this.defaultValueProvider = defaultValueProvider;
return this;
}
public CommandArgumentSpecBuilder<T, V> withSuggestionProvider(@Nullable SuggestionProvider<T> suggestionProvider) {
this.suggestionProvider = suggestionProvider;
return this;
}
public CommandArgumentSpec<T, V> build() {
return new CommandArgumentSpecImpl<>(id, type, requirements, defaultValueProvider, suggestionProvider);
}
}

View file

@ -0,0 +1,42 @@
package de.kentoj.kencommandapi.api.argument;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Function;
@RequiredArgsConstructor
public final class CommandArgumentSpecImpl<T, V> implements CommandArgumentSpec<T, V> {
@Getter
private final String id;
@Getter
private final ArgumentType<T, V> type;
private final List<ArgumentRequirement<V>> requirements;
private final Function<CommandContext<T>, V> defaultValueProvider;
private final @Nullable SuggestionProvider<T> suggestionProvider;
@Override
public V parseInput(String rawInput) {
var parsed = type.parseInputUnchecked(rawInput);
for (var requirement : requirements) {
if (!requirement.getPredicate().test(parsed))
throw new IllegalArgumentException(requirement.getMessage());
}
return parsed;
}
@Override
public @Nullable Function<CommandContext<T>, V> getDefaultValueProvider() {
return defaultValueProvider;
}
@Override
public @NotNull SuggestionProvider<T> getSuggestionProvider() {
return suggestionProvider != null ? suggestionProvider : type.getDefaultSuggestionProvider();
}
}

View file

@ -2,17 +2,16 @@ package de.kentoj.kencommandapi.api.argument.types;
import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import de.kentoj.kencommandapi.api.suggestion.NoSuggestionProvider;
public class IntegerArgumentType<T> implements ArgumentType<T, Integer> {
@Override
public Integer parseInput(String string) {
public Integer parseInputUnchecked(String string) {
return Integer.parseInt(string);
}
@Override
public SuggestionProvider<T> defaultSuggestionProvider() {
return new NoSuggestionProvider<>();
public SuggestionProvider<T> getDefaultSuggestionProvider() {
return SuggestionProvider.none();
}
}

View file

@ -2,7 +2,6 @@ package de.kentoj.kencommandapi.api.argument.types;
import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import de.kentoj.kencommandapi.api.suggestion.NoSuggestionProvider;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@ -15,13 +14,13 @@ public class StringArgumentType<T> implements ArgumentType<T, String> {
}
@Override
public String parseInput(String string) {
public String parseInputUnchecked(String string) {
return string;
}
@Override
public SuggestionProvider<T> defaultSuggestionProvider() {
return new NoSuggestionProvider<>();
public SuggestionProvider<T> getDefaultSuggestionProvider() {
return SuggestionProvider.none();
}
@Override

View file

@ -1,37 +1,37 @@
package de.kentoj.kencommandapi.api.invocation;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
import java.util.Map;
@RequiredArgsConstructor
public class CommandContext<T> {
@Getter
private final T sender;
@Getter
private final Map<String, ParsedArgument<T, ?>> parsedArguments;
public interface CommandContext<T> {
public <V> ParsedArgument<T, V> getParsedArgument(String name) {
try {
//noinspection unchecked
return (ParsedArgument<T, V>) parsedArguments.get(name);
} catch (ClassCastException e) {
throw new RuntimeException("requested argument with wrong type", e);
}
}
T getSender();
<V> CommandArgument<T, V> getParsedArgument(String id);
/**
* @return the value of the argument with the specified id
* @param <V> the type of the value of the argument with the specified id
*/
@SuppressWarnings("unchecked")
public <V> V getArg(String name) {
return (V) getParsedArgument(name).getValue();
default <V> V getArg(String id) {
return (V) getParsedArgument(id).getValue();
}
public <V> V getArg(CommandArgument<T, V> arg) {
/**
* @param arg the argument whose value to return
* @return the value of the argument
* @param <V> the type of the value of the argument
*/
default <V> V getArg(CommandArgumentSpec<T, V> arg) {
return getArg(arg.getId());
}
public <V> ParsedArgument<T, V> getParsedArgument(CommandArgument<T, V> arg) {
default <V> CommandArgument<T, V> getParsedArgument(CommandArgumentSpec<T, V> arg) {
return getParsedArgument(arg.getId());
}
Map<String, CommandArgument<T, ?>> getParsedArguments();
}

View file

@ -1,10 +0,0 @@
package de.kentoj.kencommandapi.api.invocation;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
public interface ParsedArgument<T, V> {
V getValue();
CommandArgument<T, V> getArgument();
}

View file

@ -1,10 +1,9 @@
package de.kentoj.kencommandapi.api.literal;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.internal.LiteralImpl;
import de.kentoj.kencommandapi.internal.RootLiteralImpl;
import de.kentoj.kencommandapi.api.invocation.SyncCommandExecutor;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -12,51 +11,26 @@ import java.util.List;
import java.util.Set;
public interface Literal<T> {
static <T> Literal<T> literal(String... name) {
return new LiteralImpl<>(name);
}
static <T> RootLiteral<T> rootLiteral(MessageStyle messageStyle, String...name) {
return new RootLiteralImpl<>(messageStyle, name);
static <T> LiteralBuilder<T> builder(String... name) {
return new LiteralBuilder<>(name);
}
@NotNull String[] getNames();
void setDescription(@Nullable String description);
/**
* @return what the literal does
*/
@Nullable String getDescription();
/*
* arguments
*/
/**
* @throws IllegalArgumentException if there is already an argument with the same id
* @throws IllegalStateException if a greedy argument was added to the literal previously
*/
<V> void addArgument(CommandArgument<T, V> argument);
List<CommandArgument<T, ?>> getArguments();
/*
* literals
*/
void setExecutor(@Nullable CommandExecutor<T> executor);
List<CommandArgumentSpec<T, ?>> getArguments();
@Nullable CommandExecutor<T> getExecutor();
/**
* @throws IllegalArgumentException if there is already a literal with the same name
* @throws IllegalStateException if a greedy argument was added to the literal previously
*/
void addLiteral(@NotNull Literal<T> literal);
@Nullable Literal<T> getLiteral(@NotNull String name);
@NotNull Set<Literal<T>> getLiterals();
@NotNull List<Literal<T>> getLiterals();
@Nullable String getPermission();
void setPermission(@Nullable String permission);
MessageStyle getMessageStyle();
}

View file

@ -0,0 +1,100 @@
package de.kentoj.kencommandapi.api.literal;
import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.invocation.SyncCommandExecutor;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
public final class LiteralBuilder<T> {
private final String[] names;
private @Nullable String description;
private @Nullable CommandExecutor<T> executor;
private @Nullable String permission;
private @Nullable MessageStyle messageStyle;
private final List<CommandArgumentSpec<T, ?>> arguments = new ArrayList<>();
private final List<Literal<T>> literals = new ArrayList<>();
LiteralBuilder(String... names) {
checkArgument(names.length > 0, "at least one name is required");
this.names = names;
}
/**
* @param description what the literal does
*/
public LiteralBuilder<T> withDescription(@Nullable String description) {
this.description = description;
return this;
}
public LiteralBuilder<T> withExecutor(@Nullable CommandExecutor<T> executor) {
this.executor = executor;
return this;
}
public LiteralBuilder<T> withSyncExecutor(@Nullable SyncCommandExecutor<T> executor) {
return withExecutor(executor);
}
public LiteralBuilder<T> withPermission(@Nullable String permission) {
this.permission = permission;
return this;
}
public LiteralBuilder<T> withStyle(MessageStyle messageStyle) {
this.messageStyle = messageStyle;
return this;
}
/**
* @throws IllegalArgumentException if there is already an argument with the same id
* @throws IllegalStateException if a greedy argument was added to the literal previously
*/
public LiteralBuilder<T> withArgument(CommandArgumentSpec<T, ?> argument) {
checkArgument(!hasGreedyArg(), "cannot add an argument after a greedy argument");
if (arguments.stream().anyMatch(arg -> arg.getId().equals(argument.getId())))
throw new IllegalArgumentException("cannot add multiple arguments with the same id");
this.arguments.add(argument);
return this;
}
/**
* @throws IllegalArgumentException if there is already a literal with the same name
* @throws IllegalStateException if a greedy argument was added to the literal previously
*/
public LiteralBuilder<T> withLiteral(Literal<T> literal) {
checkArgument(!hasGreedyArg(), "cannot add a literal after a greedy argument");
for (String name : literal.getNames())
for (var l : literals)
if (l.getLiteral(name) != null)
throw new IllegalArgumentException("literal with same name already exists");
this.literals.add(literal);
return this;
}
public Literal<T> build() {
return new LiteralImpl<>(
names,
literals,
arguments,
executor,
permission,
messageStyle == null ? MessageStyle.PLAIN : messageStyle
);
}
private boolean hasGreedyArg() {
if (arguments.isEmpty()) return false;
var arg = arguments.getLast();
return arg != null && arg.getType().isGreedy();
}
}

View file

@ -0,0 +1,38 @@
package de.kentoj.kencommandapi.api.literal;
import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@Getter
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class LiteralImpl<T> implements Literal<T> {
private final String[] names;
private final List<Literal<T>> literals;
private final List<CommandArgumentSpec<T, ?>> arguments;
private final CommandExecutor<T> executor;
/**
* what the literal does
*/
private String description;
private final @Nullable String permission;
private final MessageStyle messageStyle;
@Override
public @Nullable Literal<T> getLiteral(@NotNull String name) {
for (Literal<T> literal : literals) {
for (var literalName : literal.getNames()) {
if (literalName.equalsIgnoreCase(name))
return literal;
}
}
return null;
}
}

View file

@ -1,10 +0,0 @@
package de.kentoj.kencommandapi.api.literal;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
public interface RootLiteral<T> extends Literal<T> {
MessageStyle getMessageStyle();
void setMessageStyle(MessageStyle messageStyle);
}

View file

@ -1,8 +1,11 @@
package de.kentoj.kencommandapi.api.platform;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.kyori.adventure.text.Component;
public class PlainMessageStyle implements MessageStyle {
@NoArgsConstructor(access = AccessLevel.PACKAGE)
class PlainMessageStyle implements MessageStyle {
@Override
public Component ok(Component msg) {
return msg;

View file

@ -1,10 +1,14 @@
package de.kentoj.kencommandapi.api.suggestion;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class NoSuggestionProvider<T> implements SuggestionProvider<T> {
@NoArgsConstructor(access = AccessLevel.PACKAGE)
class NoSuggestionProvider<T> implements SuggestionProvider<T> {
@Override
public List<String> suggest(T __) {

View file

@ -1,10 +1,13 @@
package de.kentoj.kencommandapi.api.suggestion;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public interface SuggestionProvider<T> {
static <T> SuggestionProvider<T> none() {
return new NoSuggestionProvider<>();
}
/**
* @return an unfiltered(no permission checks or filtering by input) list of strings that may be used for the argument/literal
*/

View file

@ -1,34 +0,0 @@
package de.kentoj.kencommandapi.internal;
import de.kentoj.kencommandapi.api.argument.ArgumentType;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.suggestion.SuggestionProvider;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
@RequiredArgsConstructor
public class CommandArgumentImpl<T, V> implements CommandArgument<T, V> {
@Getter
private final String id;
@Getter
private final ArgumentType<T, V> type;
@Getter
@Setter
private @Nullable Function<T, V> defaultValueProvider = null;
@Setter
private @Nullable SuggestionProvider<T> suggestionProvider;
@Override
public @NotNull SuggestionProvider<T> getSuggestionProvider() {
if (suggestionProvider == null)
return getType().defaultSuggestionProvider();
return this.suggestionProvider;
}
}

View file

@ -0,0 +1,26 @@
package de.kentoj.kencommandapi.internal;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Map;
@RequiredArgsConstructor
public class CommandContextImpl<T> implements CommandContext<T> {
@Getter
private final T sender;
@Getter
private final Map<String, CommandArgument<T, ?>> parsedArguments;
@Override
public <V> CommandArgument<T, V> getParsedArgument(String id) {
try {
//noinspection unchecked
return (CommandArgument<T, V>) parsedArguments.get(id);
} catch (ClassCastException e) {
throw new RuntimeException("requested argument with wrong type", e);
}
}
}

View file

@ -1,72 +0,0 @@
package de.kentoj.kencommandapi.internal;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class LiteralImpl<T> implements Literal<T> {
@Getter
private final String[] names;
@Getter
private final Set<Literal<T>> literals = new HashSet<>();
@Getter
private final List<CommandArgument<T, ?>> arguments = new ArrayList<>();
@Getter
@Setter
private @Nullable CommandExecutor<T> executor;
@Getter
@Setter
private String description;
@Getter
@Setter
private @Nullable String permission = null;
public LiteralImpl(String[] names) {
if (names.length == 0) throw new IllegalArgumentException("at least one name is required");
this.names = names;
}
@Override
public void addLiteral(@NotNull Literal<T> literal) {
if (isGreedy()) throw new IllegalStateException("can't add literal to a node with a greedy argument");
for (String name : literal.getNames())
if (getLiteral(name) != null)
throw new IllegalArgumentException("literal with same name already exists");
literals.add(literal);
}
@Override
public Literal<T> getLiteral(@NotNull String name) {
for (var literal : literals)
for (String literalName : literal.getNames())
if (literalName.equalsIgnoreCase(name)) return literal;
return null;
}
@Override
public <V> void addArgument(CommandArgument<T, V> argument) {
if (isGreedy()) throw new IllegalStateException("can't add literal to a node with a greedy argument");
if (arguments.stream().anyMatch(arg -> arg.getId().equals(argument.getId())))
throw new IllegalArgumentException("argument with same identifier is already added");
arguments.add(argument);
}
private boolean isGreedy() {
if (arguments.isEmpty()) return false;
var arg = arguments.getLast();
return arg != null && arg.getType().isGreedy();
}
}

View file

@ -1,11 +0,0 @@
package de.kentoj.kencommandapi.internal;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
import lombok.Value;
@Value
public class ParsedArgumentImpl<T, V> implements ParsedArgument<T, V> {
V value;
CommandArgument<T, V> argument;
}

View file

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

View file

@ -2,12 +2,13 @@ package de.kentoj.kencommandapi.internal.parser;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.argument.CommandArgumentSpec;
import de.kentoj.kencommandapi.api.invocation.CommandContext;
import de.kentoj.kencommandapi.api.invocation.CommandExecutor;
import de.kentoj.kencommandapi.api.invocation.ParsedArgument;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import de.kentoj.kencommandapi.internal.ParsedArgumentImpl;
import de.kentoj.kencommandapi.api.argument.CommandArgumentImpl;
import de.kentoj.kencommandapi.internal.CommandContextImpl;
import lombok.RequiredArgsConstructor;
import lombok.Value;
@ -20,17 +21,17 @@ public class InvocationCommandParser<T> {
private final CommandParseHelper<T> helper;
public Result<ExecutionData, String> parse(
RootLiteral<T> rootLiteral,
Literal<T> rootLiteral,
T sender,
Iterator<String> tokenIter
) {
Literal<T> currentLiteral = rootLiteral;
Map<String, ParsedArgument<T, ?>> parsedArguments = new HashMap<>();
var ctx = new CommandContextImpl<>(sender, new HashMap<>());
while (true) {
{
var result = processArguments(currentLiteral, sender, tokenIter)
.ifSuccess(parsedArguments::putAll);
var result = processArguments(currentLiteral, ctx, tokenIter)
.ifSuccess(ctx.getParsedArguments()::putAll);
if (result.hasFailure())
return result.mapSuccess(__ -> null);
}
@ -49,35 +50,35 @@ public class InvocationCommandParser<T> {
currentLiteral = nextLiteral;
}
return Results.success(new ExecutionData(currentLiteral.getExecutor(), parsedArguments));
return Results.success(new ExecutionData(currentLiteral.getExecutor(), ctx));
}
private Result<Map<String, ParsedArgument<T, ?>>, String> processArguments(
private Result<Map<String, CommandArgument<T, ?>>, String> processArguments(
Literal<T> literal,
T sender,
CommandContext<T> ctx,
Iterator<String> tokenIter
) {
Map<String, ParsedArgument<T, ?>> parsedArguments = new HashMap<>();
Map<String, CommandArgument<T, ?>> parsedArguments = new HashMap<>();
for (var _argument : literal.getArguments()) {
@SuppressWarnings("unchecked")
var argument = ((CommandArgument<T, Object>) _argument);
var argument = ((CommandArgumentSpec<T, Object>) _argument);
Object value;
if (!tokenIter.hasNext()) {
if (argument.getDefaultValueProvider() == null)
return Results.failure("Argument expected: " + argument.getId());
value = argument.getDefaultValueProvider().apply(sender);
value = argument.getDefaultValueProvider().apply(ctx);
} else {
var input = helper.parseInput(tokenIter, argument.getType().isGreedy());
try {
value = argument.getType().parseInput(input);
value = argument.parseInput(input);
} catch (IllegalArgumentException ex) {
return Results.failure("Illegal argument '" + input + "' for " + argument.getId() + ": " + ex.getMessage());
}
}
parsedArguments.put(argument.getId(), new ParsedArgumentImpl<>(value, argument));
parsedArguments.put(argument.getId(), new CommandArgumentImpl<>(value, argument));
}
return Results.success(parsedArguments);
@ -86,6 +87,6 @@ public class InvocationCommandParser<T> {
@Value
public class ExecutionData {
CommandExecutor<T> executor;
Map<String, ParsedArgument<T, ?>> arguments;
CommandContext<T> context;
}
}

View file

@ -1,7 +1,6 @@
package de.kentoj.kencommandapi.internal.parser;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import lombok.RequiredArgsConstructor;
import org.jetbrains.annotations.Nullable;
@ -16,7 +15,7 @@ public class SuggestionCommandParser<T> {
private final CommandParseHelper<T> helper;
public List<String> suggest(
RootLiteral<T> rootLiteral,
Literal<T> rootLiteral,
T sender,
Iterator<String> tokenIter,
boolean suggestNext

View file

@ -3,7 +3,7 @@ package de.kentoj.kencommandapi.api.parser;
import de.kentoj.kencommandapi.api.argument.CommandArgument;
import de.kentoj.kencommandapi.api.argument.types.StringArgumentType;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.literal.RootLiteral;
import de.kentoj.kencommandapi.api.literal.Literal;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import de.kentoj.kencommandapi.internal.parser.SuggestionCommandParser;
import org.junit.jupiter.api.Test;
@ -18,7 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class SuggestionCommandParserTest {
private final SuggestionCommandParser<Object> suggestionParser;
private final RootLiteral<Object> rootLiteral;
private final Literal<Object> rootLiteral;
public SuggestionCommandParserTest() {
rootLiteral = Literal.rootLiteral(MessageStyle.PLAIN, "someRootNode");