refactor: Turn argument from mutable list to a custom string parser
class
This commit is contained in:
parent
b46dc2f5eb
commit
e01dafa69d
11 changed files with 116 additions and 36 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,6 +4,7 @@ build/
|
||||||
!**/src/main/**/build/
|
!**/src/main/**/build/
|
||||||
!**/src/test/**/build/
|
!**/src/test/**/build/
|
||||||
kls_database.db
|
kls_database.db
|
||||||
|
/notebook.ipynb
|
||||||
|
|
||||||
### IntelliJ IDEA ###
|
### IntelliJ IDEA ###
|
||||||
.idea/
|
.idea/
|
||||||
|
|
|
@ -16,6 +16,6 @@ suspend fun main() {
|
||||||
|
|
||||||
modules(generalModule)
|
modules(generalModule)
|
||||||
|
|
||||||
koinModules(appModule)
|
koin(appModule)
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import io.github.null2264.tsukumogami.core.ext.loadModule
|
||||||
import io.github.null2264.tsukumogami.core.koin.TsukumogamiKoinContext
|
import io.github.null2264.tsukumogami.core.koin.TsukumogamiKoinContext
|
||||||
import io.github.null2264.tsukumogami.core.module.BotModule
|
import io.github.null2264.tsukumogami.core.module.BotModule
|
||||||
import kotlin.reflect.KFunction
|
import kotlin.reflect.KFunction
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import org.koin.core.module.Module as KoinModule
|
import org.koin.core.module.Module as KoinModule
|
||||||
import org.koin.dsl.bind
|
import org.koin.dsl.bind
|
||||||
|
|
||||||
|
@ -36,11 +35,11 @@ class BotBuilder internal constructor(
|
||||||
prefixes.forEach(bot::addPrefix)
|
prefixes.forEach(bot::addPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun earlyKoinModules(vararg modules: KoinModule) {
|
fun earlyKoin(vararg modules: KoinModule) {
|
||||||
earlyKoinModules.addAll(modules)
|
earlyKoinModules.addAll(modules)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun koinModules(vararg modules: KoinModule) {
|
fun koin(vararg modules: KoinModule) {
|
||||||
koinModules.addAll(modules)
|
koinModules.addAll(modules)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import dev.kord.core.entity.Message
|
||||||
import dev.kord.rest.builder.message.AllowedMentionsBuilder
|
import dev.kord.rest.builder.message.AllowedMentionsBuilder
|
||||||
import dev.kord.rest.builder.message.allowedMentions
|
import dev.kord.rest.builder.message.allowedMentions
|
||||||
import io.github.null2264.tsukumogami.core.commands.Command
|
import io.github.null2264.tsukumogami.core.commands.Command
|
||||||
|
import io.github.null2264.tsukumogami.core.commands.StringParser
|
||||||
import io.github.null2264.tsukumogami.core.koin.TsukumogamiKoinComponent
|
import io.github.null2264.tsukumogami.core.koin.TsukumogamiKoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
|
@ -35,6 +36,16 @@ class Context(
|
||||||
*/
|
*/
|
||||||
var command: Command? = null
|
var command: Command? = null
|
||||||
|
|
||||||
|
val parser: StringParser by lazy {
|
||||||
|
StringParser(
|
||||||
|
if (prefix != null && invokedWith != null) {
|
||||||
|
message.content
|
||||||
|
.substringAfter("$prefix$invokedWith")
|
||||||
|
.trim()
|
||||||
|
} else ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun send(content: String) = message.channel.createMessage(content)
|
suspend fun send(content: String) = message.channel.createMessage(content)
|
||||||
|
|
||||||
suspend fun reply(content: String, mentionsAuthor: Boolean = false) = message.channel.createMessage {
|
suspend fun reply(content: String, mentionsAuthor: Boolean = false) = message.channel.createMessage {
|
||||||
|
@ -47,15 +58,4 @@ class Context(
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun typing() = message.channel.type()
|
suspend fun typing() = message.channel.type()
|
||||||
|
|
||||||
fun parseArguments(): MutableList<String>? =
|
|
||||||
if (prefix != null && invokedWith != null) {
|
|
||||||
message.content
|
|
||||||
.substringAfter("$prefix$invokedWith")
|
|
||||||
.trim()
|
|
||||||
.split(' ')
|
|
||||||
.toMutableList()
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,9 @@ abstract class Arguments {
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun parse(context: Context) {
|
suspend fun parse(context: Context) {
|
||||||
val values = context.parseArguments()?.toMutableList() ?: return
|
|
||||||
|
|
||||||
run {
|
run {
|
||||||
args.forEach { arg ->
|
args.forEach { arg ->
|
||||||
arg.converter.consume(context, values)
|
arg.converter.consume(context, context.parser)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package io.github.null2264.tsukumogami.core.commands
|
||||||
|
|
||||||
|
class StringParser(private val string: String) {
|
||||||
|
private var currentIndex = 0
|
||||||
|
private var lastPeekIndex = 0
|
||||||
|
|
||||||
|
fun hasNext() = currentIndex < string.length
|
||||||
|
|
||||||
|
private fun parse(peek: Boolean, naive: Boolean = false): String {
|
||||||
|
check(hasNext())
|
||||||
|
|
||||||
|
val currentToken = StringBuilder()
|
||||||
|
var inQuotes = false
|
||||||
|
val currentIndexInitial = currentIndex
|
||||||
|
|
||||||
|
while (hasNext()) {
|
||||||
|
val char = string[currentIndex]
|
||||||
|
currentIndex++
|
||||||
|
|
||||||
|
when (char) {
|
||||||
|
'"' -> {
|
||||||
|
if (naive) {
|
||||||
|
currentToken.append(char)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inQuotes) {
|
||||||
|
// Closing quote: add the accumulated token
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
if (currentToken.isNotEmpty()) {
|
||||||
|
// Did not start with quote, probably something like: hello"world"
|
||||||
|
currentToken.append(char)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Opening quote:
|
||||||
|
inQuotes = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
' ' -> {
|
||||||
|
if (inQuotes) {
|
||||||
|
// Space inside quotes: append it
|
||||||
|
currentToken.append(char)
|
||||||
|
} else {
|
||||||
|
// Space outside quotes: separator
|
||||||
|
if (currentToken.isNotEmpty()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
// Any other character: append it
|
||||||
|
currentToken.append(char)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//check(!inQuotes)
|
||||||
|
if (peek) {
|
||||||
|
lastPeekIndex = currentIndex
|
||||||
|
currentIndex = currentIndexInitial
|
||||||
|
}
|
||||||
|
return currentToken.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseNext(naive: Boolean = false): String = parse(false, naive)
|
||||||
|
fun peekNext(naive: Boolean = false): String = parse(true, naive)
|
||||||
|
fun next() {
|
||||||
|
currentIndex = lastPeekIndex
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package io.github.null2264.tsukumogami.core.commands.converters
|
||||||
import io.github.null2264.tsukumogami.core.Context
|
import io.github.null2264.tsukumogami.core.Context
|
||||||
import io.github.null2264.tsukumogami.core.commands.Argument
|
import io.github.null2264.tsukumogami.core.commands.Argument
|
||||||
import io.github.null2264.tsukumogami.core.commands.Arguments
|
import io.github.null2264.tsukumogami.core.commands.Arguments
|
||||||
|
import io.github.null2264.tsukumogami.core.commands.StringParser
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
abstract class Converter<OutputType: Any?> {
|
abstract class Converter<OutputType: Any?> {
|
||||||
|
@ -13,7 +14,7 @@ abstract class Converter<OutputType: Any?> {
|
||||||
|
|
||||||
abstract var parsed: OutputType
|
abstract var parsed: OutputType
|
||||||
|
|
||||||
abstract suspend fun consume(context: Context, inputs: MutableList<String>): Boolean
|
abstract suspend fun consume(context: Context, parser: StringParser): Boolean
|
||||||
|
|
||||||
operator fun getValue(thisRef: Arguments, property: KProperty<*>): OutputType {
|
operator fun getValue(thisRef: Arguments, property: KProperty<*>): OutputType {
|
||||||
return this.parsed
|
return this.parsed
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
package io.github.null2264.tsukumogami.core.commands.converters.impl
|
package io.github.null2264.tsukumogami.core.commands.converters.impl
|
||||||
|
|
||||||
import io.github.null2264.tsukumogami.core.Context
|
import io.github.null2264.tsukumogami.core.Context
|
||||||
|
import io.github.null2264.tsukumogami.core.commands.StringParser
|
||||||
import io.github.null2264.tsukumogami.core.commands.converters.Converter
|
import io.github.null2264.tsukumogami.core.commands.converters.Converter
|
||||||
|
|
||||||
class StringConverter : Converter<String>() {
|
class StringConverter : Converter<String>() {
|
||||||
|
|
||||||
override var parsed: String = ""
|
override var parsed: String = ""
|
||||||
|
|
||||||
override suspend fun consume(context: Context, inputs: MutableList<String>): Boolean {
|
override suspend fun consume(context: Context, parser: StringParser): Boolean {
|
||||||
this.parsed = if (isGreedy && inputs.size > 1) run {
|
if (!parser.hasNext()) return this.parsed.isEmpty()
|
||||||
val limit = inputs.size
|
|
||||||
|
this.parsed = if (isGreedy) run {
|
||||||
var buffer = ""
|
var buffer = ""
|
||||||
var count = 0
|
var count = 0
|
||||||
for (i in (1..limit)) {
|
while (parser.hasNext()) {
|
||||||
if (++count > 1) buffer += " "
|
if (++count > 1) buffer += " "
|
||||||
if (count <= limit) {
|
buffer += parser.parseNext(true)
|
||||||
buffer += inputs.removeFirstOrNull() ?: ""
|
|
||||||
} else break
|
|
||||||
}
|
}
|
||||||
buffer
|
buffer
|
||||||
} else inputs.removeFirstOrNull().orEmpty()
|
} else parser.parseNext()
|
||||||
|
|
||||||
return this.parsed.isNotEmpty()
|
return this.parsed.isNotEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package io.github.null2264.tsukumogami.core.commands.converters.impl
|
package io.github.null2264.tsukumogami.core.commands.converters.impl
|
||||||
|
|
||||||
import io.github.null2264.tsukumogami.core.Context
|
import io.github.null2264.tsukumogami.core.Context
|
||||||
|
import io.github.null2264.tsukumogami.core.commands.StringParser
|
||||||
import io.github.null2264.tsukumogami.core.commands.converters.Converter
|
import io.github.null2264.tsukumogami.core.commands.converters.Converter
|
||||||
import io.github.null2264.tsukumogami.core.exceptions.CommandException
|
import io.github.null2264.tsukumogami.core.exceptions.CommandException
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
@ -13,13 +14,15 @@ class URIConverter : Converter<URI>() {
|
||||||
|
|
||||||
override lateinit var parsed: URI
|
override lateinit var parsed: URI
|
||||||
|
|
||||||
override suspend fun consume(context: Context, inputs: MutableList<String>): Boolean {
|
override suspend fun consume(context: Context, parser: StringParser): Boolean {
|
||||||
val input = inputs.getOrNull(0) ?: return false
|
if (!parser.hasNext()) return false
|
||||||
|
val input = parser.peekNext()
|
||||||
this.parsed = try {
|
this.parsed = try {
|
||||||
URI(input)
|
URI(input)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw CommandException("Failed to parse URI", e)
|
throw CommandException("Failed to parse URI", e)
|
||||||
}
|
}
|
||||||
|
parser.next()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package io.github.null2264.tsukumogami.core.commands.converters.impl
|
package io.github.null2264.tsukumogami.core.commands.converters.impl
|
||||||
|
|
||||||
import io.github.null2264.tsukumogami.core.Context
|
import io.github.null2264.tsukumogami.core.Context
|
||||||
|
import io.github.null2264.tsukumogami.core.commands.StringParser
|
||||||
import io.github.null2264.tsukumogami.core.commands.converters.Converter
|
import io.github.null2264.tsukumogami.core.commands.converters.Converter
|
||||||
import io.github.null2264.tsukumogami.core.exceptions.CommandException
|
import io.github.null2264.tsukumogami.core.exceptions.CommandException
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
@ -14,13 +15,15 @@ class URLConverter : Converter<URL>() {
|
||||||
|
|
||||||
override lateinit var parsed: URL
|
override lateinit var parsed: URL
|
||||||
|
|
||||||
override suspend fun consume(context: Context, inputs: MutableList<String>): Boolean {
|
override suspend fun consume(context: Context, parser: StringParser): Boolean {
|
||||||
val input = inputs.getOrNull(0) ?: return false
|
if (!parser.hasNext()) return false
|
||||||
|
val input = parser.peekNext()
|
||||||
this.parsed = try {
|
this.parsed = try {
|
||||||
URL(input)
|
URL(input)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw CommandException("Failed to parse URL", e)
|
throw CommandException("Failed to parse URL", e)
|
||||||
}
|
}
|
||||||
|
parser.next()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package io.github.null2264.tsukumogami.core.commands.converters.impl
|
||||||
import dev.kord.common.entity.Snowflake
|
import dev.kord.common.entity.Snowflake
|
||||||
import dev.kord.core.entity.User
|
import dev.kord.core.entity.User
|
||||||
import io.github.null2264.tsukumogami.core.Context
|
import io.github.null2264.tsukumogami.core.Context
|
||||||
|
import io.github.null2264.tsukumogami.core.commands.StringParser
|
||||||
import io.github.null2264.tsukumogami.core.commands.converters.Converter
|
import io.github.null2264.tsukumogami.core.commands.converters.Converter
|
||||||
import io.github.null2264.tsukumogami.core.exceptions.CommandException
|
import io.github.null2264.tsukumogami.core.exceptions.CommandException
|
||||||
import io.github.null2264.tsukumogami.core.ext.users
|
import io.github.null2264.tsukumogami.core.ext.users
|
||||||
|
@ -16,27 +17,28 @@ class UserConverter : Converter<User>() {
|
||||||
|
|
||||||
override lateinit var parsed: User
|
override lateinit var parsed: User
|
||||||
|
|
||||||
override suspend fun consume(context: Context, inputs: MutableList<String>): Boolean {
|
override suspend fun consume(context: Context, parser: StringParser): Boolean {
|
||||||
val input = inputs.getOrNull(0) ?: throw CommandException("User ID is null")
|
if (!parser.hasNext()) throw CommandException("User is not specified")
|
||||||
|
val input = parser.peekNext()
|
||||||
|
|
||||||
if (input.equals("me", true)) {
|
if (input.equals("me", true)) {
|
||||||
val user = context.author
|
val user = context.author
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
this.parsed = user
|
this.parsed = user
|
||||||
inputs.removeAt(0)
|
parser.next()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (input.equals("you", true)) {
|
if (input.equals("you", true)) {
|
||||||
this.parsed = context.bot.client.getSelf()
|
this.parsed = context.bot.client.getSelf()
|
||||||
inputs.removeAt(0)
|
parser.next()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
this.parsed = context.findUser(input) ?:
|
this.parsed = context.findUser(input) ?:
|
||||||
throw CommandException("User not found")
|
throw CommandException("User not found")
|
||||||
|
|
||||||
inputs.removeAt(0)
|
parser.next()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue