diff --git a/common/site/lab0x13/scrow/commands/CommandHandler.java b/common/site/lab0x13/scrow/commands/CommandHandler.java index daa9cb7..ae71956 100644 --- a/common/site/lab0x13/scrow/commands/CommandHandler.java +++ b/common/site/lab0x13/scrow/commands/CommandHandler.java @@ -1,14 +1,10 @@ package site.lab0x13.scrow.commands; import site.lab0x13.scrow.commands.model.CommandContext; -import site.lab0x13.scrow.commands.model.literal.LiteralMeta; import site.lab0x13.scrow.commands.model.literal.RootLiteral; import site.lab0x13.scrow.commands.parser.NodeTreeWalker; -import site.lab0x13.scrow.commands.parser.WalkResult; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.stream.Stream; @@ -25,13 +21,16 @@ public class CommandHandler> { } public void invoke(RootLiteral rootLiteral, C ctx, Stream args) { - var walkResult = nodeTreeWalker.walk(ctx, rootLiteral, args.iterator()); - if (!walkResult.isValidUsage()) { - platformAdapter.sendMessage(ctx, rootLiteral.style().err(walkResult.message())); + var walkResult = nodeTreeWalker.walk(ctx, rootLiteral, args.iterator(), false); + var error = walkResult.executor().getFailure(); + if (error.isPresent()) { + platformAdapter.sendMessage(ctx, rootLiteral.style().err(error.get())); return; } - requireNonNull(walkResult.lastLiteral().executor()) - .execute(walkResult.ctx()) + + var executor = walkResult.executor().getSuccess().orElseThrow(); + requireNonNull(executor) + .execute(ctx) .exceptionally(ex -> { platformAdapter.sendMessage(ctx, rootLiteral.style().exception(ex)); return null; @@ -39,39 +38,10 @@ public class CommandHandler> { } public List suggest(RootLiteral rootLiteral, C ctx, String[] args, boolean trailingSpace) { - var walkResult = nodeTreeWalker.walk(ctx, rootLiteral, Arrays.stream(args).iterator()); - Stream stream; - String input = null; - switch (walkResult) { - case WalkResult.LiteralUnknown res -> { - stream = filterMetas(ctx, res.lastLiteral().subLiteralMetas()); - input = res.input(); - } - case WalkResult.LiteralExpected res when trailingSpace -> - stream = filterMetas(ctx, res.lastLiteral().subLiteralMetas()); - case WalkResult.ArgumentIllegal res -> { - stream = res.arg().suggestionProvider().suggest(ctx, res.input()).stream(); - input = res.input(); - } - case WalkResult.ArgumentExpected res when trailingSpace -> - stream = res.arg().suggestionProvider().suggest(ctx, "").stream(); - case WalkResult.Complete res when !trailingSpace -> stream = filterMetas(ctx, res.alternativeLiterals()); - default -> { - return Collections.emptyList(); - } - } - - if (input == null) return stream.toList(); - final var finalInput = input; - return stream - .filter(s -> s.startsWith(finalInput)) + // FIXME trailing space + var walkResult = nodeTreeWalker.walk(ctx, rootLiteral, Arrays.stream(args).iterator(), trailingSpace); + return walkResult.suggestionProvider().suggest(ctx, walkResult.input()).stream() + .filter(s -> s.startsWith(walkResult.input())) .toList(); } - - private Stream filterMetas(C ctx, List metas) { - return metas.stream() - .filter(literalMeta -> platformAdapter.checkPermission(ctx, literalMeta.permission())) - .map(LiteralMeta::names) - .flatMap(Collection::stream); - } } \ No newline at end of file diff --git a/common/site/lab0x13/scrow/commands/model/Node.java b/common/site/lab0x13/scrow/commands/model/Node.java deleted file mode 100644 index 84ccbe4..0000000 --- a/common/site/lab0x13/scrow/commands/model/Node.java +++ /dev/null @@ -1,6 +0,0 @@ -package site.lab0x13.scrow.commands.model; - -public interface Node> { - - SuggestionProvider suggestionProvider(); -} diff --git a/common/site/lab0x13/scrow/commands/model/argument/Argument.java b/common/site/lab0x13/scrow/commands/model/argument/Argument.java index 094b7b1..95e50b8 100644 --- a/common/site/lab0x13/scrow/commands/model/argument/Argument.java +++ b/common/site/lab0x13/scrow/commands/model/argument/Argument.java @@ -3,7 +3,6 @@ package site.lab0x13.scrow.commands.model.argument; import com.leakyabstractions.result.api.Result; import com.leakyabstractions.result.core.Results; import site.lab0x13.scrow.commands.model.CommandContext; -import site.lab0x13.scrow.commands.model.Node; import site.lab0x13.scrow.commands.model.SuggestionProvider; import java.util.List; @@ -18,8 +17,7 @@ public record Argument, V>( List> requirements, SuggestionProvider suggestionProvider, DefaultValueProvider defaultValueProvider -) implements Node { - +) { public static , V> ArgumentBuilder builder(String id, ArgumentType type) { return new ArgumentBuilder<>(id, type); } diff --git a/common/site/lab0x13/scrow/commands/model/literal/Literal.java b/common/site/lab0x13/scrow/commands/model/literal/Literal.java index 204cfaa..2158c54 100644 --- a/common/site/lab0x13/scrow/commands/model/literal/Literal.java +++ b/common/site/lab0x13/scrow/commands/model/literal/Literal.java @@ -3,18 +3,14 @@ package site.lab0x13.scrow.commands.model.literal; import org.jetbrains.annotations.Nullable; import site.lab0x13.scrow.commands.model.CommandContext; import site.lab0x13.scrow.commands.model.CommandExecutor; -import site.lab0x13.scrow.commands.model.Node; -import site.lab0x13.scrow.commands.model.SuggestionProvider; import site.lab0x13.scrow.commands.model.argument.Argument; -import java.util.Arrays; -import java.util.Collection; import java.util.List; /** * @param argument of the command context */ -public interface Literal> extends Node, LiteralMeta { +public interface Literal> extends LiteralMeta { static > StaticLiteralBuilder builder(String... names) { return new StaticLiteralBuilder<>(names); @@ -34,12 +30,4 @@ public interface Literal> extends Node, LiteralMe List subLiteralMetas(); List> arguments(); - - @Override - default SuggestionProvider suggestionProvider() { - return (_, _) -> subLiteralMetas().stream() - .map(LiteralMeta::names) - .flatMap(Collection::stream) - .toList(); - } } \ No newline at end of file diff --git a/common/site/lab0x13/scrow/commands/parser/Cursor.java b/common/site/lab0x13/scrow/commands/parser/Cursor.java new file mode 100644 index 0000000..3fadaf6 --- /dev/null +++ b/common/site/lab0x13/scrow/commands/parser/Cursor.java @@ -0,0 +1,29 @@ +package site.lab0x13.scrow.commands.parser; + +import org.jetbrains.annotations.Nullable; + +import java.util.Iterator; + +public class Cursor { + private final Iterator tokenIter; + + public Cursor(Iterator tokenIter) { + this.tokenIter = tokenIter; + } + + public boolean hasNext() { + return tokenIter.hasNext(); + } + + public @Nullable String nextToken() { + if (!tokenIter.hasNext()) + return null; + return tokenIter.next(); + } + + public @Nullable String remainingTokens() { + var builder = new StringBuilder(); + tokenIter.forEachRemaining(s -> builder.append(' ').append(s)); + return builder.toString(); + } +} diff --git a/common/site/lab0x13/scrow/commands/parser/NodeTreeWalker.java b/common/site/lab0x13/scrow/commands/parser/NodeTreeWalker.java index e6712c8..5ea035d 100644 --- a/common/site/lab0x13/scrow/commands/parser/NodeTreeWalker.java +++ b/common/site/lab0x13/scrow/commands/parser/NodeTreeWalker.java @@ -1,14 +1,18 @@ package site.lab0x13.scrow.commands.parser; +import com.leakyabstractions.result.core.Results; import org.jetbrains.annotations.Nullable; import site.lab0x13.scrow.commands.PlatformAdapter; import site.lab0x13.scrow.commands.model.CommandContext; +import site.lab0x13.scrow.commands.model.CommandExecutor; +import site.lab0x13.scrow.commands.model.SuggestionProvider; import site.lab0x13.scrow.commands.model.argument.Argument; import site.lab0x13.scrow.commands.model.literal.Literal; +import site.lab0x13.scrow.commands.model.literal.LiteralMeta; import site.lab0x13.scrow.commands.model.literal.RootLiteral; -import java.util.Collections; import java.util.Iterator; +import java.util.List; import static java.util.Objects.requireNonNull; @@ -23,7 +27,8 @@ public class NodeTreeWalker> { public WalkResult walk( C ctx, RootLiteral rootLiteral, - Iterator iter + Iterator iter, + boolean suggestNext ) { var cursor = new Cursor(iter); Literal previousLiteral = null; @@ -32,7 +37,7 @@ public class NodeTreeWalker> { while (true) { for (var arg : literal.arguments()) { //noinspection unchecked - var res = walkArgument(ctx, cursor, literal, (Argument) arg); + var res = walkArgument(ctx, cursor, literal.executor(), (Argument) arg); if (res != null) return res; } @@ -42,57 +47,47 @@ public class NodeTreeWalker> { break; var nextLiteral = literal.getSubLiteral(literalName); if (nextLiteral == null || !platformAdapter.checkPermission(ctx, nextLiteral.permission())) - return new WalkResult.LiteralUnknown<>(ctx, literal, literalName); + return new WalkResult<>(Results.failure("Unknown literal."), suggestionProvider(literal), literalName); previousLiteral = literal; literal = nextLiteral; } - if (!literal.subLiteralMetas().isEmpty()) { - var isOptional = literal.executor() != null; - return new WalkResult.LiteralExpected<>(ctx, literal, isOptional); - } - - return new WalkResult.Complete<>(ctx, literal, previousLiteral == null ? Collections.emptyList() : previousLiteral.subLiteralMetas()); + var suggestionProvider = suggestionProvider(suggestNext ? literal : previousLiteral); + if (literal.executor() == null) + return new WalkResult<>(Results.failure("Literal expected."), suggestionProvider); + return new WalkResult<>(Results.success(requireNonNull(literal.executor())), suggestionProvider); } private @Nullable WalkResult walkArgument( C ctx, Cursor cursor, - Literal literal, + CommandExecutor executor, Argument arg ) { var rawInput = arg.type().greedy() ? cursor.remainingTokens() : cursor.nextToken(); if (rawInput == null) { var defaultValue = requireNonNull(arg.defaultValueProvider()).getDefault(ctx); ctx.addParsedArgument(arg, "", defaultValue); - return new WalkResult.ArgumentExpected<>(ctx, literal, defaultValue != null, arg); + if (defaultValue == null) + return new WalkResult<>(Results.failure("Argument expected."), arg.suggestionProvider()); + // default value used. if this is the last, we probably want to suggest this. + if (!cursor.hasNext()) + return new WalkResult<>(Results.success(executor), arg.suggestionProvider()); + } else { + var parseResult = arg.parse(ctx, rawInput); + if (parseResult.hasFailure()) + return new WalkResult<>(parseResult.mapSuccess(_ -> null), arg.suggestionProvider(), rawInput); + ctx.addParsedArgument(arg, rawInput, parseResult.getSuccess().orElseThrow().value()); } - - var parseResult = arg.parse(ctx, rawInput); - if (parseResult.hasFailure()) - return new WalkResult.ArgumentIllegal<>(ctx, literal, false, - parseResult.getFailure().orElseThrow(), arg, rawInput); - ctx.addParsedArgument(arg, rawInput, parseResult.getSuccess().orElseThrow().value()); return null; } - private static class Cursor { - private final Iterator tokenIter; - - public Cursor(Iterator tokenIter) { - this.tokenIter = tokenIter; - } - - public @Nullable String nextToken() { - if (!tokenIter.hasNext()) - return null; - return tokenIter.next(); - } - - public @Nullable String remainingTokens() { - var builder = new StringBuilder(); - tokenIter.forEachRemaining(s -> builder.append(' ').append(s)); - return builder.toString(); - } + private SuggestionProvider suggestionProvider(Literal literal) { + if (literal == null) return SuggestionProvider.none(); + return (ctx, _) -> literal.subLiteralMetas().stream() + .filter(meta -> platformAdapter.checkPermission(ctx, meta.permission())) + .map(LiteralMeta::names) + .flatMap(List::stream) + .toList(); } } diff --git a/common/site/lab0x13/scrow/commands/parser/WalkResult.java b/common/site/lab0x13/scrow/commands/parser/WalkResult.java index ad07ed9..b33de61 100644 --- a/common/site/lab0x13/scrow/commands/parser/WalkResult.java +++ b/common/site/lab0x13/scrow/commands/parser/WalkResult.java @@ -1,84 +1,16 @@ package site.lab0x13.scrow.commands.parser; -import org.jetbrains.annotations.Nullable; +import com.leakyabstractions.result.api.Result; import site.lab0x13.scrow.commands.model.CommandContext; -import site.lab0x13.scrow.commands.model.argument.Argument; -import site.lab0x13.scrow.commands.model.literal.Literal; -import site.lab0x13.scrow.commands.model.literal.LiteralMeta; +import site.lab0x13.scrow.commands.model.CommandExecutor; +import site.lab0x13.scrow.commands.model.SuggestionProvider; -import java.util.List; - -public sealed interface WalkResult> { - C ctx(); - - Literal lastLiteral(); - - boolean isValidUsage(); - - @Nullable String message(); - - record ArgumentIllegal>( - C ctx, - Literal lastLiteral, - boolean isValidUsage, - String message, - Argument arg, - String input - ) implements WalkResult { - } - - record ArgumentExpected>( - C ctx, - Literal lastLiteral, - boolean isValidUsage, - Argument arg - ) implements WalkResult { - @Override - public String message() { - return "Argument expected."; - } - } - - record LiteralExpected>( - C ctx, - Literal lastLiteral, - boolean isValidUsage - ) implements WalkResult { - @Override - public String message() { - return "Literal expected."; - } - } - - record LiteralUnknown>( - C ctx, - Literal lastLiteral, - String input - ) implements WalkResult { - @Override - public boolean isValidUsage() { - return false; - } - - @Override - public String message() { - return "Unknown literal \"" + input + "\"."; - } - } - - record Complete>( - C ctx, - Literal lastLiteral, - List alternativeLiterals - ) implements WalkResult { - @Override - public boolean isValidUsage() { - return true; - } - - @Override - public @Nullable String message() { - return null; - } +public record WalkResult>( + Result, String> executor, + SuggestionProvider suggestionProvider, + String input +) { + public WalkResult(Result, String> executor, SuggestionProvider suggestionProvider) { + this(executor, suggestionProvider, ""); } } diff --git a/common/tests/site/lab0x13/scrow/commands/CommandHandlerTest.java b/common/tests/site/lab0x13/scrow/commands/CommandHandlerTest.java index f373ddb..fe42d72 100644 --- a/common/tests/site/lab0x13/scrow/commands/CommandHandlerTest.java +++ b/common/tests/site/lab0x13/scrow/commands/CommandHandlerTest.java @@ -93,7 +93,7 @@ class CommandHandlerTest { @Test void testFailOnBadLiteral() { commandHandler.invoke(rootLiteral, new CommandContextImpl(), Stream.of("BAD_LITERAL")); - assertEquals("Unknown literal \"BAD_LITERAL\".\n", outContent.toString(StandardCharsets.UTF_8)); + assertEquals("Unknown literal.\n", outContent.toString(StandardCharsets.UTF_8)); } @Test diff --git a/defs.bzl b/defs.bzl index 60e3f05..feddd43 100644 --- a/defs.bzl +++ b/defs.bzl @@ -1,2 +1,2 @@ GROUP = "site.lab0x13.scrow" -VERSION = "1.3.5" \ No newline at end of file +VERSION = "1.4.0" \ No newline at end of file diff --git a/velocity/site/lab0x13/scrow/commands/velocity/VelocityCommand.java b/velocity/site/lab0x13/scrow/commands/velocity/VelocityCommand.java index 3c45da5..2614f7f 100644 --- a/velocity/site/lab0x13/scrow/commands/velocity/VelocityCommand.java +++ b/velocity/site/lab0x13/scrow/commands/velocity/VelocityCommand.java @@ -12,10 +12,10 @@ public class VelocityCommand> implements Raw private final CommandHandler commandHandler; private final CommandContextFactory commandContextFactory; - private final PlatformAdapter platformAdapter; + private final PlatformAdapter platformAdapter; private final RootLiteral rootLiteral; - public VelocityCommand(CommandHandler commandHandler, CommandContextFactory commandContextFactory, PlatformAdapter platformAdapter, RootLiteral rootLiteral) { + public VelocityCommand(CommandHandler commandHandler, CommandContextFactory commandContextFactory, PlatformAdapter platformAdapter, RootLiteral rootLiteral) { this.commandHandler = commandHandler; this.commandContextFactory = commandContextFactory; this.platformAdapter = platformAdapter; @@ -32,7 +32,7 @@ public class VelocityCommand> implements Raw @Override public boolean hasPermission(Invocation ctx) { - return platformAdapter.checkPermission(ctx, rootLiteral.permission()); + return platformAdapter.checkPermission(commandContextFactory.createContext(rootLiteral, ctx.source()), rootLiteral.permission()); } @Override