001/*
002 *     Copyright 2021 Siroshun09
003 *
004 *     Licensed under the Apache License, Version 2.0 (the "License");
005 *     you may not use this file except in compliance with the License.
006 *     You may obtain a copy of the License at
007 *
008 *         http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *     Unless required by applicable law or agreed to in writing, software
011 *     distributed under the License is distributed on an "AS IS" BASIS,
012 *     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *     See the License for the specific language governing permissions and
014 *     limitations under the License.
015 */
016
017package com.github.siroshun09.mccommand.common.argument.parser;
018
019import com.github.siroshun09.mccommand.common.argument.Argument;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022
023import java.util.Optional;
024import java.util.function.Function;
025
026/**
027 * Parses an argument from a String
028 *
029 * @param <T> the value type
030 */
031@FunctionalInterface
032public interface ArgumentParser<T> {
033
034    /**
035     * Creates an instance of the parser from {@link Function}.
036     *
037     * @param function the {@link Function} to parse {@link Argument} to a specific type.
038     * @param <R>      the value type
039     * @return the {@link ArgumentParser} instance
040     */
041    static <R> ArgumentParser<R> of(@NotNull Function<Argument, R> function) {
042        return function::apply;
043    }
044
045    /**
046     * Parses an {@link Argument} to a specified type and returns it.
047     *
048     * @param argument the argument to be parsed
049     * @return the value to be parsed, or {@code null} if the parsing fails
050     */
051    @Nullable
052    T parse(@NotNull Argument argument);
053
054    /**
055     * Parses an {@link Argument} to a specified type and returns it.
056     *
057     * @param argument the argument to be parsed
058     * @return the {@link Optional} value
059     */
060    default Optional<T> parseOptional(@NotNull Argument argument) {
061        return Optional.ofNullable(parse(argument));
062    }
063
064    /**
065     * Parses an {@link Argument} to a specified type and returns it.
066     *
067     * @param argument the argument to be parsed
068     * @param def      the default value to be returned if the parsing fails
069     * @return the value to be parsed, or default value if the parsing fails
070     */
071    @NotNull
072    default T parseOrDefault(@NotNull Argument argument, @NotNull T def) {
073        T value = parse(argument);
074
075        return value != null ? value : def;
076    }
077
078    /**
079     * Parses an {@link Argument} to a specified type and returns it.
080     * <p>
081     * Depending on the implementation, it is possible to use {@link IllegalArgumentException#getCause()}
082     * to get the exception that caused the conversion to fail.
083     *
084     * @param argument the argument to be parsed
085     * @return the value to be parsed, or {@code null} if the parsing fails
086     * @throws IllegalArgumentException if the parsing fails
087     */
088    @NotNull
089    default T parseOrThrow(@NotNull Argument argument) throws IllegalArgumentException {
090        T value = parse(argument);
091
092        if (value != null) {
093            return value;
094        } else {
095            throw generateException(argument);
096        }
097    }
098
099    /**
100     * Generates an exception that throws when the parsing fails.
101     *
102     * @param argument the argument that failed to be parsed
103     * @return the generated exception
104     */
105    default IllegalArgumentException generateException(@NotNull Argument argument) {
106        return generateException(argument, null);
107    }
108
109    /**
110     * Generates an exception that throws when the parsing fails.
111     *
112     * @param argument  the argument that failed to be parsed
113     * @param exception the exception that caused the parsing to fail
114     * @return the generated exception
115     */
116    default IllegalArgumentException generateException(@NotNull Argument argument, @Nullable Throwable exception) {
117        return new IllegalArgumentException(
118                "Could not parse argument '" + argument.get() + "' (index: " + argument.getIndex() + ")", exception
119        );
120    }
121}