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

@ -910,6 +910,15 @@ public abstract class CommandManager<C> {
}
}
/**
* Returns the command execution coordinator used in this manager
*
* @return Command execution coordinator
*/
public @NonNull CommandExecutionCoordinator<C> commandExecutionCoordinator() {
return this.commandExecutionCoordinator;
}
/**
* Transition from the {@code in} state to the {@code out} state, if the manager is not already in that state.
*

View file

@ -81,13 +81,15 @@ public final class AsynchronousCommandExecutionCoordinator<C> extends CommandExe
final Consumer<Command<C>> commandConsumer = command -> {
if (this.commandManager.postprocessContext(commandContext, command) == State.ACCEPTED) {
try {
command.getCommandExecutionHandler().execute(commandContext);
} catch (final CommandExecutionException exception) {
resultFuture.completeExceptionally(exception);
} catch (final Exception exception) {
resultFuture.completeExceptionally(new CommandExecutionException(exception, commandContext));
}
command.getCommandExecutionHandler().executeFuture(commandContext).whenComplete((result, throwable) -> {
if (throwable != null) {
if (throwable instanceof CommandExecutionException) {
resultFuture.completeExceptionally(throwable);
} else {
resultFuture.completeExceptionally(new CommandExecutionException(throwable, commandContext));
}
}
});
}
};

View file

@ -118,7 +118,7 @@ public abstract class CommandExecutionCoordinator<C> {
final Command<C> command = Objects.requireNonNull(pair.getFirst());
if (this.getCommandTree().getCommandManager().postprocessContext(commandContext, command) == State.ACCEPTED) {
try {
command.getCommandExecutionHandler().execute(commandContext);
command.getCommandExecutionHandler().executeFuture(commandContext).get();
} catch (final CommandExecutionException exception) {
completableFuture.completeExceptionally(exception);
} catch (final Exception exception) {

View file

@ -25,7 +25,11 @@ package cloud.commandframework.execution;
import cloud.commandframework.Command;
import cloud.commandframework.context.CommandContext;
import java.util.concurrent.CompletableFuture;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Handler that is invoked whenever a {@link Command} is executed
@ -43,6 +47,23 @@ public interface CommandExecutionHandler<C> {
*/
void execute(@NonNull CommandContext<C> commandContext);
/**
* Handle command execution
*
* @param commandContext Command context
* @return future that completes when the command has finished execution
*/
default CompletableFuture<@Nullable Object> executeFuture(@NonNull CommandContext<C> commandContext) {
final CompletableFuture<Object> future = new CompletableFuture<>();
try {
execute(commandContext);
/* The command executed successfully */
future.complete(null);
} catch (final Throwable throwable) {
future.completeExceptionally(throwable);
}
return future;
}
/**
* Command execution handler that does nothing
@ -57,4 +78,27 @@ public interface CommandExecutionHandler<C> {
}
/**
* Handler that is invoked whenever a {@link Command} is executed
* by a command sender
*
* @param <C> Command sender type
*/
@FunctionalInterface
interface FutureCommandExecutionHandler<C> extends CommandExecutionHandler<C> {
@Override
@SuppressWarnings("FunctionalInterfaceMethodChanged")
default void execute(
@NonNull CommandContext<C> commandContext
) {
}
@Override
CompletableFuture<@Nullable Object> executeFuture(
@NonNull CommandContext<C> commandContext
);
}
}

View file

@ -24,6 +24,7 @@
package cloud.commandframework.extra.confirmation;
import cloud.commandframework.CommandManager;
import cloud.commandframework.context.CommandContext;
import cloud.commandframework.execution.CommandExecutionHandler;
import cloud.commandframework.execution.postprocessor.CommandPostprocessingContext;
import cloud.commandframework.execution.postprocessor.CommandPostprocessor;
@ -31,6 +32,9 @@ import cloud.commandframework.meta.CommandMeta;
import cloud.commandframework.meta.SimpleCommandMeta;
import cloud.commandframework.services.types.ConsumerService;
import cloud.commandframework.types.tuples.Pair;
import java.util.concurrent.CompletableFuture;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.LinkedHashMap;
@ -158,20 +162,20 @@ public class CommandConfirmationManager<C> {
* @return Handler for a confirmation command
*/
public @NonNull CommandExecutionHandler<C> createConfirmationExecutionHandler() {
return context -> {
return (CommandExecutionHandler.FutureCommandExecutionHandler<C>) context -> {
final Optional<CommandPostprocessingContext<C>> pending = this.getPending(context.getSender());
if (pending.isPresent()) {
final CommandPostprocessingContext<C> postprocessingContext = pending.get();
postprocessingContext.getCommand()
return postprocessingContext.getCommand()
.getCommandExecutionHandler()
.execute(postprocessingContext.getCommandContext());
.executeFuture(postprocessingContext.getCommandContext());
} else {
this.errorNotifier.accept(context.getSender());
}
return CompletableFuture.completedFuture(null);
};
}
private final class CommandConfirmationPostProcessor implements CommandPostprocessor<C> {
@Override