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.argument;
018
019import ca.stellardrift.permissionsex.impl.util.PCollections;
020import ca.stellardrift.permissionsex.minecraft.MinecraftPermissionsEx;
021import ca.stellardrift.permissionsex.minecraft.command.PEXCommandPreprocessor;
022import ca.stellardrift.permissionsex.subject.InvalidIdentifierException;
023import ca.stellardrift.permissionsex.subject.SubjectType;
024import cloud.commandframework.arguments.parser.ArgumentParseResult;
025import cloud.commandframework.arguments.parser.ArgumentParser;
026import cloud.commandframework.context.CommandContext;
027import cloud.commandframework.exceptions.parsing.NoInputProvidedException;
028import org.checkerframework.checker.nullness.qual.NonNull;
029import org.checkerframework.checker.nullness.qual.Nullable;
030
031import java.util.List;
032import java.util.Queue;
033import java.util.function.Function;
034import java.util.stream.Collectors;
035
036public final class SubjectIdentifierParser<C, I> implements ArgumentParser<C, I> {
037    private final boolean contextFree;
038    private final Function<CommandContext<C>, SubjectType<I>> subjectTypeExtractor;
039
040    SubjectIdentifierParser(final SubjectType<I> identifier) {
041        this.subjectTypeExtractor = ctx -> identifier;
042        this.contextFree = true;
043    }
044
045    SubjectIdentifierParser(final Function<CommandContext<C>, SubjectType<I>> subjectTypeExtractor) {
046        this.subjectTypeExtractor = subjectTypeExtractor;
047        this.contextFree = false;
048    }
049
050    @Override
051    public @NonNull ArgumentParseResult<@NonNull I> parse(
052        @NonNull final CommandContext<@NonNull C> commandContext,
053        @NonNull final Queue<@NonNull String> inputQueue
054    ) {
055        final @Nullable String input = inputQueue.peek();
056        if (input == null) {
057            return ArgumentParseResult.failure(new NoInputProvidedException(
058                SubjectIdentifierParser.class,
059                commandContext)
060            );
061        }
062        final SubjectType<I> subjectType = this.subjectTypeExtractor.apply(commandContext);
063        @Nullable InvalidIdentifierException possibleException = null;
064        @Nullable I identifier;
065        try {
066            identifier = subjectType.parseIdentifier(input);
067        } catch (final InvalidIdentifierException ex) {
068            possibleException = ex;
069            identifier = subjectType.parseOrCoerceIdentifier(input);
070        }
071
072        if (identifier == null) {
073            return ArgumentParseResult.failure(possibleException);
074        }
075
076        inputQueue.remove();
077        return ArgumentParseResult.success(identifier);
078    }
079
080    @Override
081    public @NonNull List<@NonNull String> suggestions(
082        @NonNull final CommandContext<C> commandContext,
083        @NonNull final String input
084    ) {
085        final MinecraftPermissionsEx<?> manager = commandContext.get(PEXCommandPreprocessor.PEX_MANAGER);
086        final SubjectType<I> type = this.subjectTypeExtractor.apply(commandContext);
087        if (type == null) {
088            return PCollections.vector();
089        }
090        // TODO: Include friendly names here?
091        // TODO: Do we want to filter this
092        return manager.engine().subjects(type).allIdentifiers()
093            .limit(100)
094            .map(type::serializeIdentifier)
095            .collect(Collectors.toList());
096    }
097
098    @Override
099    public boolean isContextFree() {
100        return this.contextFree;
101    }
102
103}