001/* 002 * PermissionsEx 003 * Copyright (C) zml and PermissionsEx contributors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package ca.stellardrift.permissionsex.minecraft.command; 018 019import ca.stellardrift.permissionsex.PermissionsEngine; 020import ca.stellardrift.permissionsex.context.ContextValue; 021import ca.stellardrift.permissionsex.impl.util.PCollections; 022import ca.stellardrift.permissionsex.minecraft.command.argument.Parsers; 023import cloud.commandframework.arguments.CommandArgument; 024import cloud.commandframework.arguments.flags.CommandFlag; 025import cloud.commandframework.context.CommandContext; 026import cloud.commandframework.execution.CommandExecutionHandler; 027import cloud.commandframework.minecraft.extras.RichDescription; 028import io.leangen.geantyref.TypeToken; 029import net.kyori.adventure.text.Component; 030import org.checkerframework.checker.nullness.qual.Nullable; 031import org.spongepowered.configurate.reactive.Publisher; 032import org.spongepowered.configurate.reactive.Subscriber; 033 034import java.util.List; 035import java.util.Set; 036import java.util.concurrent.CompletableFuture; 037import java.util.concurrent.CompletionException; 038import java.util.function.BiConsumer; 039import java.util.function.BiFunction; 040import java.util.function.Consumer; 041 042/** 043 * Assorted pieces of command handling, used across commands. 044 */ 045public final class Elements { 046 047 public static final CommandFlag<Void> FLAG_TRANSIENT = CommandFlag.newBuilder("transient") 048 .withAliases("t") 049 .withDescription(RichDescription.of(Messages.COMMON_TRANSIENT_DESCRIPTION)) 050 .build(); 051 052 public static CommandFlag<ContextValue<?>> FLAG_CONTEXT = CommandFlag.newBuilder("contexts") 053 .withAliases("c") 054 .withArgument(CommandArgument.<Commander, ContextValue<?>>ofType(new TypeToken<ContextValue<?>>() {}, "value") 055 .withParser(Parsers.contextValue())) 056 .withDescription(RichDescription.of(Messages.COMMON_CONTEXT_DESCRIPTION)) 057 .build(); 058 059 public static Set<ContextValue<?>> contexts(final CommandContext<?> context) { 060 final @Nullable ContextValue<?> single = context.flags().get(FLAG_CONTEXT.getName()); 061 if (single == null) { 062 return PCollections.set(); 063 } else { 064 return PCollections.set(single); 065 } 066 } 067 068 /** 069 * To be used with {@link CompletableFuture#whenComplete(BiConsumer)} 070 */ 071 public static <V> BiConsumer<V, Throwable> messageSender( 072 final Commander src, 073 final Component message 074 ) { 075 return messageSender(src, send -> send.accept(message)); 076 } 077 078 /** 079 * Message a subject with the result of a completable future. 080 * 081 * <p>intended to be used with {@link CompletableFuture#whenComplete(BiConsumer)}</p> 082 * 083 * @param src the commander to receive the response 084 * @param callback the callback to execute on completion 085 * @param <V> upstream input value 086 * @return a handler function for a future 087 */ 088 public static <V> BiConsumer<@Nullable V, @Nullable Throwable> messageSender( 089 final Commander src, 090 final Consumer<Consumer<Component>> callback 091 ) { 092 093 return (result, err) -> { 094 if (err != null) { 095 final Throwable cause = err.getCause(); 096 if (err instanceof CompletionException && cause != null) { 097 err = cause; 098 } 099 if (err instanceof CommandException) { 100 src.error(((CommandException) err).componentMessage()); 101 } else { 102 // TODO: err.getMessage() can be null 103 src.error(Messages.EXECUTOR_ERROR_ASYNC_TASK.tr(err.getClass().getSimpleName(), err.getMessage()), err); 104 src.formatter().manager().engine().logger().error(Messages.EXECUTOR_ERROR_ASYNC_TASK_CONSOLE.tr(src.name()), err); 105 } 106 } else { 107 callback.accept(src::sendMessage); 108 } 109 }; 110 } 111 112 public static <V> CompletableFuture<V> toCompletableFuture(final Publisher<V> publisher) { 113 final CompletableFuture<V> ret = new CompletableFuture<>(); 114 publisher.subscribe(new Subscriber<V>() { 115 @Override 116 public void submit(final V item) { 117 ret.complete(item); 118 } 119 120 @Override 121 public void onError(final Throwable ex) { 122 ret.completeExceptionally(ex); 123 } 124 }); 125 return ret; 126 } 127 128 @FunctionalInterface 129 public interface PexExecutor<C> { 130 void execute(C sender, PermissionsEngine engine, CommandContext<C> ctx); 131 } 132 133 public static <C> CommandExecutionHandler<C> handler(final PexExecutor<C> executor) { 134 return ctx -> executor.execute(ctx.getSender(), ctx.get(PEXCommandPreprocessor.PEX_ENGINE), ctx); 135 } 136 137 public static <C> BiFunction<CommandContext<C>, String, List<String>> engineCompletions(final BiFunction<PermissionsEngine, String, List<String>> completer) { 138 return (ctx, input) -> { 139 final PermissionsEngine engine = ctx.get(PEXCommandPreprocessor.PEX_ENGINE); 140 return completer.apply(engine, input); 141 }; 142 } 143 144 private Elements() { 145 } 146 147}