cli: enum parser

This commit is contained in:
Bixilon 2022-05-18 15:47:14 +02:00
parent 3ec82ad51f
commit 6e8fdb78fe
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
9 changed files with 187 additions and 14 deletions

View File

@ -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)
}
}

View File

@ -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) {

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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)

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<E : Enum<*>>(
val values: ValuesEnum<E>,
) : ArgumentParser<E> {
override val examples: List<E> = values.VALUES.toList()
private val suggestion = ArraySuggestion(examples)
override val placeholder = ChatComponent.of("<enum>")
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<E> {
val text = reader.readResult { reader.readString() }
if (text.result == null) {
return examples
}
return suggestion.suggest(text.result) ?: throw NoSuggestionError(reader, text)
}
companion object : ArgumentParserFactory<EnumParser<*>> {
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!")
}
}

View File

@ -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))
}

View File

@ -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<HelpCommands> {
override val VALUES: Array<HelpCommands> = values()
override val NAME_MAP: Map<String, HelpCommands> = EnumUtil.getEnumValues(VALUES)
}
}
}

View File

@ -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<BooleanParseError> { (BooleanParser.parse(reader)) }
}
@Test
fun testEmpty() {
val reader = CommandReader("")
assertThrows(BooleanParseError::class.java) { (BooleanParser.parse(reader)) }
assertThrows<BooleanParseError> { (BooleanParser.parse(reader)) }
}
@Test
fun testTrash() {
val reader = CommandReader("this is trash")
assertThrows(BooleanParseError::class.java) { (BooleanParser.parse(reader)) }
assertThrows<BooleanParseError> { (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<NoSuggestionError> { BooleanParser.getSuggestions(reader).size }
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<EnumParseError> { parser.parse(reader) }
}
enum class Tests {
FIRST,
SECOND,
;
companion object : ValuesEnum<Tests> {
override val VALUES: Array<Tests> = values()
override val NAME_MAP: Map<String, Tests> = EnumUtil.getEnumValues(VALUES)
}
}
}

View File

@ -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<OutOfBoundsError> { reader.unsafeRead() }
}
@Test