Allow for use of named suggestion providers in @Flag annotated command method parameters, add methods to FlagContext to work with flag values as optionals

This commit is contained in:
jmp 2020-11-01 13:38:08 -08:00 committed by Alexander Söderberg
parent 6d0301d9dd
commit fc1a613463
10 changed files with 104 additions and 30 deletions

View file

@ -8,10 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Added access to the CloudBrigadierManager from Brigadier-enabled command managers - Added access to the CloudBrigadierManager from Brigadier-enabled command managers
- Added parameter injectors - Added parameter injectors (cloud-annotations)
- Store currently parsing command argument in the command context - Store currently parsing command argument in the command context
- Added a method to CloudBrigadierManager to enable or disable Brigadier native suggestions for specific argument types - Added a method to CloudBrigadierManager to enable or disable Brigadier native suggestions for specific argument types
- Added a method to get the failure reason of SelectorParseExceptions - Added a method to get the failure reason of SelectorParseExceptions
- Added some methods to FlagContext to work with flag values as optionals
- Allow for use of named suggestion providers with `@Flag`s (cloud-annotations)
### Changed ### Changed
- Allow for use of `@Completions` annotation with argument types other than String - Allow for use of `@Completions` annotation with argument types other than String

View file

@ -23,6 +23,8 @@
// //
package cloud.commandframework.annotations; package cloud.commandframework.annotations;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@ -41,14 +43,14 @@ public @interface Argument {
* *
* @return Argument name * @return Argument name
*/ */
String value(); @NonNull String value();
/** /**
* Name of the argument parser * Name of the argument parser
* *
* @return Argument name * @return Argument name
*/ */
String parserName() default ""; @NonNull String parserName() default "";
/** /**
* Name of the suggestions provider to use. If the string is left empty, the default * Name of the suggestions provider to use. If the string is left empty, the default
@ -64,20 +66,20 @@ public @interface Argument {
* should be used instead * should be used instead
* @since 1.1.0 * @since 1.1.0
*/ */
String suggestions() default ""; @NonNull String suggestions() default "";
/** /**
* Get the default value * Get the default value
* *
* @return Default value * @return Default value
*/ */
String defaultValue() default ""; @NonNull String defaultValue() default "";
/** /**
* The argument description * The argument description
* *
* @return Argument description * @return Argument description
*/ */
String description() default ""; @NonNull String description() default "";
} }

View file

@ -24,6 +24,7 @@
package cloud.commandframework.annotations; package cloud.commandframework.annotations;
import cloud.commandframework.arguments.parser.StandardParameters; import cloud.commandframework.arguments.parser.StandardParameters;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -42,6 +43,6 @@ public @interface CommandDescription {
* *
* @return Command syntax * @return Command syntax
*/ */
String value() default ""; @NonNull String value() default "";
} }

View file

@ -23,6 +23,8 @@
// //
package cloud.commandframework.annotations; package cloud.commandframework.annotations;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@ -40,13 +42,13 @@ public @interface CommandMethod {
* *
* @return Command syntax * @return Command syntax
*/ */
String value(); @NonNull String value();
/** /**
* The required sender * The required sender
* *
* @return Required sender * @return Required sender
*/ */
Class<?> requiredSender() default Object.class; @NonNull Class<?> requiredSender() default Object.class;
} }

View file

@ -23,6 +23,8 @@
// //
package cloud.commandframework.annotations; package cloud.commandframework.annotations;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@ -40,6 +42,6 @@ public @interface CommandPermission {
* *
* @return Command permission * @return Command permission
*/ */
String value() default ""; @NonNull String value() default "";
} }

View file

@ -23,15 +23,21 @@
// //
package cloud.commandframework.annotations; package cloud.commandframework.annotations;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; 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;
/** /**
* Indicates that the parameter should be treated like a {@link cloud.commandframework.arguments.flags.CommandFlag}. * Indicates that the parameter should be treated like a {@link cloud.commandframework.arguments.flags.CommandFlag}.
* If the parameter is a {@code boolean} then a presence flag will be created, else a value flag will be created * <ul>
* and the parser will be resolved the same way as it would for a {@link Argument} * <li>If the parameter is a {@code boolean}, a presence flag will be created</li>
* <li>If the parameter is of any other type, a value flag will be created and the parser
* will resolve it in the same way that it would for an {@link Argument}</li>
* </ul>
*/ */
@Target(ElementType.PARAMETER) @Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -42,14 +48,14 @@ public @interface Flag {
* *
* @return Flag name * @return Flag name
*/ */
String value(); @NonNull String value();
/** /**
* Flag aliases * Flag aliases
* *
* @return Aliases * @return Aliases
*/ */
String[] aliases() default ""; @NonNull String[] aliases() default "";
/** /**
* Name of the parser. Leave empty to use * Name of the parser. Leave empty to use
@ -57,13 +63,29 @@ public @interface Flag {
* *
* @return Parser name * @return Parser name
*/ */
String parserName() default ""; @NonNull String parserName() default "";
/**
* Name of the suggestions provider to use. If the string is left empty, the default
* provider for the argument parser will be used. Otherwise,
* the {@link cloud.commandframework.arguments.parser.ParserRegistry} instance in the
* {@link cloud.commandframework.CommandManager} will be queried for a matching suggestion provider.
* <p>
* For this to work, the suggestion needs to be registered in the parser registry. To do this, use
* {@link cloud.commandframework.arguments.parser.ParserRegistry#registerSuggestionProvider(String, BiFunction)}.
* The registry instance can be retrieved using {@link cloud.commandframework.CommandManager#getParserRegistry()}.
*
* @return The name of the suggestion provider, or {@code ""} if the default suggestion provider for the argument parser
* should be used instead
* @since 1.2.0
*/
@NonNull String suggestions() default "";
/** /**
* The argument description * The argument description
* *
* @return Argument description * @return Argument description
*/ */
String description() default ""; @NonNull String description() default "";
} }

