diff --git a/plutocommandparser/build.gradle b/plutocommandparser/build.gradle new file mode 100644 index 0000000..5852874 --- /dev/null +++ b/plutocommandparser/build.gradle @@ -0,0 +1,9 @@ +apply plugin: 'java-library' + +description = "" + +dependencies { + api project(":plutolib") + + testImplementation("org.junit.jupiter:junit-jupiter:5.6.2") +} \ No newline at end of file diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/CommandBase.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/CommandBase.java new file mode 100644 index 0000000..709670e --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/CommandBase.java @@ -0,0 +1,18 @@ +package cz.tefek.pluto.command; + +public abstract class CommandBase +{ + public abstract String name(); + + public abstract String[] aliases(); + + public abstract String description(); + + public abstract Class commandClass(); + + @Override + public final int hashCode() + { + return this.name().hashCode(); + } +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/context/CommandContextBuilder.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/context/CommandContextBuilder.java new file mode 100644 index 0000000..758b7eb --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/context/CommandContextBuilder.java @@ -0,0 +1,96 @@ +package cz.tefek.pluto.command.context; + +import cz.tefek.pluto.command.CommandBase; + +public class CommandContextBuilder +{ + private CommandContext ctx; + + public CommandContextBuilder() + { + this.ctx = new CommandContext(); + } + + public CommandContextBuilder prefix(String prefix) + { + this.ctx.usedPrefix = prefix; + + return this; + } + + public CommandContextBuilder alias(String alias) + { + this.ctx.usedAlias = alias; + + return this; + } + + public CommandContextBuilder command(CommandBase command) + { + this.ctx.command = command; + + return this; + } + + public CommandContext resolved() + { + this.ctx.resolved = true; + + return this.ctx; + } + + public CommandContext unresolved(EnumCommandParseFailure cause) + { + this.ctx.resolved = false; + + return this.ctx; + } + + public static class CommandContext + { + private boolean resolved; + private EnumCommandParseFailure failureCause; + + private String usedPrefix; + private String usedAlias; + private CommandBase command; + + private CommandContext() + { + + } + + public String getUsedPrefix() + { + return this.usedPrefix; + } + + public String getUsedAlias() + { + return this.usedAlias; + } + + public CommandBase getCommand() + { + return this.command; + } + + public boolean isResolved() + { + return this.resolved; + } + + public EnumCommandParseFailure getFailureCause() + { + return this.failureCause; + } + } + + public enum EnumCommandParseFailure + { + UNRESOLVED_PREFIX, + UNRESOLVED_COMMAND_NAME, + UNRESOLVED_PARAMETERS, + UNRESOLVED_UNEXPECTED_STATE; + } +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/parser/CommandParser.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/parser/CommandParser.java new file mode 100644 index 0000000..8f2dfbd --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/parser/CommandParser.java @@ -0,0 +1,252 @@ +package cz.tefek.pluto.command.parser; + +import java.util.PrimitiveIterator.OfInt; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import cz.tefek.pluto.command.CommandBase; +import cz.tefek.pluto.command.context.CommandContextBuilder; +import cz.tefek.pluto.command.context.CommandContextBuilder.CommandContext; +import cz.tefek.pluto.command.context.CommandContextBuilder.EnumCommandParseFailure; +import cz.tefek.pluto.command.registry.CommandRegistry; + +public class CommandParser +{ + private String text; + private Set prefixes; + private EnumParserState state; + + private StringBuilder prefixBuilder; + private StringBuilder commandNameBuilder; + + private CommandBase command; + + private StringBuilder parameterBuilder; + + private CommandContextBuilder ctx; + + private static final int CP_QUOTE = '"'; + + public CommandParser(String text) + { + this.text = text; + this.state = EnumParserState.BEGIN; + } + + private boolean readCodepoint(int cp) + { + switch (this.state) + { + case READING_PREFIX: + this.prefixBuilder.appendCodePoint(cp); + + this.prefixes.removeIf(ii -> ii.nextInt() != cp); + + if (this.prefixes.isEmpty()) + { + this.state = EnumParserState.END_NO_PREFIX; + return false; + } + + if (this.hasEmptyPrefix()) + { + this.ctx.prefix(this.prefixBuilder.toString()); + this.state = EnumParserState.READING_COMMAND; + } + + break; + + case READING_COMMAND: + this.commandNameBuilder.appendCodePoint(cp); + + if (Character.isWhitespace(cp)) + { + if (!this.resolveCommand()) + { + return false; + } + + this.state = EnumParserState.READ_WHITESPACE; + } + + break; + + case READ_WHITESPACE: + if (Character.isWhitespace(cp)) + { + break; + } + + if (cp == CP_QUOTE) + { + this.state = EnumParserState.READING_PARAMETER_QUOTED; + } + else + { + this.parameterBuilder.appendCodePoint(cp); + this.state = EnumParserState.READING_PARAMETER; + } + + break; + + case READING_PARAMETER_QUOTED: + if (cp == CP_QUOTE) + { + this.state = EnumParserState.READING_PARAMETER_CANDIDATE_UNQUOTE; + } + else + { + this.parameterBuilder.appendCodePoint(cp); + } + + break; + + case READING_PARAMETER: + if (Character.isWhitespace(cp)) + { + this.emitParameter(); + this.state = EnumParserState.READ_WHITESPACE; + } + else + { + this.parameterBuilder.appendCodePoint(cp); + } + + break; + + case READING_PARAMETER_CANDIDATE_UNQUOTE: + if (Character.isWhitespace(cp)) + { + this.emitParameter(); + this.state = EnumParserState.READ_WHITESPACE; + } + else + { + this.parameterBuilder.appendCodePoint(cp); + this.state = EnumParserState.READING_PARAMETER_QUOTED; + } + + break; + + case END: + this.state = EnumParserState.UNEXPECTED_STATE_FALLBACK; + return false; + + default: + this.state = EnumParserState.UNEXPECTED_STATE_FALLBACK; + return false; + } + + return true; + } + + private boolean resolveCommand() + { + var alias = this.commandNameBuilder.toString(); + + this.ctx.alias(alias); + + this.command = CommandRegistry.getByAlias(alias); + + if (this.command == null) + { + this.state = EnumParserState.END_NO_COMMAND; + return false; + } + + return true; + } + + private void emitParameter() + { + + } + + private boolean hasEmptyPrefix() + { + return this.prefixes.stream().anyMatch(Predicate.not(OfInt::hasNext)); + } + + /** + * Parse using this parser and supplied prefixes. This function also + * resolves the command context and the parameters. Yeah it does a lot of + * stuff. + * + */ + public CommandContext parse(Set prefixes) + { + if (this.state != EnumParserState.BEGIN) + { + throw new IllegalStateException("Cannot run a parser that is not in the BEGIN state."); + } + + this.prefixBuilder = new StringBuilder(); + this.ctx = new CommandContextBuilder(); + + this.prefixes = prefixes.stream().map(String::codePoints).map(IntStream::iterator).collect(Collectors.toSet()); + + if (prefixes.isEmpty() || this.hasEmptyPrefix()) + { + this.state = EnumParserState.READING_COMMAND; + } + else + { + this.state = EnumParserState.READING_PREFIX; + } + + this.text.codePoints().takeWhile(this::readCodepoint); + + // Update the state for EOF + switch (this.state) + { + case READING_PARAMETER_QUOTED: + case READ_WHITESPACE: + case READING_PARAMETER: + case READING_PARAMETER_CANDIDATE_UNQUOTE: + this.state = EnumParserState.END; + this.emitParameter(); + + break; + + case READING_COMMAND: + if (this.resolveCommand()) + { + this.state = EnumParserState.END; + } + + break; + + default: + break; + } + + // Check the end state + switch (this.state) + { + case READING_PREFIX: + case END_NO_PREFIX: + return this.ctx.unresolved(EnumCommandParseFailure.UNRESOLVED_PREFIX); + + case END_NO_COMMAND: + return this.ctx.unresolved(EnumCommandParseFailure.UNRESOLVED_COMMAND_NAME); + + case END: + break; + + default: + return this.ctx.unresolved(EnumCommandParseFailure.UNRESOLVED_UNEXPECTED_STATE); + } + + // At this point we are 100% sure the command was resolved and can validate the parameters + + /* + * + * TODO: Validate parameters here + * + */ + + return this.ctx.resolved(); + } +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/parser/EnumParserState.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/parser/EnumParserState.java new file mode 100644 index 0000000..570a02f --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/parser/EnumParserState.java @@ -0,0 +1,17 @@ +package cz.tefek.pluto.command.parser; + +public enum EnumParserState +{ + BEGIN, + READING_PREFIX, + READING_COMMAND, + READING_PARAMETER, + READING_PARAMETER_QUOTED, + READING_PARAMETER_CANDIDATE_UNQUOTE, + READ_WHITESPACE, + END_NO_PREFIX, + END_NO_COMMAND, + END_EMISSION_FAILURE, + END, + UNEXPECTED_STATE_FALLBACK; +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/platform/CommandPlatform.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/platform/CommandPlatform.java new file mode 100644 index 0000000..1f02629 --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/platform/CommandPlatform.java @@ -0,0 +1,26 @@ +package cz.tefek.pluto.command.platform; + +import cz.tefek.pluto.command.context.CommandContextBuilder.EnumCommandParseFailure; + +public abstract class CommandPlatform +{ + public abstract String getID(); + + public abstract String getName(); + + public boolean shouldWarnOn(EnumCommandParseFailure failure) + { + switch (failure) + { + case UNRESOLVED_PREFIX: + case UNRESOLVED_COMMAND_NAME: + case UNRESOLVED_UNEXPECTED_STATE: + return false; + + default: + return true; + } + } + + public abstract int getMessageLimit(); +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/registry/CommandRegistry.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/registry/CommandRegistry.java new file mode 100644 index 0000000..a69c96d --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/registry/CommandRegistry.java @@ -0,0 +1,71 @@ +package cz.tefek.pluto.command.registry; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import cz.tefek.pluto.command.CommandBase; +import cz.tefek.pluto.io.logger.Logger; +import cz.tefek.pluto.io.logger.SmartSeverity; + +public final class CommandRegistry +{ + private static CommandRegistry instance; + + private final Set commands; + private final Map aliasTable; + + static + { + instance = new CommandRegistry(); + } + + private CommandRegistry() + { + this.aliasTable = new HashMap<>(); + this.commands = new HashSet<>(); + } + + private void registerAlias(String alias, CommandBase command) + { + if (this.aliasTable.containsKey(alias)) + { + Logger.logf(SmartSeverity.ERROR, "Alias '%s' for command '%s' is already used, skipping.%n", alias, command.name()); + + return; + } + + this.aliasTable.put(alias, command); + } + + public static void registerCommand(CommandBase command) + { + if (!instance.commands.add(command)) + { + Logger.logf(SmartSeverity.ERROR, "Command '%s' is already registered, skipping.%n", command.name()); + return; + } + + instance.registerAlias(command.name(), command); + + Arrays.stream(command.aliases()).forEach(alias -> instance.registerAlias(alias, command)); + } + + public static CommandBase getByAlias(String alias) + { + return instance.aliasTable.get(alias); + } + + public static Set getCommands() + { + return Collections.unmodifiableSet(instance.commands); + } + + public static void clear() + { + instance = new CommandRegistry(); + } +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/AbstractResolveException.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/AbstractResolveException.java new file mode 100644 index 0000000..18847fd --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/AbstractResolveException.java @@ -0,0 +1,24 @@ +package cz.tefek.pluto.command.resolver; + +public abstract class AbstractResolveException extends RuntimeException +{ + /** + * + */ + private static final long serialVersionUID = -8934505669078637864L; + + public AbstractResolveException(Exception exception) + { + super(exception); + } + + public AbstractResolveException(String what) + { + super(what); + } + + protected AbstractResolveException() + { + + } +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/AbstractResolver.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/AbstractResolver.java new file mode 100644 index 0000000..3cba691 --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/AbstractResolver.java @@ -0,0 +1,6 @@ +package cz.tefek.pluto.command.resolver; + +public abstract class AbstractResolver +{ + public abstract Class getOutputType(); +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/EnumResolveFailure.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/EnumResolveFailure.java new file mode 100644 index 0000000..bdab475 --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/EnumResolveFailure.java @@ -0,0 +1,13 @@ +package cz.tefek.pluto.command.resolver; + +public enum EnumResolveFailure +{ + INT_PARSE, + LONG_PARSE, + FLOAT_PARSE, + DOUBLE_PARSE, + FRAC_INVALID_PERCENTAGE, + FRAC_PERCENTAGE_OUT_OF_RANGE, + FRAC_VALUE_HIGHER_THAN_BASE, + OTHER; +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/GenericResolver.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/GenericResolver.java new file mode 100644 index 0000000..59692b3 --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/GenericResolver.java @@ -0,0 +1,21 @@ +package cz.tefek.pluto.command.resolver; + +import java.util.function.Function; + +public abstract class GenericResolver extends AbstractResolver +{ + protected Function func; + + public GenericResolver(Function func) + { + this.func = func; + } + + public R apply(String param) + { + return this.func.apply(param); + } + + @Override + public abstract Class getOutputType(); +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/ResolveException.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/ResolveException.java new file mode 100644 index 0000000..5cdda12 --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/ResolveException.java @@ -0,0 +1,22 @@ +package cz.tefek.pluto.command.resolver; + +public class ResolveException extends AbstractResolveException +{ + /** + * + */ + private static final long serialVersionUID = -3098373878754649161L; + + private EnumResolveFailure failure; + + public ResolveException(EnumResolveFailure failure) + { + this.failure = failure; + } + + public EnumResolveFailure getResolveFailure() + { + return this.failure; + } + +} \ No newline at end of file diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/BasicDoubleResolver.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/BasicDoubleResolver.java new file mode 100644 index 0000000..c2127f1 --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/BasicDoubleResolver.java @@ -0,0 +1,23 @@ +package cz.tefek.pluto.command.resolver.primitive; + +import cz.tefek.pluto.command.resolver.EnumResolveFailure; +import cz.tefek.pluto.command.resolver.ResolveException; + +public class BasicDoubleResolver extends DoubleResolver +{ + public BasicDoubleResolver() + { + super(str -> + { + try + { + return Double.parseDouble(str); + } + catch (NumberFormatException e) + { + throw new ResolveException(EnumResolveFailure.DOUBLE_PARSE); + } + }); + } + +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/BasicIntResolver.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/BasicIntResolver.java new file mode 100644 index 0000000..47cd2c0 --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/BasicIntResolver.java @@ -0,0 +1,22 @@ +package cz.tefek.pluto.command.resolver.primitive; + +import cz.tefek.pluto.command.resolver.EnumResolveFailure; +import cz.tefek.pluto.command.resolver.ResolveException; + +public class BasicIntResolver extends IntResolver +{ + public BasicIntResolver() + { + super(str -> + { + try + { + return Integer.parseInt(str); + } + catch (NumberFormatException e) + { + throw new ResolveException(EnumResolveFailure.INT_PARSE); + } + }); + } +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/BasicLongResolver.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/BasicLongResolver.java new file mode 100644 index 0000000..e46ab67 --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/BasicLongResolver.java @@ -0,0 +1,23 @@ +package cz.tefek.pluto.command.resolver.primitive; + +import cz.tefek.pluto.command.resolver.EnumResolveFailure; +import cz.tefek.pluto.command.resolver.ResolveException; + +public class BasicLongResolver extends LongResolver +{ + public BasicLongResolver() + { + super(str -> + { + try + { + return Long.parseLong(str); + } + catch (NumberFormatException e) + { + throw new ResolveException(EnumResolveFailure.LONG_PARSE); + } + }); + } + +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/DoubleResolver.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/DoubleResolver.java new file mode 100644 index 0000000..eb3de72 --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/DoubleResolver.java @@ -0,0 +1,26 @@ +package cz.tefek.pluto.command.resolver.primitive; + +import java.util.function.ToDoubleFunction; + +import cz.tefek.pluto.command.resolver.AbstractResolver; + +public class DoubleResolver extends AbstractResolver +{ + protected ToDoubleFunction func; + + public DoubleResolver(ToDoubleFunction func) + { + this.func = func; + } + + public double apply(String param) + { + return this.func.applyAsDouble(param); + } + + @Override + public Class getOutputType() + { + return double.class; + } +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/IntFractionResolver.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/IntFractionResolver.java new file mode 100644 index 0000000..7af03d7 --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/IntFractionResolver.java @@ -0,0 +1,63 @@ +package cz.tefek.pluto.command.resolver.primitive; + +import cz.tefek.pluto.command.resolver.EnumResolveFailure; +import cz.tefek.pluto.command.resolver.ResolveException; + +public class IntFractionResolver extends IntResolver +{ + public IntFractionResolver(int base) + { + super(str -> parseAmount(str, base)); + } + + private static int parseAmount(String amountString, int base) throws ResolveException + { + if (amountString.equalsIgnoreCase("all") || amountString.equalsIgnoreCase("everything")) + { + return base; + } + + if (amountString.equalsIgnoreCase("half")) + { + return base / 2; + } + + if (amountString.endsWith("%")) + { + try + { + float percentage = Float.parseFloat(amountString.substring(0, amountString.length() - 1)); + + if (percentage < 0 || percentage > 100) + { + throw new ResolveException(EnumResolveFailure.FRAC_PERCENTAGE_OUT_OF_RANGE); + } + else + { + return Math.round(percentage / 100.0f * base); + } + } + catch (NumberFormatException e1) + { + throw new ResolveException(EnumResolveFailure.FRAC_INVALID_PERCENTAGE); + } + } + + try + { + int amount = Integer.parseInt(amountString); + + if (amount > base) + { + throw new ResolveException(EnumResolveFailure.FRAC_VALUE_HIGHER_THAN_BASE); + } + + return amount; + } + catch (NumberFormatException e) + { + throw new ResolveException(EnumResolveFailure.INT_PARSE); + } + } + +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/IntResolver.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/IntResolver.java new file mode 100644 index 0000000..f958cf8 --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/IntResolver.java @@ -0,0 +1,26 @@ +package cz.tefek.pluto.command.resolver.primitive; + +import java.util.function.ToIntFunction; + +import cz.tefek.pluto.command.resolver.AbstractResolver; + +public class IntResolver extends AbstractResolver +{ + protected ToIntFunction func; + + public IntResolver(ToIntFunction func) + { + this.func = func; + } + + public int apply(String param) + { + return this.func.applyAsInt(param); + } + + @Override + public final Class getOutputType() + { + return int.class; + } +} diff --git a/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/LongResolver.java b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/LongResolver.java new file mode 100644 index 0000000..3ba641b --- /dev/null +++ b/plutocommandparser/src/main/java/cz/tefek/pluto/command/resolver/primitive/LongResolver.java @@ -0,0 +1,26 @@ +package cz.tefek.pluto.command.resolver.primitive; + +import java.util.function.ToLongFunction; + +import cz.tefek.pluto.command.resolver.AbstractResolver; + +public class LongResolver extends AbstractResolver +{ + protected ToLongFunction func; + + public LongResolver(ToLongFunction func) + { + this.func = func; + } + + public long apply(String param) + { + return this.func.applyAsLong(param); + } + + @Override + public final Class getOutputType() + { + return long.class; + } +} diff --git a/plutocommandparser/src/test/java/cz/tefek/pluto/command/resolver/BasicIntResolverTest.java b/plutocommandparser/src/test/java/cz/tefek/pluto/command/resolver/BasicIntResolverTest.java new file mode 100644 index 0000000..e54212a --- /dev/null +++ b/plutocommandparser/src/test/java/cz/tefek/pluto/command/resolver/BasicIntResolverTest.java @@ -0,0 +1,41 @@ +package cz.tefek.pluto.command.resolver; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import cz.tefek.pluto.command.resolver.primitive.BasicIntResolver; + +class BasicIntResolverTest +{ + @Test + void parse() + { + var resolver = new BasicIntResolver(); + + Assertions.assertEquals(5, resolver.apply("5")); + + Assertions.assertEquals(-50, resolver.apply("-50")); + + Assertions.assertEquals(155, resolver.apply("155")); + + Assertions.assertEquals(0, resolver.apply("0")); + + Assertions.assertEquals(-0, resolver.apply("-0")); + } + + @Test + void exceptions() + { + var resolver = new BasicIntResolver(); + + // No foreign characters + Assertions.assertThrows(NumberFormatException.class, () -> resolver.apply("abc")); + + // No floats + Assertions.assertThrows(NumberFormatException.class, () -> resolver.apply("12.5")); + + // No empty strings + Assertions.assertThrows(NumberFormatException.class, () -> resolver.apply("")); + + } +} diff --git a/settings.gradle b/settings.gradle index da0336b..84a93b6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,5 @@ include 'plutolib', + 'plutocommandparser', 'plutostatic', 'plutotexturing', 'plutomesher',