From 6e8fdb78fe5cde8e45a661d5211ff972a329dde5 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Wed, 18 May 2022 15:47:14 +0200 Subject: [PATCH] cli: enum parser --- .../minosoft/commands/nodes/ArgumentNode.kt | 9 +++ .../minosoft/commands/nodes/ExecutableNode.kt | 2 +- .../parser/minosoft/enums/EnumParseError.kt | 23 +++++++ .../parser/minosoft/enums/EnumParser.kt | 58 +++++++++++++++++ .../minosoft/commands/stack/CommandStack.kt | 2 +- .../minosoft/terminal/commands/HelpCommand.kt | 26 ++++++-- .../brigadier/bool/BooleanParserTest.kt | 13 ++-- .../parser/minosoft/enums/EnumParserTest.kt | 64 +++++++++++++++++++ .../commands/util/CommandReaderTest.kt | 4 +- 9 files changed, 187 insertions(+), 14 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParseError.kt create mode 100644 src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParser.kt create mode 100644 src/test/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParserTest.kt diff --git a/src/main/java/de/bixilon/minosoft/commands/nodes/ArgumentNode.kt b/src/main/java/de/bixilon/minosoft/commands/nodes/ArgumentNode.kt index ad7e58772..bd84743dc 100644 --- a/src/main/java/de/bixilon/minosoft/commands/nodes/ArgumentNode.kt +++ b/src/main/java/de/bixilon/minosoft/commands/nodes/ArgumentNode.kt @@ -15,7 +15,9 @@ package de.bixilon.minosoft.commands.nodes import de.bixilon.minosoft.commands.parser.ArgumentParser import de.bixilon.minosoft.commands.stack.CommandExecutor +import de.bixilon.minosoft.commands.stack.CommandStack import de.bixilon.minosoft.commands.suggestion.types.SuggestionType +import de.bixilon.minosoft.commands.util.CommandReader class ArgumentNode : ExecutableNode { private val parser: ArgumentParser<*> @@ -40,4 +42,11 @@ class ArgumentNode : ExecutableNode { super.addChild(node) return this } + + override fun execute(reader: CommandReader, stack: CommandStack) { + reader.skipWhitespaces() + val parsed = parser.parse(reader) + stack.push(name, parsed) + super.execute(reader, stack) + } } diff --git a/src/main/java/de/bixilon/minosoft/commands/nodes/ExecutableNode.kt b/src/main/java/de/bixilon/minosoft/commands/nodes/ExecutableNode.kt index 4167e3c0f..7c7501894 100644 --- a/src/main/java/de/bixilon/minosoft/commands/nodes/ExecutableNode.kt +++ b/src/main/java/de/bixilon/minosoft/commands/nodes/ExecutableNode.kt @@ -29,7 +29,7 @@ abstract class ExecutableNode( redirect: CommandNode? = null, ) : CommandNode(executable, redirect) { - private fun execute(stack: CommandStack) { + protected fun execute(stack: CommandStack) { try { executor?.invoke(stack) } catch (exception: Throwable) { diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParseError.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParseError.kt new file mode 100644 index 000000000..0a04829a4 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParseError.kt @@ -0,0 +1,23 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.parser.minosoft.enums + +import de.bixilon.minosoft.commands.errors.parser.ParserError +import de.bixilon.minosoft.commands.util.CommandReader +import de.bixilon.minosoft.commands.util.ReadResult + +class EnumParseError( + reader: CommandReader, + result: ReadResult<*>, +) : ParserError(reader, result) diff --git a/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParser.kt b/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParser.kt new file mode 100644 index 000000000..c8b169f10 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParser.kt @@ -0,0 +1,58 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.parser.minosoft.enums + +import de.bixilon.kutil.enums.ValuesEnum +import de.bixilon.minosoft.commands.errors.suggestion.NoSuggestionError +import de.bixilon.minosoft.commands.parser.ArgumentParser +import de.bixilon.minosoft.commands.parser.factory.ArgumentParserFactory +import de.bixilon.minosoft.commands.suggestion.ArraySuggestion +import de.bixilon.minosoft.commands.util.CommandReader +import de.bixilon.minosoft.data.registries.ResourceLocation +import de.bixilon.minosoft.data.text.ChatComponent +import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection +import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer +import de.bixilon.minosoft.util.KUtil.toResourceLocation + +class EnumParser>( + val values: ValuesEnum, +) : ArgumentParser { + override val examples: List = values.VALUES.toList() + private val suggestion = ArraySuggestion(examples) + override val placeholder = ChatComponent.of("") + + override fun parse(reader: CommandReader): E { + reader.readResult { reader.readEnum() }.let { return it.result ?: throw EnumParseError(reader, it) } + } + + fun CommandReader.readEnum(): E? { + return values.getOrNull(readString()?.lowercase()) // ToDo: Allow ordinals + } + + override fun getSuggestions(reader: CommandReader): List { + val text = reader.readResult { reader.readString() } + if (text.result == null) { + return examples + } + return suggestion.suggest(text.result) ?: throw NoSuggestionError(reader, text) + } + + companion object : ArgumentParserFactory> { + override val RESOURCE_LOCATION: ResourceLocation = "minosoft:enum".toResourceLocation() + + override fun build(connection: PlayConnection?) = TODO("Can not construct enum parser yet!") + + override fun read(buffer: PlayInByteBuffer) = TODO("Can not construct enum parser yet!") + } +} diff --git a/src/main/java/de/bixilon/minosoft/commands/stack/CommandStack.kt b/src/main/java/de/bixilon/minosoft/commands/stack/CommandStack.kt index 3d6b11ec0..ae9b86ac5 100644 --- a/src/main/java/de/bixilon/minosoft/commands/stack/CommandStack.kt +++ b/src/main/java/de/bixilon/minosoft/commands/stack/CommandStack.kt @@ -33,7 +33,7 @@ class CommandStack { stack.removeAll { index++ >= size } } - fun push(name: String, data: Any) { + fun push(name: String, data: Any?) { stack.add(StackEntry(name, data)) } diff --git a/src/main/java/de/bixilon/minosoft/terminal/commands/HelpCommand.kt b/src/main/java/de/bixilon/minosoft/terminal/commands/HelpCommand.kt index 02524c95e..071393163 100644 --- a/src/main/java/de/bixilon/minosoft/terminal/commands/HelpCommand.kt +++ b/src/main/java/de/bixilon/minosoft/terminal/commands/HelpCommand.kt @@ -13,19 +13,35 @@ package de.bixilon.minosoft.terminal.commands +import de.bixilon.kutil.enums.EnumUtil +import de.bixilon.kutil.enums.ValuesEnum +import de.bixilon.minosoft.commands.nodes.ArgumentNode import de.bixilon.minosoft.commands.nodes.LiteralNode +import de.bixilon.minosoft.commands.parser.minosoft.enums.EnumParser object HelpCommand : Command { override fun build(): LiteralNode { - return LiteralNode("help", setOf("?"), onlyDirectExecution = false, executor = { - printHelp(it["general"]) - }) - .addChild(LiteralNode("general", executable = true)) + return LiteralNode("help", setOf("?"), executor = { printHelp() }) + .addChild(ArgumentNode("subcommand", parser = EnumParser(HelpCommands), executor = { printHelp(it["subcommand"]!!) })) } - fun printHelp(subcommand: String?) { + fun printHelp() { + println("-------------- Minosoft help --------------") + } + + fun printHelp(subcommand: HelpCommands) { println("-------------- Minosoft help --------------") println("Subcommand: $subcommand") } + + enum class HelpCommands { + GENERAL, + ; + + companion object : ValuesEnum { + override val VALUES: Array = values() + override val NAME_MAP: Map = EnumUtil.getEnumValues(VALUES) + } + } } diff --git a/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/bool/BooleanParserTest.kt b/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/bool/BooleanParserTest.kt index 2fd105107..942e090f1 100644 --- a/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/bool/BooleanParserTest.kt +++ b/src/test/java/de/bixilon/minosoft/commands/parser/brigadier/bool/BooleanParserTest.kt @@ -15,8 +15,11 @@ package de.bixilon.minosoft.commands.parser.brigadier.bool import de.bixilon.minosoft.commands.errors.suggestion.NoSuggestionError import de.bixilon.minosoft.commands.util.CommandReader -import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue internal class BooleanParserTest { @@ -35,19 +38,19 @@ internal class BooleanParserTest { @Test fun testCaseSensitivity() { val reader = CommandReader("True") - assertThrows(BooleanParseError::class.java) { (BooleanParser.parse(reader)) } + assertThrows { (BooleanParser.parse(reader)) } } @Test fun testEmpty() { val reader = CommandReader("") - assertThrows(BooleanParseError::class.java) { (BooleanParser.parse(reader)) } + assertThrows { (BooleanParser.parse(reader)) } } @Test fun testTrash() { val reader = CommandReader("this is trash") - assertThrows(BooleanParseError::class.java) { (BooleanParser.parse(reader)) } + assertThrows { (BooleanParser.parse(reader)) } } @Test @@ -83,6 +86,6 @@ internal class BooleanParserTest { @Test fun testNoSuggestion() { val reader = CommandReader("a") - assertThrows(NoSuggestionError::class.java) { BooleanParser.getSuggestions(reader).size } + assertThrows { BooleanParser.getSuggestions(reader).size } } } diff --git a/src/test/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParserTest.kt b/src/test/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParserTest.kt new file mode 100644 index 000000000..2f651d40e --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/commands/parser/minosoft/enums/EnumParserTest.kt @@ -0,0 +1,64 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.commands.parser.minosoft.enums + +import de.bixilon.kutil.enums.EnumUtil +import de.bixilon.kutil.enums.ValuesEnum +import de.bixilon.minosoft.commands.util.CommandReader +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import kotlin.test.assertEquals + +internal class EnumParserTest { + + + @Test + fun checkFirst() { + val reader = CommandReader("FIRST") + val parser = EnumParser(Tests) + assertEquals(parser.parse(reader), Tests.FIRST) + } + + @Test + fun checkSecond() { + val reader = CommandReader("SECOND") + val parser = EnumParser(Tests) + assertEquals(parser.parse(reader), Tests.SECOND) + } + + @Test + fun checkCaseInsensitivity() { + val reader = CommandReader("sEcOnD") + val parser = EnumParser(Tests) + assertEquals(parser.parse(reader), Tests.SECOND) + } + + @Test + fun checkInvalid() { + val reader = CommandReader("invalid") + val parser = EnumParser(Tests) + assertThrows { parser.parse(reader) } + } + + enum class Tests { + FIRST, + SECOND, + ; + + companion object : ValuesEnum { + override val VALUES: Array = values() + override val NAME_MAP: Map = EnumUtil.getEnumValues(VALUES) + } + } +} diff --git a/src/test/java/de/bixilon/minosoft/commands/util/CommandReaderTest.kt b/src/test/java/de/bixilon/minosoft/commands/util/CommandReaderTest.kt index fac33b0ca..d3d442061 100644 --- a/src/test/java/de/bixilon/minosoft/commands/util/CommandReaderTest.kt +++ b/src/test/java/de/bixilon/minosoft/commands/util/CommandReaderTest.kt @@ -15,8 +15,8 @@ package de.bixilon.minosoft.commands.util import de.bixilon.minosoft.commands.errors.reader.OutOfBoundsError import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertThrows import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows internal class CommandReaderTest { @Test @@ -52,7 +52,7 @@ internal class CommandReaderTest { assertEquals(reader.read(), 'e'.code) assertEquals(reader.read(), 's'.code) assertEquals(reader.read(), 't'.code) - assertThrows(OutOfBoundsError::class.java) { reader.unsafeRead() } + assertThrows { reader.unsafeRead() } } @Test