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}