001//
002// MIT License
003//
004// Copyright (c) 2021 Alexander Söderberg & Contributors
005//
006// Permission is hereby granted, free of charge, to any person obtaining a copy
007// of this software and associated documentation files (the "Software"), to deal
008// in the Software without restriction, including without limitation the rights
009// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010// copies of the Software, and to permit persons to whom the Software is
011// furnished to do so, subject to the following conditions:
012//
013// The above copyright notice and this permission notice shall be included in all
014// copies or substantial portions of the Software.
015//
016// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022// SOFTWARE.
023//
024package cloud.commandframework.arguments.standard;
025
026import cloud.commandframework.ArgumentDescription;
027import cloud.commandframework.arguments.CommandArgument;
028import cloud.commandframework.arguments.parser.ArgumentParseResult;
029import cloud.commandframework.arguments.parser.ArgumentParser;
030import cloud.commandframework.context.CommandContext;
031import cloud.commandframework.exceptions.parsing.NoInputProvidedException;
032import cloud.commandframework.exceptions.parsing.NumberParseException;
033import org.checkerframework.checker.nullness.qual.NonNull;
034import org.checkerframework.checker.nullness.qual.Nullable;
035
036import java.util.List;
037import java.util.Queue;
038import java.util.function.BiFunction;
039
040@SuppressWarnings("unused")
041public final class ByteArgument<C> extends CommandArgument<C, Byte> {
042
043    private final byte min;
044    private final byte max;
045
046    private ByteArgument(
047            final boolean required,
048            final @NonNull String name,
049            final byte min,
050            final byte max,
051            final @NonNull String defaultValue,
052            final @Nullable BiFunction<@NonNull CommandContext<C>, @NonNull String,
053                    @NonNull List<@NonNull String>> suggestionsProvider,
054            final @NonNull ArgumentDescription defaultDescription
055    ) {
056        super(required, name, new ByteParser<>(min, max), defaultValue, Byte.class, suggestionsProvider, defaultDescription);
057        this.min = min;
058        this.max = max;
059    }
060
061    /**
062     * Create a new builder
063     *
064     * @param name Name of the argument
065     * @param <C>  Command sender type
066     * @return Created builder
067     */
068    public static <C> @NonNull Builder<C> newBuilder(final @NonNull String name) {
069        return new Builder<>(name);
070    }
071
072    /**
073     * Create a new required command argument
074     *
075     * @param name Argument name
076     * @param <C>  Command sender type
077     * @return Created argument
078     */
079    public static <C> @NonNull CommandArgument<C, Byte> of(final @NonNull String name) {
080        return ByteArgument.<C>newBuilder(name).asRequired().build();
081    }
082
083    /**
084     * Create a new optional command argument
085     *
086     * @param name Argument name
087     * @param <C>  Command sender type
088     * @return Created argument
089     */
090    public static <C> @NonNull CommandArgument<C, Byte> optional(final @NonNull String name) {
091        return ByteArgument.<C>newBuilder(name).asOptional().build();
092    }
093
094    /**
095     * Create a new required command argument with a default value
096     *
097     * @param name       Argument name
098     * @param defaultNum Default num
099     * @param <C>        Command sender type
100     * @return Created argument
101     */
102    public static <C> @NonNull CommandArgument<C, Byte> optional(
103            final @NonNull String name,
104            final byte defaultNum
105    ) {
106        return ByteArgument.<C>newBuilder(name).asOptionalWithDefault(Byte.toString(defaultNum)).build();
107    }
108
109    /**
110     * Get the minimum accepted byteeger that could have been parsed
111     *
112     * @return Minimum byteeger
113     */
114    public byte getMin() {
115        return this.min;
116    }
117
118    /**
119     * Get the maximum accepted byteeger that could have been parsed
120     *
121     * @return Maximum byteeger
122     */
123    public byte getMax() {
124        return this.max;
125    }
126
127    public static final class Builder<C> extends CommandArgument.Builder<C, Byte> {
128
129        private byte min = Byte.MIN_VALUE;
130        private byte max = Byte.MAX_VALUE;
131
132        private Builder(final @NonNull String name) {
133            super(Byte.class, name);
134        }
135
136        /**
137         * Set a minimum value
138         *
139         * @param min Minimum value
140         * @return Builder instance
141         */
142        public @NonNull Builder<C> withMin(final byte min) {
143            this.min = min;
144            return this;
145        }
146
147        /**
148         * Set a maximum value
149         *
150         * @param max Maximum value
151         * @return Builder instance
152         */
153        public @NonNull Builder<C> withMax(final byte max) {
154            this.max = max;
155            return this;
156        }
157
158        /**
159         * Builder a new byte argument
160         *
161         * @return Constructed argument
162         */
163        @Override
164        public @NonNull ByteArgument<C> build() {
165            return new ByteArgument<>(this.isRequired(), this.getName(), this.min, this.max,
166                    this.getDefaultValue(), this.getSuggestionsProvider(), this.getDefaultDescription()
167            );
168        }
169
170    }
171
172    public static final class ByteParser<C> implements ArgumentParser<C, Byte> {
173
174        private final byte min;
175        private final byte max;
176
177        /**
178         * Construct a new byte parser
179         *
180         * @param min Minimum value
181         * @param max Maximum value
182         */
183        public ByteParser(final byte min, final byte max) {
184            this.min = min;
185            this.max = max;
186        }
187
188        @Override
189        public @NonNull ArgumentParseResult<Byte> parse(
190                final @NonNull CommandContext<C> commandContext,
191                final @NonNull Queue<@NonNull String> inputQueue
192        ) {
193            final String input = inputQueue.peek();
194            if (input == null) {
195                return ArgumentParseResult.failure(new NoInputProvidedException(
196                        ByteParser.class,
197                        commandContext
198                ));
199            }
200            try {
201                final byte value = Byte.parseByte(input);
202                if (value < this.min || value > this.max) {
203                    return ArgumentParseResult.failure(
204                            new ByteParseException(
205                                    input,
206                                    this.min,
207                                    this.max,
208                                    commandContext
209                            ));
210                }
211                inputQueue.remove();
212                return ArgumentParseResult.success(value);
213            } catch (final Exception e) {
214                return ArgumentParseResult.failure(
215                        new ByteParseException(
216                                input,
217                                this.min,
218                                this.max,
219                                commandContext
220                        ));
221            }
222        }
223
224        @Override
225        public boolean isContextFree() {
226            return true;
227        }
228
229        @Override
230        public @NonNull List<@NonNull String> suggestions(
231                final @NonNull CommandContext<C> commandContext,
232                final @NonNull String input
233        ) {
234            return IntegerArgument.IntegerParser.getSuggestions(this.min, this.max, input);
235        }
236
237        /**
238         * Get the max value
239         *
240         * @return Max value
241         */
242        public byte getMax() {
243            return this.max;
244        }
245
246        /**
247         * Get the min value
248         *
249         * @return Min value
250         */
251        public byte getMin() {
252            return this.min;
253        }
254
255    }
256
257
258    /**
259     * Byte parse exception
260     */
261    public static final class ByteParseException extends NumberParseException {
262
263        private static final long serialVersionUID = -4724241304872989208L;
264
265        /**
266         * Construct a new byte parse exception
267         *
268         * @param input   String input
269         * @param min     Minimum value
270         * @param max     Maximum value
271         * @param context Command context
272         */
273        public ByteParseException(
274                final @NonNull String input,
275                final byte min,
276                final byte max,
277                final @NonNull CommandContext<?> context
278        ) {
279            super(
280                    input,
281                    min,
282                    max,
283                    ByteParser.class,
284                    context
285            );
286        }
287
288        @Override
289        public boolean hasMin() {
290            return this.getMin().byteValue() != Byte.MIN_VALUE;
291        }
292
293        @Override
294        public boolean hasMax() {
295            return this.getMax().byteValue() != Byte.MAX_VALUE;
296        }
297
298        @Override
299        public @NonNull String getNumberType() {
300            return "byte";
301        }
302
303    }
304
305}