Add support for suspending functions

This also changes CommandExecutionHandler to return futures instead. The old method is still supported, and the new future-returning method will delegate to the old non-returning method.
This commit is contained in:
Alexander Söderberg 2021-07-07 14:03:06 +01:00 committed by Jason
parent c16ee8049b
commit 7bb5e63dc7
12 changed files with 556 additions and 249 deletions

View file

@ -48,6 +48,9 @@ import cloud.commandframework.extra.confirmation.CommandConfirmationManager;
import cloud.commandframework.meta.CommandMeta;
import cloud.commandframework.meta.SimpleCommandMeta;
import io.leangen.geantyref.TypeToken;
import java.util.function.Predicate;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -85,6 +88,8 @@ public final class AnnotationParser<C> {
@NonNull Queue<@NonNull String>, @NonNull ArgumentParseResult<Boolean>>>> preprocessorMappers;
private final Map<Class<? extends Annotation>, BiFunction<? extends Annotation, Command.Builder<C>, Command.Builder<C>>>
builderModifiers;
private final Map<Predicate<Method>, Function<MethodCommandExecutionHandler.CommandMethodContext<C>,
MethodCommandExecutionHandler<C>>> commandMethodFactories;
private final Class<C> commandSenderClass;
private final MetaFactory metaFactory;
private final FlagExtractor flagExtractor;
@ -110,6 +115,7 @@ public final class AnnotationParser<C> {
this.annotationMappers = new HashMap<>();
this.preprocessorMappers = new HashMap<>();
this.builderModifiers = new HashMap<>();
this.commandMethodFactories = new HashMap<>();
this.flagExtractor = new FlagExtractor(manager);
this.registerAnnotationMapper(CommandDescription.class, d ->
ParserParameters.single(StandardParameters.DESCRIPTION, d.value()));
@ -180,6 +186,29 @@ public final class AnnotationParser<C> {
return getMethodOrClassAnnotation(method, clazz) != null;
}
/**
* Returns the command manager that was used to create this parser
*
* @return Command manager
*/
public @NonNull CommandManager<C> manager() {
return this.manager;
}
/**
* Registers a new command execution method factory. This allows for the registration of
* custom command method execution strategies.
*
* @param predicate The predicate that decides whether or not to apply the custom execution handler to the given method
* @param function The function that produces the command execution handler
*/
public void registerCommandExecutionMethodFactory(
final @NonNull Predicate<@NonNull Method> predicate,
final @NonNull Function<MethodCommandExecutionHandler.CommandMethodContext<C>, MethodCommandExecutionHandler<C>> function
) {
this.commandMethodFactories.put(predicate, function);
}
/**
* Register a builder modifier for a specific annotation. The builder modifiers are
* allowed to act on a {@link Command.Builder} after all arguments have been added
@ -265,12 +294,6 @@ public final class AnnotationParser<C> {
if (!method.isAccessible()) {
method.setAccessible(true);
}
if (method.getReturnType() != Void.TYPE) {
throw new IllegalArgumentException(String.format(
"@CommandMethod annotated method '%s' has non-void return type",
method.getName()
));
}
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalArgumentException(String.format(
"@CommandMethod annotated method '%s' is static! @CommandMethod annotated methods should not be static.",
@ -460,13 +483,25 @@ public final class AnnotationParser<C> {
builder = builder.senderType(senderType);
}
try {
/* Construct the handler */
final CommandExecutionHandler<C> commandExecutionHandler = new MethodCommandExecutionHandler<>(
final MethodCommandExecutionHandler.CommandMethodContext<C> context = new MethodCommandExecutionHandler.CommandMethodContext<>(
instance,
commandArguments,
method,
this.getParameterInjectorRegistry()
);
/* Create the command execution handler */
CommandExecutionHandler<C> commandExecutionHandler = new MethodCommandExecutionHandler<>(context);
for (final Map.Entry<Predicate<Method>, Function<MethodCommandExecutionHandler.CommandMethodContext<C>, MethodCommandExecutionHandler<C>>> entry :
commandMethodFactories.entrySet()) {
if (entry.getKey().test(method)) {
commandExecutionHandler = entry.getValue().apply(context);
/* Once we have our custom handler, we stop */
break;
}
}
builder = builder.handler(commandExecutionHandler);
} catch (final Exception e) {
throw new RuntimeException("Failed to construct command execution handler", e);