From b225229e603596f046d720a7e048e356433d68b5 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Wed, 21 Feb 2024 15:20:41 -0700 Subject: [PATCH] Return syntax instead of no permission in some cases --- .../java/org/incendo/cloud/CommandTree.java | 96 ++++++++++++++----- .../incendo/cloud/setting/ManagerSetting.java | 5 +- 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/cloud-core/src/main/java/org/incendo/cloud/CommandTree.java b/cloud-core/src/main/java/org/incendo/cloud/CommandTree.java index 637d93835..c568e903f 100644 --- a/cloud-core/src/main/java/org/incendo/cloud/CommandTree.java +++ b/cloud-core/src/main/java/org/incendo/cloud/CommandTree.java @@ -225,11 +225,7 @@ private CommandTree(final @NonNull CommandManager commandManager) { final PermissionResult permissionResult = this.determinePermissionResult(commandContext.sender(), root); if (permissionResult.denied()) { return CompletableFutures.failedFuture( - new NoPermissionException( - permissionResult, - commandContext.sender(), - this.getComponentChain(root) - ) + this.noPermissionOrSyntax(permissionResult, commandContext.sender(), root) ); } @@ -328,11 +324,7 @@ private CommandTree(final @NonNull CommandManager commandManager) { ); if (check.denied()) { return CompletableFutures.failedFuture( - new NoPermissionException( - check, - commandContext.sender(), - this.getComponentChain(root) - ) + this.noPermissionOrSyntax(check, commandContext.sender(), root) ); } return CompletableFuture.completedFuture(root.command()); @@ -349,6 +341,72 @@ private CommandTree(final @NonNull CommandManager commandManager) { }); } + @SuppressWarnings({"unchecked", "rawtypes"}) + private Exception noPermissionOrSyntax( + final PermissionResult permissionResult, + final C sender, + final CommandNode root + ) { + final boolean convert = this.commandManager.settings().get(ManagerSetting.CONVERT_NO_PERMISSION_TO_SYNTAX_EXCEPTION); + if (!convert) { + return new NoPermissionException( + permissionResult, + sender, + this.getComponentChain(root) + ); + } + + if (this.childPermitted(root, sender)) { + return new InvalidSyntaxException( + this.commandManager.commandSyntaxFormatter().apply(sender, (List) this.getComponentChain(root), root), + sender, this.getComponentChain(root) + ); + } + + final @Nullable List> parentChain = this.permittedParentChain(root, sender); + if (parentChain != null) { + return new InvalidSyntaxException( + this.commandManager.commandSyntaxFormatter().apply( + sender, + parentChain.stream().map(CommandNode::component) + .filter(Objects::nonNull).collect(Collectors.toList()), + root + ), + sender, this.getComponentChain(root) + ); + } + + return new NoPermissionException( + permissionResult, + sender, + this.getComponentChain(root) + ); + } + + private boolean childPermitted(CommandNode node, C sender) { + if (this.determinePermissionResult(sender, node).allowed()) { + return true; + } + for (final CommandNode child : node.children()) { + if (this.childPermitted(child, sender)) { + return true; + } + } + return false; + } + + private @Nullable List> permittedParentChain(CommandNode node, C sender) { + final @Nullable CommandNode parent = node.parent(); + if (parent != null) { + if (this.determinePermissionResult(sender, parent).allowed()) { + return this.getChain(parent); + } else { + return this.permittedParentChain(parent, sender); + } + } + return null; + } + private @Nullable CompletableFuture<@Nullable Command> attemptParseUnambiguousChild( final @NonNull List<@NonNull CommandComponent> parsedArguments, final @NonNull CommandContext commandContext, @@ -380,11 +438,7 @@ private CommandTree(final @NonNull CommandManager commandManager) { final PermissionResult childCheck = this.determinePermissionResult(sender, child); if (!commandInput.isEmpty() && childCheck.denied()) { return CompletableFutures.failedFuture( - new NoPermissionException( - childCheck, - sender, - this.getComponentChain(child) - ) + this.noPermissionOrSyntax(childCheck, sender, child) ); } @@ -447,11 +501,7 @@ private CommandTree(final @NonNull CommandManager commandManager) { return CompletableFuture.completedFuture(command); } return CompletableFutures.failedFuture( - new NoPermissionException( - check, - sender, - this.getComponentChain(root) - ) + this.noPermissionOrSyntax(check, sender, root) ); } else { // The child is not a leaf, but may have an intermediary executor, attempt to use it @@ -476,11 +526,7 @@ private CommandTree(final @NonNull CommandManager commandManager) { } return CompletableFutures.failedFuture( - new NoPermissionException( - check, - sender, - this.getComponentChain(root) - ) + this.noPermissionOrSyntax(check, sender, root) ); } } diff --git a/cloud-core/src/main/java/org/incendo/cloud/setting/ManagerSetting.java b/cloud-core/src/main/java/org/incendo/cloud/setting/ManagerSetting.java index 234a0b489..2632d94ca 100644 --- a/cloud-core/src/main/java/org/incendo/cloud/setting/ManagerSetting.java +++ b/cloud-core/src/main/java/org/incendo/cloud/setting/ManagerSetting.java @@ -61,5 +61,8 @@ public enum ManagerSetting implements Setting { * and code inspecting the command tree may need to be adjusted. */ @API(status = API.Status.EXPERIMENTAL) - LIBERAL_FLAG_PARSING + LIBERAL_FLAG_PARSING, + + @API(status = API.Status.EXPERIMENTAL) + CONVERT_NO_PERMISSION_TO_SYNTAX_EXCEPTION }