parent
d3864414aa
commit
ec535dad7f
9 changed files with 459 additions and 80 deletions
|
|
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Core: Add flag yielding modes to `StringArgument` and `StringArrayArgument` ([#367](https://github.com/Incendo/cloud/pull/367))
|
- Core: Add flag yielding modes to `StringArgument` and `StringArrayArgument` ([#367](https://github.com/Incendo/cloud/pull/367))
|
||||||
- Core: Add [apiguardian](https://github.com/apiguardian-team/apiguardian) `@API` annotations ([#368](https://github.com/Incendo/cloud/pull/368))
|
- Core: Add [apiguardian](https://github.com/apiguardian-team/apiguardian) `@API` annotations ([#368](https://github.com/Incendo/cloud/pull/368))
|
||||||
- Core: Deprecate prefixed getters/setters in `CommandManager` ([#377](https://github.com/Incendo/cloud/pull/377))
|
- Core: Deprecate prefixed getters/setters in `CommandManager` ([#377](https://github.com/Incendo/cloud/pull/377))
|
||||||
|
- Core: Add repeatable flags ([#378](https://github.com/Incendo/cloud/pull/378))
|
||||||
- Annotations: Annotation string processors ([#353](https://github.com/Incendo/cloud/pull/353))
|
- Annotations: Annotation string processors ([#353](https://github.com/Incendo/cloud/pull/353))
|
||||||
- Annotations: `@CommandContainer` annotation processing ([#364](https://github.com/Incendo/cloud/pull/364))
|
- Annotations: `@CommandContainer` annotation processing ([#364](https://github.com/Incendo/cloud/pull/364))
|
||||||
- Annotations: `@CommandMethod` annotation processing for compile-time validation ([#365](https://github.com/Incendo/cloud/pull/365))
|
- Annotations: `@CommandMethod` annotation processing for compile-time validation ([#365](https://github.com/Incendo/cloud/pull/365))
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
import org.apiguardian.api.API;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -94,4 +95,13 @@ public @interface Flag {
|
||||||
* @since 1.6.0
|
* @since 1.6.0
|
||||||
*/
|
*/
|
||||||
@NonNull String permission() default "";
|
@NonNull String permission() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the flag can be repeated.
|
||||||
|
*
|
||||||
|
* @return whether the flag can be repeated
|
||||||
|
* @since 1.7.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.7.0")
|
||||||
|
boolean repeatable() default false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import cloud.commandframework.arguments.flags.CommandFlag;
|
||||||
import cloud.commandframework.arguments.parser.ArgumentParser;
|
import cloud.commandframework.arguments.parser.ArgumentParser;
|
||||||
import cloud.commandframework.arguments.parser.ParserRegistry;
|
import cloud.commandframework.arguments.parser.ParserRegistry;
|
||||||
import cloud.commandframework.permission.Permission;
|
import cloud.commandframework.permission.Permission;
|
||||||
|
import io.leangen.geantyref.GenericTypeReflector;
|
||||||
import io.leangen.geantyref.TypeToken;
|
import io.leangen.geantyref.TypeToken;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
@ -65,15 +66,34 @@ final class FlagExtractor implements Function<@NonNull Method, Collection<@NonNu
|
||||||
}
|
}
|
||||||
final Flag flag = parameter.getAnnotation(Flag.class);
|
final Flag flag = parameter.getAnnotation(Flag.class);
|
||||||
final String flagName = this.annotationParser.processString(flag.value());
|
final String flagName = this.annotationParser.processString(flag.value());
|
||||||
final CommandFlag.Builder<Void> builder = this.commandManager
|
|
||||||
|
CommandFlag.Builder<Void> builder = this.commandManager
|
||||||
.flagBuilder(this.annotationParser.processString(flagName))
|
.flagBuilder(this.annotationParser.processString(flagName))
|
||||||
.withDescription(ArgumentDescription.of(this.annotationParser.processString(flag.description())))
|
.withDescription(ArgumentDescription.of(this.annotationParser.processString(flag.description())))
|
||||||
.withAliases(this.annotationParser.processStrings(flag.aliases()))
|
.withAliases(this.annotationParser.processStrings(flag.aliases()))
|
||||||
.withPermission(Permission.of(this.annotationParser.processString(flag.permission())));
|
.withPermission(Permission.of(this.annotationParser.processString(flag.permission())));
|
||||||
|
if (flag.repeatable()) {
|
||||||
|
builder = builder.asRepeatable();
|
||||||
|
}
|
||||||
|
|
||||||
if (parameter.getType().equals(boolean.class)) {
|
if (parameter.getType().equals(boolean.class)) {
|
||||||
flags.add(builder.build());
|
flags.add(builder.build());
|
||||||
} else {
|
} else {
|
||||||
final TypeToken<?> token = TypeToken.get(parameter.getType());
|
final TypeToken<?> token;
|
||||||
|
if (flag.repeatable() && Collection.class.isAssignableFrom(parameter.getType())) {
|
||||||
|
token = TypeToken.get(GenericTypeReflector.getTypeParameter(
|
||||||
|
parameter.getParameterizedType(),
|
||||||
|
Collection.class.getTypeParameters()[0]
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
token = TypeToken.get(parameter.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.equals(TypeToken.get(boolean.class))) {
|
||||||
|
flags.add(builder.build());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
final Collection<Annotation> annotations = Arrays.asList(parameter.getAnnotations());
|
final Collection<Annotation> annotations = Arrays.asList(parameter.getAnnotations());
|
||||||
final ParserRegistry<?> registry = this.commandManager.parserRegistry();
|
final ParserRegistry<?> registry = this.commandManager.parserRegistry();
|
||||||
final ArgumentParser<?, ?> parser;
|
final ArgumentParser<?, ?> parser;
|
||||||
|
|
|
||||||
|
|
@ -126,8 +126,10 @@ public class MethodCommandExecutionHandler<C> implements CommandExecutionHandler
|
||||||
} else if (parameter.isAnnotationPresent(Flag.class)) {
|
} else if (parameter.isAnnotationPresent(Flag.class)) {
|
||||||
final Flag flag = parameter.getAnnotation(Flag.class);
|
final Flag flag = parameter.getAnnotation(Flag.class);
|
||||||
final String flagName = this.annotationParser.processString(flag.value());
|
final String flagName = this.annotationParser.processString(flag.value());
|
||||||
if (parameter.getType() == boolean.class) {
|
if (parameter.getType().equals(boolean.class)) {
|
||||||
arguments.add(flagContext.isPresent(flagName));
|
arguments.add(flagContext.isPresent(flagName));
|
||||||
|
} else if (flag.repeatable() && parameter.getType().isAssignableFrom(List.class)) {
|
||||||
|
arguments.add(flagContext.getAll(flagName));
|
||||||
} else {
|
} else {
|
||||||
arguments.add(flagContext.getValue(flagName, null));
|
arguments.add(flagContext.getValue(flagName, null));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Alexander Söderberg & Contributors
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package cloud.commandframework.annotations.feature;
|
||||||
|
|
||||||
|
import cloud.commandframework.CommandManager;
|
||||||
|
import cloud.commandframework.annotations.AnnotationParser;
|
||||||
|
import cloud.commandframework.annotations.CommandMethod;
|
||||||
|
import cloud.commandframework.annotations.Flag;
|
||||||
|
import cloud.commandframework.annotations.TestCommandManager;
|
||||||
|
import cloud.commandframework.annotations.TestCommandSender;
|
||||||
|
import cloud.commandframework.arguments.parser.StandardParameters;
|
||||||
|
import cloud.commandframework.execution.CommandExecutionCoordinator;
|
||||||
|
import cloud.commandframework.execution.CommandResult;
|
||||||
|
import cloud.commandframework.internal.CommandRegistrationHandler;
|
||||||
|
import cloud.commandframework.meta.CommandMeta;
|
||||||
|
import cloud.commandframework.meta.SimpleCommandMeta;
|
||||||
|
import io.leangen.geantyref.TypeToken;
|
||||||
|
import java.util.Collection;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
class RepeatableFlagTest {
|
||||||
|
|
||||||
|
private CommandManager<TestCommandSender> commandManager;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() {
|
||||||
|
this.commandManager = new TestCommandManager();
|
||||||
|
final AnnotationParser<TestCommandSender> annotationParser = new AnnotationParser<>(
|
||||||
|
this.commandManager,
|
||||||
|
TestCommandSender.class,
|
||||||
|
p -> SimpleCommandMeta.empty()
|
||||||
|
);
|
||||||
|
annotationParser.parse(new TestClassA());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRepeatableFlagParsing() {
|
||||||
|
// Act
|
||||||
|
final CommandResult<TestCommandSender> result = this.commandManager.executeCommand(
|
||||||
|
new TestCommandSender(),
|
||||||
|
"test --flag one --flag two --flag three"
|
||||||
|
).join();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(result.getCommandContext().flags().getAll("flag")).containsExactly("one", "two", "three");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final class TestClassA {
|
||||||
|
|
||||||
|
@CommandMethod("test")
|
||||||
|
public void command(@Flag(value = "flag", repeatable = true) Collection<String> flags) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -41,6 +41,7 @@ import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -186,8 +187,8 @@ public final class FlagArgument<C> extends CommandArgument<C, Object> {
|
||||||
final @NonNull String input
|
final @NonNull String input
|
||||||
) {
|
) {
|
||||||
/* Check if we have a last flag stored */
|
/* Check if we have a last flag stored */
|
||||||
final String lastArg = commandContext.getOrDefault(FLAG_META_KEY, "");
|
final String lastArg = Objects.requireNonNull(commandContext.getOrDefault(FLAG_META_KEY, ""));
|
||||||
if (lastArg.isEmpty() || !lastArg.startsWith("-")) {
|
if (!lastArg.startsWith("-")) {
|
||||||
final String rawInput = commandContext.getRawInputJoined();
|
final String rawInput = commandContext.getRawInputJoined();
|
||||||
/* Collection containing all used flags */
|
/* Collection containing all used flags */
|
||||||
final List<CommandFlag<?>> usedFlags = new LinkedList<>();
|
final List<CommandFlag<?>> usedFlags = new LinkedList<>();
|
||||||
|
|
@ -220,38 +221,30 @@ public final class FlagArgument<C> extends CommandArgument<C, Object> {
|
||||||
final List<String> strings = new LinkedList<>();
|
final List<String> strings = new LinkedList<>();
|
||||||
/* Recommend "primary" flags */
|
/* Recommend "primary" flags */
|
||||||
for (final CommandFlag<?> flag : this.flags) {
|
for (final CommandFlag<?> flag : this.flags) {
|
||||||
if (usedFlags.contains(flag) || !commandContext.hasPermission(flag.permission())) {
|
if (usedFlags.contains(flag) && flag.mode() != CommandFlag.FlagMode.REPEATABLE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
strings.add(
|
if (!commandContext.hasPermission(flag.permission())) {
|
||||||
String.format(
|
continue;
|
||||||
"--%s",
|
}
|
||||||
flag.getName()
|
|
||||||
)
|
strings.add(String.format("--%s", flag.getName()));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
/* Recommend aliases */
|
/* Recommend aliases */
|
||||||
final boolean suggestCombined = input.length() > 1 && input.charAt(0) == '-' && input.charAt(1) != '-';
|
final boolean suggestCombined = input.length() > 1 && input.charAt(0) == '-' && input.charAt(1) != '-';
|
||||||
for (final CommandFlag<?> flag : this.flags) {
|
for (final CommandFlag<?> flag : this.flags) {
|
||||||
if (usedFlags.contains(flag) || !commandContext.hasPermission(flag.permission())) {
|
if (usedFlags.contains(flag) && flag.mode() != CommandFlag.FlagMode.REPEATABLE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!commandContext.hasPermission(flag.permission())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (final String alias : flag.getAliases()) {
|
for (final String alias : flag.getAliases()) {
|
||||||
if (suggestCombined && flag.getCommandArgument() == null) {
|
if (suggestCombined && flag.getCommandArgument() == null) {
|
||||||
strings.add(
|
strings.add(String.format("%s%s", input, alias));
|
||||||
String.format(
|
|
||||||
"%s%s",
|
|
||||||
input,
|
|
||||||
alias
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
strings.add(
|
strings.add(String.format("-%s", alias));
|
||||||
String.format(
|
|
||||||
"-%s",
|
|
||||||
alias
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -348,31 +341,33 @@ public final class FlagArgument<C> extends CommandArgument<C, Object> {
|
||||||
final String flagName = string.substring(1);
|
final String flagName = string.substring(1);
|
||||||
if (flagName.length() > 1) {
|
if (flagName.length() > 1) {
|
||||||
boolean oneAdded = false;
|
boolean oneAdded = false;
|
||||||
/* This is a multi-alias flag, find all flags that apply */
|
for (int i = 0; i < flagName.length(); i++) {
|
||||||
for (final CommandFlag<?> flag : FlagArgumentParser.this.flags) {
|
final String parsedFlag = Character.toString(flagName.charAt(i))
|
||||||
if (flag.getCommandArgument() != null) {
|
.toLowerCase(Locale.ENGLISH);
|
||||||
continue;
|
for (final CommandFlag<?> candidateFlag : FlagArgumentParser.this.flags) {
|
||||||
}
|
if (candidateFlag.getCommandArgument() != null) {
|
||||||
for (final String alias : flag.getAliases()) {
|
continue;
|
||||||
if (flagName.toLowerCase(Locale.ENGLISH).contains(alias.toLowerCase(Locale.ENGLISH))) {
|
|
||||||
if (parsedFlags.contains(flag)) {
|
|
||||||
return ArgumentParseResult.failure(new FlagParseException(
|
|
||||||
string,
|
|
||||||
FailureReason.DUPLICATE_FLAG,
|
|
||||||
commandContext
|
|
||||||
));
|
|
||||||
} else if (!commandContext.hasPermission(flag.permission())) {
|
|
||||||
return ArgumentParseResult.failure(new FlagParseException(
|
|
||||||
string,
|
|
||||||
FailureReason.NO_PERMISSION,
|
|
||||||
commandContext
|
|
||||||
));
|
|
||||||
}
|
|
||||||
parsedFlags.add(flag);
|
|
||||||
commandContext.flags().addPresenceFlag(flag);
|
|
||||||
oneAdded = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (candidateFlag.getAliases().contains(parsedFlag)) {
|
||||||
|
if (parsedFlags.contains(candidateFlag)
|
||||||
|
&& candidateFlag.mode() != CommandFlag.FlagMode.REPEATABLE) {
|
||||||
|
return ArgumentParseResult.failure(new FlagParseException(
|
||||||
|
string,
|
||||||
|
FailureReason.DUPLICATE_FLAG,
|
||||||
|
commandContext
|
||||||
|
));
|
||||||
|
} else if (!commandContext.hasPermission(candidateFlag.permission())) {
|
||||||
|
return ArgumentParseResult.failure(new FlagParseException(
|
||||||
|
string,
|
||||||
|
FailureReason.NO_PERMISSION,
|
||||||
|
commandContext
|
||||||
|
));
|
||||||
|
}
|
||||||
|
parsedFlags.add(candidateFlag);
|
||||||
|
commandContext.flags().addPresenceFlag(candidateFlag);
|
||||||
|
oneAdded = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* We need to parse at least one flag */
|
/* We need to parse at least one flag */
|
||||||
|
|
@ -402,7 +397,7 @@ public final class FlagArgument<C> extends CommandArgument<C, Object> {
|
||||||
FailureReason.UNKNOWN_FLAG,
|
FailureReason.UNKNOWN_FLAG,
|
||||||
commandContext
|
commandContext
|
||||||
));
|
));
|
||||||
} else if (parsedFlags.contains(currentFlag)) {
|
} else if (parsedFlags.contains(currentFlag) && currentFlag.mode() != CommandFlag.FlagMode.REPEATABLE) {
|
||||||
return ArgumentParseResult.failure(new FlagParseException(
|
return ArgumentParseResult.failure(new FlagParseException(
|
||||||
string,
|
string,
|
||||||
FailureReason.DUPLICATE_FLAG,
|
FailureReason.DUPLICATE_FLAG,
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ public final class CommandFlag<T> {
|
||||||
private final @NonNull String @NonNull [] aliases;
|
private final @NonNull String @NonNull [] aliases;
|
||||||
private final @NonNull ArgumentDescription description;
|
private final @NonNull ArgumentDescription description;
|
||||||
private final @NonNull CommandPermission permission;
|
private final @NonNull CommandPermission permission;
|
||||||
|
private final @NonNull FlagMode mode;
|
||||||
|
|
||||||
private final @Nullable CommandArgument<?, T> commandArgument;
|
private final @Nullable CommandArgument<?, T> commandArgument;
|
||||||
|
|
||||||
|
|
@ -59,13 +60,15 @@ public final class CommandFlag<T> {
|
||||||
final @NonNull String @NonNull [] aliases,
|
final @NonNull String @NonNull [] aliases,
|
||||||
final @NonNull ArgumentDescription description,
|
final @NonNull ArgumentDescription description,
|
||||||
final @NonNull CommandPermission permission,
|
final @NonNull CommandPermission permission,
|
||||||
final @Nullable CommandArgument<?, T> commandArgument
|
final @Nullable CommandArgument<?, T> commandArgument,
|
||||||
|
final @NonNull FlagMode mode
|
||||||
) {
|
) {
|
||||||
this.name = Objects.requireNonNull(name, "name cannot be null");
|
this.name = Objects.requireNonNull(name, "name cannot be null");
|
||||||
this.aliases = Objects.requireNonNull(aliases, "aliases cannot be null");
|
this.aliases = Objects.requireNonNull(aliases, "aliases cannot be null");
|
||||||
this.description = Objects.requireNonNull(description, "description cannot be null");
|
this.description = Objects.requireNonNull(description, "description cannot be null");
|
||||||
this.permission = Objects.requireNonNull(permission, "permission cannot be null");
|
this.permission = Objects.requireNonNull(permission, "permission cannot be null");
|
||||||
this.commandArgument = commandArgument;
|
this.commandArgument = commandArgument;
|
||||||
|
this.mode = Objects.requireNonNull(mode, "mode cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -96,6 +99,16 @@ public final class CommandFlag<T> {
|
||||||
return Arrays.asList(this.aliases);
|
return Arrays.asList(this.aliases);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link FlagMode mode} of this flag.
|
||||||
|
*
|
||||||
|
* @return the flag mode
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.7.0")
|
||||||
|
public @NonNull FlagMode mode() {
|
||||||
|
return this.mode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the flag description
|
* Get the flag description
|
||||||
*
|
*
|
||||||
|
|
@ -174,23 +187,26 @@ public final class CommandFlag<T> {
|
||||||
private final ArgumentDescription description;
|
private final ArgumentDescription description;
|
||||||
private final CommandPermission permission;
|
private final CommandPermission permission;
|
||||||
private final CommandArgument<?, T> commandArgument;
|
private final CommandArgument<?, T> commandArgument;
|
||||||
|
private final FlagMode mode;
|
||||||
|
|
||||||
private Builder(
|
private Builder(
|
||||||
final @NonNull String name,
|
final @NonNull String name,
|
||||||
final @NonNull String[] aliases,
|
final @NonNull String[] aliases,
|
||||||
final @NonNull ArgumentDescription description,
|
final @NonNull ArgumentDescription description,
|
||||||
final @NonNull CommandPermission permission,
|
final @NonNull CommandPermission permission,
|
||||||
final @Nullable CommandArgument<?, T> commandArgument
|
final @Nullable CommandArgument<?, T> commandArgument,
|
||||||
|
final @NonNull FlagMode mode
|
||||||
) {
|
) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.aliases = aliases;
|
this.aliases = aliases;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.permission = permission;
|
this.permission = permission;
|
||||||
this.commandArgument = commandArgument;
|
this.commandArgument = commandArgument;
|
||||||
|
this.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Builder(final @NonNull String name) {
|
private Builder(final @NonNull String name) {
|
||||||
this(name, new String[0], ArgumentDescription.empty(), Permission.empty(), null);
|
this(name, new String[0], ArgumentDescription.empty(), Permission.empty(), null, FlagMode.SINGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -200,7 +216,7 @@ public final class CommandFlag<T> {
|
||||||
* @param aliases Flag aliases
|
* @param aliases Flag aliases
|
||||||
* @return New builder instance
|
* @return New builder instance
|
||||||
*/
|
*/
|
||||||
public Builder<T> withAliases(final @NonNull String... aliases) {
|
public @NonNull Builder<T> withAliases(final @NonNull String... aliases) {
|
||||||
final Set<String> filteredAliases = new HashSet<>();
|
final Set<String> filteredAliases = new HashSet<>();
|
||||||
for (final String alias : aliases) {
|
for (final String alias : aliases) {
|
||||||
if (alias.isEmpty()) {
|
if (alias.isEmpty()) {
|
||||||
|
|
@ -221,7 +237,8 @@ public final class CommandFlag<T> {
|
||||||
filteredAliases.toArray(new String[0]),
|
filteredAliases.toArray(new String[0]),
|
||||||
this.description,
|
this.description,
|
||||||
this.permission,
|
this.permission,
|
||||||
this.commandArgument
|
this.commandArgument,
|
||||||
|
this.mode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,7 +251,7 @@ public final class CommandFlag<T> {
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@API(status = API.Status.DEPRECATED, since = "1.4.0")
|
@API(status = API.Status.DEPRECATED, since = "1.4.0")
|
||||||
public Builder<T> withDescription(final cloud.commandframework.@NonNull Description description) {
|
public @NonNull Builder<T> withDescription(final cloud.commandframework.@NonNull Description description) {
|
||||||
return this.withDescription((ArgumentDescription) description);
|
return this.withDescription((ArgumentDescription) description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,8 +263,8 @@ public final class CommandFlag<T> {
|
||||||
* @since 1.4.0
|
* @since 1.4.0
|
||||||
*/
|
*/
|
||||||
@API(status = API.Status.STABLE, since = "1.4.0")
|
@API(status = API.Status.STABLE, since = "1.4.0")
|
||||||
public Builder<T> withDescription(final @NonNull ArgumentDescription description) {
|
public @NonNull Builder<T> withDescription(final @NonNull ArgumentDescription description) {
|
||||||
return new Builder<>(this.name, this.aliases, description, this.permission, this.commandArgument);
|
return new Builder<>(this.name, this.aliases, description, this.permission, this.commandArgument, this.mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -257,8 +274,8 @@ public final class CommandFlag<T> {
|
||||||
* @param <N> New argument type
|
* @param <N> New argument type
|
||||||
* @return New builder instance
|
* @return New builder instance
|
||||||
*/
|
*/
|
||||||
public <N> Builder<N> withArgument(final @NonNull CommandArgument<?, N> argument) {
|
public <N> @NonNull Builder<N> withArgument(final @NonNull CommandArgument<?, N> argument) {
|
||||||
return new Builder<>(this.name, this.aliases, this.description, this.permission, argument);
|
return new Builder<>(this.name, this.aliases, this.description, this.permission, argument, this.mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -268,7 +285,7 @@ public final class CommandFlag<T> {
|
||||||
* @param <N> New argument type
|
* @param <N> New argument type
|
||||||
* @return New builder instance
|
* @return New builder instance
|
||||||
*/
|
*/
|
||||||
public <N> Builder<N> withArgument(final CommandArgument.@NonNull Builder<?, N> builder) {
|
public <N> @NonNull Builder<N> withArgument(final CommandArgument.@NonNull Builder<?, N> builder) {
|
||||||
return this.withArgument(builder.build());
|
return this.withArgument(builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -280,8 +297,26 @@ public final class CommandFlag<T> {
|
||||||
* @since 1.6.0
|
* @since 1.6.0
|
||||||
*/
|
*/
|
||||||
@API(status = API.Status.STABLE, since = "1.6.0")
|
@API(status = API.Status.STABLE, since = "1.6.0")
|
||||||
public Builder<T> withPermission(final @NonNull CommandPermission permission) {
|
public @NonNull Builder<T> withPermission(final @NonNull CommandPermission permission) {
|
||||||
return new Builder<>(this.name, this.aliases, this.description, permission, this.commandArgument);
|
return new Builder<>(this.name, this.aliases, this.description, permission, this.commandArgument, this.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the flag as {@link FlagMode#REPEATABLE}.
|
||||||
|
*
|
||||||
|
* @return new builder instance
|
||||||
|
* @since 1.7.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.7.0")
|
||||||
|
public @NonNull Builder<T> asRepeatable() {
|
||||||
|
return new Builder<>(
|
||||||
|
this.name,
|
||||||
|
this.aliases,
|
||||||
|
this.description,
|
||||||
|
this.permission,
|
||||||
|
this.commandArgument,
|
||||||
|
FlagMode.REPEATABLE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -290,7 +325,29 @@ public final class CommandFlag<T> {
|
||||||
* @return Constructed instance
|
* @return Constructed instance
|
||||||
*/
|
*/
|
||||||
public @NonNull CommandFlag<T> build() {
|
public @NonNull CommandFlag<T> build() {
|
||||||
return new CommandFlag<>(this.name, this.aliases, this.description, this.permission, this.commandArgument);
|
return new CommandFlag<>(
|
||||||
|
this.name,
|
||||||
|
this.aliases,
|
||||||
|
this.description,
|
||||||
|
this.permission,
|
||||||
|
this.commandArgument,
|
||||||
|
this.mode
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@API(status = API.Status.STABLE, since = "1.7.0")
|
||||||
|
public enum FlagMode {
|
||||||
|
/**
|
||||||
|
* Only a single value can be provided for the flag, and should be extracted
|
||||||
|
* using {@link FlagContext#get(CommandFlag)}.
|
||||||
|
*/
|
||||||
|
SINGLE,
|
||||||
|
/**
|
||||||
|
* Multiple values can be provided for the flag, and sdhould be extracted
|
||||||
|
* using {@link FlagContext#getAll(CommandFlag)}.
|
||||||
|
*/
|
||||||
|
REPEATABLE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,11 @@
|
||||||
//
|
//
|
||||||
package cloud.commandframework.arguments.flags;
|
package cloud.commandframework.arguments.flags;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import org.apiguardian.api.API;
|
import org.apiguardian.api.API;
|
||||||
|
|
@ -34,6 +38,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
* Flag value mappings
|
* Flag value mappings
|
||||||
*/
|
*/
|
||||||
@API(status = API.Status.STABLE)
|
@API(status = API.Status.STABLE)
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public final class FlagContext {
|
public final class FlagContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -41,7 +46,7 @@ public final class FlagContext {
|
||||||
*/
|
*/
|
||||||
public static final Object FLAG_PRESENCE_VALUE = new Object();
|
public static final Object FLAG_PRESENCE_VALUE = new Object();
|
||||||
|
|
||||||
private final Map<String, Object> flagValues;
|
private final Map<String, List> flagValues;
|
||||||
|
|
||||||
private FlagContext() {
|
private FlagContext() {
|
||||||
this.flagValues = new HashMap<>();
|
this.flagValues = new HashMap<>();
|
||||||
|
|
@ -62,7 +67,10 @@ public final class FlagContext {
|
||||||
* @param flag Flag instance
|
* @param flag Flag instance
|
||||||
*/
|
*/
|
||||||
public void addPresenceFlag(final @NonNull CommandFlag<?> flag) {
|
public void addPresenceFlag(final @NonNull CommandFlag<?> flag) {
|
||||||
this.flagValues.put(flag.getName(), FLAG_PRESENCE_VALUE);
|
((List<Object>) this.flagValues.computeIfAbsent(
|
||||||
|
flag.getName(),
|
||||||
|
$ -> new ArrayList<>()
|
||||||
|
)).add(FLAG_PRESENCE_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -76,7 +84,36 @@ public final class FlagContext {
|
||||||
final @NonNull CommandFlag<T> flag,
|
final @NonNull CommandFlag<T> flag,
|
||||||
final @NonNull T value
|
final @NonNull T value
|
||||||
) {
|
) {
|
||||||
this.flagValues.put(flag.getName(), value);
|
((List<T>) this.flagValues.computeIfAbsent(
|
||||||
|
flag.getName(),
|
||||||
|
$ -> new ArrayList<>()
|
||||||
|
)).add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of values associated with the given {@code flag}.
|
||||||
|
*
|
||||||
|
* @param flag the flag
|
||||||
|
* @param <T> the flag value type
|
||||||
|
* @return the number of values associated with the flag
|
||||||
|
* @since 1.7.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.7.0")
|
||||||
|
public <T> int count(final @NonNull CommandFlag<T> flag) {
|
||||||
|
return this.getAll(flag).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of values associated with the given {@code flag}.
|
||||||
|
*
|
||||||
|
* @param flag the flag
|
||||||
|
* @param <T> the flag value type
|
||||||
|
* @return the number of values associated with the flag
|
||||||
|
* @since 1.7.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.7.0")
|
||||||
|
public <T> int count(final @NonNull String flag) {
|
||||||
|
return this.getAll(flag).size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -88,8 +125,8 @@ public final class FlagContext {
|
||||||
* else {@code false}
|
* else {@code false}
|
||||||
*/
|
*/
|
||||||
public boolean isPresent(final @NonNull String flag) {
|
public boolean isPresent(final @NonNull String flag) {
|
||||||
final Object value = this.flagValues.get(flag);
|
final List value = this.flagValues.get(flag);
|
||||||
return FLAG_PRESENCE_VALUE.equals(value);
|
return value != null && !value.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -107,7 +144,12 @@ public final class FlagContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a flag value as an optional. Will be empty if the value is not present.
|
* Returns a flag value.
|
||||||
|
* <p>
|
||||||
|
* If using {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#SINGLE}
|
||||||
|
* then this returns the only value, if it has been specified. If using
|
||||||
|
* {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#REPEATABLE} then
|
||||||
|
* it'll return the first value.
|
||||||
*
|
*
|
||||||
* @param name Flag name
|
* @param name Flag name
|
||||||
* @param <T> Value type
|
* @param <T> Value type
|
||||||
|
|
@ -118,16 +160,20 @@ public final class FlagContext {
|
||||||
public <T> @NonNull Optional<T> getValue(
|
public <T> @NonNull Optional<T> getValue(
|
||||||
final @NonNull String name
|
final @NonNull String name
|
||||||
) {
|
) {
|
||||||
final Object value = this.flagValues.get(name);
|
final List value = this.flagValues.get(name);
|
||||||
if (value == null) {
|
if (value == null || value.isEmpty()) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
@SuppressWarnings("unchecked") final T casted = (T) value;
|
return Optional.of((T) value.get(0));
|
||||||
return Optional.of(casted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a flag value as an optional. Will be empty if the value is not present.
|
* Returns a flag value.
|
||||||
|
* <p>
|
||||||
|
* If using {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#SINGLE}
|
||||||
|
* then this returns the only value, if it has been specified. If using
|
||||||
|
* {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#REPEATABLE} then
|
||||||
|
* it'll return the first value.
|
||||||
*
|
*
|
||||||
* @param flag Flag type
|
* @param flag Flag type
|
||||||
* @param <T> Value type
|
* @param <T> Value type
|
||||||
|
|
@ -142,7 +188,12 @@ public final class FlagContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a flag value
|
* Returns a flag value.
|
||||||
|
* <p>
|
||||||
|
* If using {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#SINGLE}
|
||||||
|
* then this returns the only value, if it has been specified. If using
|
||||||
|
* {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#REPEATABLE} then
|
||||||
|
* it'll return the first value.
|
||||||
*
|
*
|
||||||
* @param name Flag name
|
* @param name Flag name
|
||||||
* @param defaultValue Default value
|
* @param defaultValue Default value
|
||||||
|
|
@ -157,7 +208,12 @@ public final class FlagContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a flag value
|
* Returns a flag value.
|
||||||
|
* <p>
|
||||||
|
* If using {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#SINGLE}
|
||||||
|
* then this returns the only value, if it has been specified. If using
|
||||||
|
* {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#REPEATABLE} then
|
||||||
|
* it'll return the first value.
|
||||||
*
|
*
|
||||||
* @param name Flag value
|
* @param name Flag value
|
||||||
* @param defaultValue Default value
|
* @param defaultValue Default value
|
||||||
|
|
@ -234,7 +290,12 @@ public final class FlagContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a flag value
|
* Returns a flag value.
|
||||||
|
* <p>
|
||||||
|
* If using {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#SINGLE}
|
||||||
|
* then this returns the only value, if it has been specified. If using
|
||||||
|
* {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#REPEATABLE} then
|
||||||
|
* it'll return the first value.
|
||||||
*
|
*
|
||||||
* @param name Flag name
|
* @param name Flag name
|
||||||
* @param <T> Value type
|
* @param <T> Value type
|
||||||
|
|
@ -250,7 +311,12 @@ public final class FlagContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a flag value
|
* Returns a flag value.
|
||||||
|
* <p>
|
||||||
|
* If using {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#SINGLE}
|
||||||
|
* then this returns the only value, if it has been specified. If using
|
||||||
|
* {@link cloud.commandframework.arguments.flags.CommandFlag.FlagMode#REPEATABLE} then
|
||||||
|
* it'll return the first value.
|
||||||
*
|
*
|
||||||
* @param flag Flag name
|
* @param flag Flag name
|
||||||
* @param <T> Value type
|
* @param <T> Value type
|
||||||
|
|
@ -263,4 +329,42 @@ public final class FlagContext {
|
||||||
) {
|
) {
|
||||||
return this.getValue(flag).orElse(null);
|
return this.getValue(flag).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all supplied flag values for the given {@code flag}.
|
||||||
|
*
|
||||||
|
* @param flag the flag
|
||||||
|
* @param <T> the flag value type
|
||||||
|
* @return unmodifiable view of all stored flag values, or {@link Collections#emptyList()}.
|
||||||
|
* @since 1.7.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.7.0")
|
||||||
|
public <T> @NonNull Collection<T> getAll(
|
||||||
|
final @NonNull CommandFlag<T> flag
|
||||||
|
) {
|
||||||
|
final List values = this.flagValues.get(flag.getName());
|
||||||
|
if (values != null) {
|
||||||
|
return Collections.unmodifiableList((List<T>) values);
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all supplied flag values for the given {@code flag}.
|
||||||
|
*
|
||||||
|
* @param flag the flag
|
||||||
|
* @param <T> the flag value type
|
||||||
|
* @return unmodifiable view of all stored flag values, or {@link Collections#emptyList()}.
|
||||||
|
* @since 1.7.0
|
||||||
|
*/
|
||||||
|
@API(status = API.Status.STABLE, since = "1.7.0")
|
||||||
|
public <T> @NonNull Collection<T> getAll(
|
||||||
|
final @NonNull String flag
|
||||||
|
) {
|
||||||
|
final List values = this.flagValues.get(flag);
|
||||||
|
if (values != null) {
|
||||||
|
return Collections.unmodifiableList((List<T>) values);
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Alexander Söderberg & Contributors
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
package cloud.commandframework.feature;
|
||||||
|
|
||||||
|
import cloud.commandframework.CommandManager;
|
||||||
|
import cloud.commandframework.TestCommandSender;
|
||||||
|
import cloud.commandframework.arguments.standard.StringArgument;
|
||||||
|
import cloud.commandframework.execution.CommandResult;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static cloud.commandframework.util.TestUtils.createManager;
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
class RepeatableFlagTest {
|
||||||
|
|
||||||
|
private CommandManager<TestCommandSender> commandManager;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() {
|
||||||
|
this.commandManager = createManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testParsingRepeatingValueFlags() {
|
||||||
|
// Arrange
|
||||||
|
this.commandManager.command(
|
||||||
|
this.commandManager.commandBuilder("test")
|
||||||
|
.flag(
|
||||||
|
this.commandManager.flagBuilder("flag")
|
||||||
|
.asRepeatable()
|
||||||
|
.withArgument(StringArgument.single("string"))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final CommandResult<TestCommandSender> result = this.commandManager.executeCommand(
|
||||||
|
new TestCommandSender(),
|
||||||
|
"test --flag one --flag two --flag three"
|
||||||
|
).join();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(result.getCommandContext().flags().getAll("flag")).containsExactly("one", "two", "three");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testParsingRepeatingPresenceFlags() {
|
||||||
|
// Arrange
|
||||||
|
this.commandManager.command(
|
||||||
|
this.commandManager.commandBuilder("test")
|
||||||
|
.flag(
|
||||||
|
this.commandManager.flagBuilder("flag")
|
||||||
|
.withAliases("f")
|
||||||
|
.asRepeatable()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final CommandResult<TestCommandSender> result = this.commandManager.executeCommand(
|
||||||
|
new TestCommandSender(),
|
||||||
|
"test --flag -fff"
|
||||||
|
).join();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(result.getCommandContext().flags().count("flag")).isEqualTo(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuggestingRepeatableFlags() {
|
||||||
|
// Arrange
|
||||||
|
this.commandManager.command(
|
||||||
|
this.commandManager.commandBuilder("test")
|
||||||
|
.flag(
|
||||||
|
this.commandManager.flagBuilder("flag")
|
||||||
|
.withAliases("f")
|
||||||
|
.asRepeatable()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
final List<String> suggestions = this.commandManager.suggest(
|
||||||
|
new TestCommandSender(),
|
||||||
|
"test --flag --"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(suggestions).containsExactly("--flag");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue