com.github.alex1304.ultimategdbot.api.command.annotated.AnnotatedCommand Maven / Gradle / Ivy
package com.github.alex1304.ultimategdbot.api.command.annotated;
import static reactor.function.TupleUtils.function;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.alex1304.ultimategdbot.api.command.Command;
import com.github.alex1304.ultimategdbot.api.command.CommandDocumentation;
import com.github.alex1304.ultimategdbot.api.command.CommandDocumentationEntry;
import com.github.alex1304.ultimategdbot.api.command.CommandFailedException;
import com.github.alex1304.ultimategdbot.api.command.Context;
import com.github.alex1304.ultimategdbot.api.command.FlagInformation;
import com.github.alex1304.ultimategdbot.api.command.PermissionLevel;
import com.github.alex1304.ultimategdbot.api.command.Scope;
import com.github.alex1304.ultimategdbot.api.command.annotated.paramconverter.ParamConversionException;
import com.github.alex1304.ultimategdbot.api.utils.Markdown;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.annotation.Nullable;
import reactor.util.function.Tuples;
/**
* Command implemented via annotations.
*/
public class AnnotatedCommand implements Command {
private static final Logger LOGGER = LoggerFactory.getLogger(AnnotatedCommand.class);
private final Object obj;
private final Function> action;
private final Set aliases;
private final CommandDocumentation doc;
private final PermissionLevel permLevel;
private final Scope scope;
private AnnotatedCommand(Object obj, Function> action, Set aliases,
CommandDocumentation doc, PermissionLevel permLevel, Scope scope) {
this.obj = obj;
this.action = action;
this.aliases = aliases;
this.doc = doc;
this.permLevel = permLevel;
this.scope = scope;
}
@Override
public Mono run(Context ctx) {
return action.apply(ctx);
}
@Override
public Set getAliases() {
return aliases;
}
@Override
public CommandDocumentation getDocumentation() {
return doc;
}
@Override
public PermissionLevel getPermissionLevel() {
return permLevel;
}
@Override
public Scope getScope() {
return scope;
}
@Override
public String toString() {
return "AnnotatedCommand{obj=" + obj.toString() + "}";
}
static AnnotatedCommand fromAnnotatedObject(Object obj, AnnotatedCommandProvider provider) {
var cmdSpecAnnot = readCommandSpecAnnotation(obj);
Method mainMethod = null;
var subMethods = new HashMap();
for (var method : obj.getClass().getMethods()) {
method.setAccessible(true);
var cmdActionAnnot = method.getAnnotation(CommandAction.class);
if (cmdActionAnnot == null) {
continue;
}
validateMethodPrototype(method);
if (cmdActionAnnot.value().isEmpty()) {
if (mainMethod != null) {
throw new InvalidAnnotatedObjectException("Duplicate action declaration");
}
mainMethod = method;
} else {
if (subMethods.containsKey(cmdActionAnnot.value())) {
throw new InvalidAnnotatedObjectException("Duplicate subcommand declaration for '" + cmdActionAnnot.value() + "'");
}
subMethods.put(cmdActionAnnot.value(), method);
}
}
if (mainMethod == null && subMethods.isEmpty()) {
throw new InvalidAnnotatedObjectException("No action defined for the command");
}
var mainMethodOptional = Optional.ofNullable(mainMethod);
return new AnnotatedCommand(obj,
ctx -> {
var args = ctx.getArgs();
var firstArgIndex = new AtomicInteger(2);
var matchingMethod = Optional.ofNullable(args.tokenCount() > 1 ? subMethods.get(args.get(1)) : null)
.or(() -> {
firstArgIndex.set(1);
return mainMethodOptional;
});
var invalidSyntax = new CommandFailedException("Invalid syntax. See "
+ Markdown.code(ctx.getPrefixUsed() + "help " + args.get(0)) + " for more information.");
return Mono.justOrEmpty(matchingMethod)
.switchIfEmpty(Mono.error(invalidSyntax))
.flatMap(method -> {
LOGGER.debug("Matching method: {}#{}", method.getDeclaringClass().getName(), method.getName());
var parameters = Flux.fromArray(method.getParameters()).skip(1);
var argTokens = Flux.fromIterable(args.getTokens(method.getParameters().length + firstArgIndex.get() - 1))
.skip(firstArgIndex.get());
return Flux.zip(parameters, argTokens)
.concatMap(function((param, arg) -> provider.convert(ctx, arg, param.getType())
.onErrorMap(e -> new ParamConversionException(formatParamName(param.getName()), arg, e.getMessage()))))
.collectList()
.defaultIfEmpty(List.of())
.map(argList -> new ArrayList
© 2015 - 2024 Weber Informatics LLC | Privacy Policy