View file

@ -37,6 +37,8 @@ import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
final class FlagExtractor implements Function<@NonNull Method, Collection<@NonNull CommandFlag<?>>> { final class FlagExtractor implements Function<@NonNull Method, Collection<@NonNull CommandFlag<?>>> {
@ -78,14 +80,25 @@ final class FlagExtractor implements Function<@NonNull Method, Collection<@NonNu
parameter.getType().getCanonicalName(), flag.value(), method.getName() parameter.getType().getCanonicalName(), flag.value(), method.getName()
)); ));
} }
final CommandArgument.Builder argumentBuilder = CommandArgument.ofType( final BiFunction<?, @NonNull String, @NonNull List<String>> suggestionProvider;
if (!flag.suggestions().isEmpty()) {
suggestionProvider = registry.getSuggestionProvider(flag.suggestions()).orElse(null);
} else {
suggestionProvider = null;
}
final CommandArgument.Builder argumentBuilder0 = CommandArgument.ofType(
parameter.getType(), parameter.getType(),
flag.value() flag.value()
); );
final CommandArgument argument = argumentBuilder.asRequired() final CommandArgument.Builder argumentBuilder = argumentBuilder0.asRequired()
.manager(this.commandManager) .manager(this.commandManager)
.withParser(parser) .withParser(parser);
.build(); final CommandArgument argument;
if (suggestionProvider != null) {
argument = argumentBuilder.withSuggestionsProvider(suggestionProvider).build();
} else {
argument = argumentBuilder.build();
}
flags.add(builder.withArgument(argument).build()); flags.add(builder.withArgument(argument).build());
} }
} }

View file

@ -28,6 +28,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
/** /**
* Flag value mappings * Flag value mappings
@ -78,11 +79,11 @@ public final class FlagContext {
} }
/** /**
* Check whether or not a flag is present. This will return {@code false} * Check whether a presence flag is present. This will return {@code false}
* for all value flags. * for all value flags.
* *
* @param flag Flag name * @param flag Flag name
* @return {@code true} if the flag is presence and the flag is a presence flag, * @return {@code true} if the flag is a presence flag and is present,
* else {@code false} * else {@code false}
*/ */
public boolean isPresent(final @NonNull String flag) { public boolean isPresent(final @NonNull String flag) {
@ -90,6 +91,25 @@ public final class FlagContext {
return FLAG_PRESENCE_VALUE.equals(value); return FLAG_PRESENCE_VALUE.equals(value);
} }
/**
* Get a flag value as an optional. Will be empty if the value is not present.
*
* @param name Flag name
* @param <T> Value type
* @return Optional containing stored value if present
* @since 1.2.0
*/
public <T> @NonNull Optional<T> getValue(
final @NonNull String name
) {
final Object value = this.flagValues.get(name);
if (value == null) {
return Optional.empty();
}
@SuppressWarnings("unchecked") final T casted = (T) value;
return Optional.of(casted);
}
/** /**
* Get a flag value * Get a flag value
* *
@ -102,12 +122,22 @@ public final class FlagContext {
final @NonNull String name, final @NonNull String name,
final @Nullable T defaultValue final @Nullable T defaultValue
) { ) {
final Object value = this.flagValues.get(name); return this.<T>getValue(name).orElse(defaultValue);
if (value == null) { }
return defaultValue;
} /**
@SuppressWarnings("unchecked") final T casted = (T) value; * Check whether a flag is present. This will return {@code true} if the flag
return casted; * is a presence flag and is present, or if the flag is a value flag and has
* a value provided.
*
* @param name Flag name
* @return {@code true} if the flag is present, else {@code false}
* @since 1.2.0
*/
public boolean hasFlag(
final @NonNull String name
) {
return this.getValue(name).isPresent();
} }
} }

View file

@ -140,7 +140,7 @@ public interface ParserRegistry<C> {
* Get a named suggestion provider, if a suggestion provider with the given name exists in the registry * Get a named suggestion provider, if a suggestion provider with the given name exists in the registry
* *
* @param name Suggestion provider name. The name is case independent. * @param name Suggestion provider name. The name is case independent.
* @return Optional that either contains the suggestion provider name, or nothing ({@link Optional#empty()}) if no * @return Optional that either contains the suggestion provider, or is empty if no
* suggestion provider is registered with the given name * suggestion provider is registered with the given name
* @see #registerSuggestionProvider(String, BiFunction) Register a suggestion provider * @see #registerSuggestionProvider(String, BiFunction) Register a suggestion provider
* @since 1.1.0 * @since 1.1.0

View file

@ -253,7 +253,7 @@ public final class MinecraftExceptionHandler<C> {
*/ */
INVALID_SENDER, INVALID_SENDER,
/** /**
* The sender does not have permission to execute the command (@{@link NoPermissionException}) * The sender does not have permission to execute the command ({@link NoPermissionException})
*/ */
NO_PERMISSION, NO_PERMISSION,
/** /**