Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-504 Fix nullable argument profiles #504

Merged
merged 1 commit into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ public static <SENDER> AnnotationProcessorService<SENDER> defaultService() {
.register(new PriorityAnnotationResolver<>())
.register(new ValidateAnnotationResolver<>())
.register(new CooldownAnnotationResolver<>())
// argument meta processors
.register(new KeyAnnotationResolver<>())
// universal processor for requirements such as @Arg, @Varargs, @Flag, @Context, @Bind and more
.register(new RequirementDefinitionProcessor<>())
// profile processors (they apply profiles to arguments)
Expand All @@ -76,6 +74,8 @@ public static <SENDER> AnnotationProcessorService<SENDER> defaultService() {
.register(new QuotedAnnotationProcessor<>())
.register(new NullableArgumentProcessor<>())
.register(new OptionalArgArgumentProcessor<>())
// argument meta processors
.register(new KeyAnnotationResolver<>())
;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@

class NullableTest extends LiteTestSpec {

static LiteTestConfig config = builder -> builder
.advanced();

@Command(name = "nullable")
static class NullableTestCommand {
@Execute
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package dev.rollczi.litecommands.annotations.argument.resolver.nullable;

import dev.rollczi.litecommands.annotations.argument.Arg;
import dev.rollczi.litecommands.annotations.argument.Key;
import dev.rollczi.litecommands.annotations.command.Command;
import dev.rollczi.litecommands.annotations.execute.Execute;
import dev.rollczi.litecommands.annotations.optional.OptionalArg;
import dev.rollczi.litecommands.argument.ArgumentKey;
import dev.rollczi.litecommands.argument.parser.ParseResult;
import dev.rollczi.litecommands.argument.parser.Parser;
import dev.rollczi.litecommands.unit.annotations.LiteTestSpec;
import java.util.Optional;
import org.junit.jupiter.api.Test;

class OptionalTest extends LiteTestSpec {

interface IFaction {}

static class Faction implements IFaction {}

static LiteTestConfig config = builder -> builder
.argumentParser(Faction.class, ArgumentKey.of("faction"), Parser.of((invocation, text) -> ParseResult.success(new Faction())))
.argumentParser(IFaction.class, ArgumentKey.of("ifaction"), Parser.of((invocation, text) -> ParseResult.success(new Faction())))
.advanced();

@Command(name = "optional")
static class OptionalTestCommand {
@Execute
String executeOptional(@Arg("display-faction") @Key("faction") Optional<Faction> faction) {
return faction.isPresent() ? "present" : "not present";
}

@Execute(name = "interface")
String executeInterfaceOptional(@OptionalArg("display-faction") @Key("ifaction") IFaction faction) {
return faction != null ? "interface present" : "interface not present";
}
}

@Test
void testOptionalExecute() {
platform.execute("optional")
.assertSuccess("not present");

platform.execute("optional faction")
.assertSuccess("present");

platform.execute("optional interface")
.assertSuccess("interface not present");

platform.execute("optional interface faction")
.assertSuccess("interface present");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ class BukkitPlatform extends AbstractPlatform<CommandSender, LiteBukkitSettings>
protected void hook(CommandRoute<CommandSender> commandRoute, PlatformInvocationListener<CommandSender> invocationHook, PlatformSuggestionListener<CommandSender> suggestionHook) {
BukkitCommand bukkitSimpleCommand = new BukkitCommand(settings, commandRoute, invocationHook, suggestionHook);

for (String name : commandRoute.names()) {
this.settings.commandsRegistry().register(name, settings.fallbackPrefix(), bukkitSimpleCommand);
}
this.settings.tabCompleter().register(settings.fallbackPrefix(), bukkitSimpleCommand);
this.settings.commandsRegistry().register(commandRoute.getName(), settings.fallbackPrefix(), bukkitSimpleCommand);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ default Optional<ParseResult<T>> defaultValue() {
@ApiStatus.Experimental
<NEW> Argument<NEW> child(TypeToken<NEW> type);

@ApiStatus.Experimental
Argument<T> withoutProfile(ArgumentProfileNamespace<?> profile);

static <T> Argument<T> of(String name, Class<T> type) {
return new SimpleArgument<>(name, TypeToken.of(type));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,29 @@ public PrioritizedList<ArgumentProfile<?>> getProfiles() {

@Override
public <NEW> Argument<NEW> child(TypeToken<NEW> type) {
return new SimpleArgument<>(name, type, this);
SimpleArgument<NEW> newSimpleArgument = new SimpleArgument<>(name, type, this);
newSimpleArgument.setKey(key.withDefaultNamespace());
newSimpleArgument.meta.putAll(this.meta);
for (ArgumentProfile argumentProfile : profiles) {
newSimpleArgument.addProfile(argumentProfile);
}
return newSimpleArgument;
}

@Override
public Argument<T> withoutProfile(ArgumentProfileNamespace<?> profile) {
SimpleArgument<T> argument = new SimpleArgument<>(name, type, nullable);
argument.setKey(key.withDefaultNamespace());
argument.meta.putAll(this.meta);
for (ArgumentProfile argumentProfile : profiles) {
if (!argumentProfile.getNamespace().equals(profile)) {
argument.addProfile(argumentProfile);
}
else {
argument.meta.remove(profile.asMetaKey());
}
}
return argument;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import dev.rollczi.litecommands.LiteCommandsException;
import dev.rollczi.litecommands.argument.Argument;
import dev.rollczi.litecommands.argument.matcher.Matcher;
import dev.rollczi.litecommands.argument.resolver.ArgumentResolver;
import dev.rollczi.litecommands.input.raw.RawInput;
import dev.rollczi.litecommands.invocation.Invocation;
import dev.rollczi.litecommands.range.Rangeable;
import dev.rollczi.litecommands.requirement.RequirementResult;
import java.util.function.BiFunction;
import org.jetbrains.annotations.ApiStatus;

public interface Parser<SENDER, PARSED> extends Matcher<SENDER, PARSED>, Rangeable<Argument<PARSED>> {
Expand Down Expand Up @@ -42,4 +44,14 @@ default boolean match(Invocation<SENDER> invocation, Argument<PARSED> argument,
throw new LiteCommandsException("Async parsers should override Parser#match method! (" + this.getClass().getName() + ")");
}

@ApiStatus.Experimental
static <SENDER, T> Parser<SENDER, T> of(BiFunction<Invocation<SENDER>, String, ParseResult<T>> parser) {
return new ArgumentResolver<SENDER, T>() {
@Override
protected ParseResult<T> parse(Invocation<SENDER> invocation, Argument<T> context, String argument) {
return parser.apply(invocation, argument);
}
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ private <E> boolean match0(Invocation<SENDER> invocation, Argument<COLLECTION> c
}

TypeToken<E> elementType = this.getElementType(collectionArgumentContainer);
Argument<E> argument = collectionArgument.child(elementType);
Argument<E> argument = collectionArgument.child(elementType)
.withoutProfile(VarargsProfile.NAMESPACE);
Parser<SENDER, E> parser = parserRegistry.getParser(argument);
Range range = parser.getRange(argument);

Expand All @@ -100,7 +101,8 @@ private <E> boolean match0(Invocation<SENDER> invocation, Argument<COLLECTION> c
}

private <E> ParseResult<List<E>> parseToList(TypeToken<E> componentType, RawInput rawInput, Argument<COLLECTION> collectionArgument, VarargsProfile collectorArgumentHolder, Invocation<SENDER> invocation) {
Argument<E> argument = collectionArgument.child(componentType);
Argument<E> argument = collectionArgument.child(componentType)
.withoutProfile(VarargsProfile.NAMESPACE);

Parser<SENDER, E> parser = parserRegistry.getParser(argument);

Expand Down Expand Up @@ -214,7 +216,8 @@ public SuggestionResult suggest(Invocation<SENDER> invocation, Argument<COLLECTI
}

private <T> SuggestionResult suggest(TypeToken<T> componentType, SuggestionContext context, Argument<COLLECTION> collectionArgument, VarargsProfile varargsProfile, Invocation<SENDER> invocation) {
Argument<T> argument = collectionArgument.child(componentType);
Argument<T> argument = collectionArgument.child(componentType)
.withoutProfile(VarargsProfile.NAMESPACE);

Parser<SENDER, T> parser = parserRegistry.getParser(argument);
Suggester<SENDER, T> suggester = suggesterRegistry.getSuggester(componentType.getRawType(), argument.getKey());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import dev.rollczi.litecommands.argument.parser.ParseResult;
import dev.rollczi.litecommands.argument.parser.Parser;
import dev.rollczi.litecommands.argument.parser.ParserRegistry;
import dev.rollczi.litecommands.argument.profile.ProfileNamespaces;
import dev.rollczi.litecommands.argument.profile.ProfiledMultipleArgumentResolver;
import dev.rollczi.litecommands.argument.suggester.Suggester;
import dev.rollczi.litecommands.argument.suggester.SuggesterRegistry;
Expand All @@ -28,13 +29,11 @@ public NullableArgumentResolver(ParserRegistry<SENDER> parserRegistry, Suggester

@Override
protected ParseResult<Object> parse(Invocation<SENDER> invocation, Argument<Object> argument, RawInput rawInput, NullableProfile unused) {
TypeToken<Object> optionalType = argument.getType();

return parseValue(optionalType, invocation, argument, rawInput);
return parseValue(invocation, argument, rawInput);
}

private <E> ParseResult<E> parseValue(TypeToken<E> type, Invocation<SENDER> invocation, Argument<Object> optionalArgument, RawInput input) {
Argument<E> argument = Argument.of(optionalArgument.getName(), type);
private <E> ParseResult<E> parseValue(Invocation<SENDER> invocation, Argument<E> optionalArgument, RawInput input) {
Argument<E> argument = optionalArgument.withoutProfile(ProfileNamespaces.NULLABLE);
Parser<SENDER, E> parser = parserRegistry.getParser(argument);
ParseResult<E> parseResult = parser.parse(invocation, argument, input);

Expand All @@ -55,7 +54,15 @@ protected SuggestionResult suggest(Invocation<SENDER> invocation, Argument<Objec

@Override
protected Range getRange(Argument<Object> argument, NullableProfile unused) {
return Range.range(0, 1);
return getRangeTyped(argument);
}

private <T> Range getRangeTyped(Argument<T> optionalArgument) {
Argument<T> argument = optionalArgument.withoutProfile(ProfileNamespaces.NULLABLE);
Parser<SENDER, T> parser = parserRegistry.getParser(argument);
Range range = parser.getRange(argument);

return Range.range(0, range.getMax());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public ParseResult<Optional> parse(Invocation<SENDER> invocation, Argument<Optio

@SuppressWarnings("unchecked")
private <E> ParseResult<Optional> parseValue(TypeToken<E> type, Invocation<SENDER> invocation, Argument<Optional> optionalArgument, RawInput input, ParserChainAccessor<SENDER> chainAccessor) {
Argument<E> argument = Argument.of(optionalArgument.getName(), type);
Argument<E> argument = optionalArgument.child(type);
ParseResult parseResult = chainAccessor.parse(invocation, argument.child(type), input);

return parseResult
Expand Down Expand Up @@ -65,7 +65,7 @@ public SuggestionResult suggest(Invocation<SENDER> invocation, Argument<Optional
TypeToken<Optional> optionalType = argument.getType();
TypeToken<?> parameterized = optionalType.getParameterized();

return chainAccessor.suggest(invocation, Argument.of(argument.getName(), parameterized), context);
return chainAccessor.suggest(invocation, argument.child(parameterized), context);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ public boolean hasNextRoute() {

public <SENDER, T> Context<SENDER, T> toContext(
Argument<T> argument,
Parser<SENDER, T> parserSet
Parser<SENDER, T> parser
) {
return new Context<>(argument, parserSet);
return new Context<>(argument, parser);
}

public String showNextRoute() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ private class TypeIterator implements Iterator<Class<?>> {

private Class<?> next;
private final Queue<Class<?>> interfaces = new LinkedList<>();
private final Set<Class<?>> visitedInterfaces= new HashSet<>();
private final Set<Class<?>> visitedInterfaces = new HashSet<>();

private TypeIterator() {
this.next = baseType;
Expand All @@ -39,10 +39,10 @@ public boolean hasNext() {

@Override
public Class<?> next() {
Class<?> next = this.next;
Class<?> nextToReturn = this.next;
this.next = null;

if (next == null) {
if (nextToReturn == null) {
return null;
}

Expand All @@ -57,18 +57,22 @@ public Class<?> next() {
interfaces.addAll(ReflectIndex.getInterfaces(nextInterface));

this.next = nextInterface;
return next;
return nextToReturn;
}

Class<?> superclass = next.getSuperclass();
Class<?> superclass = nextToReturn.getSuperclass();

if (superclass != null) {
this.next = superclass;
interfaces.addAll(ReflectIndex.getInterfaces(superclass));
return next;
return nextToReturn;
}

return next;
if (this.next == null && nextToReturn != Object.class) {
this.next = Object.class;
}

return nextToReturn;
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ void test() {
Parser.class,
Matcher.class,
Suggester.class,
Rangeable.class
Rangeable.class,
Object.class
);
}

Expand Down
Loading