Create MessageParser, use in Reader
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
package com.shr4pnel.ferretirc.net
|
||||
|
||||
import com.shr4pnel.ferretirc.net.messages.ServerMessage
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import io.ktor.util.toUpperCasePreservingASCIIRules
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
|
||||
class MessageParser(val incoming: Channel<String>) {
|
||||
private val logger = KotlinLogging.logger("MessageParser")
|
||||
val incomingParsedMessages = Channel<ServerMessage>()
|
||||
|
||||
private companion object MessageBuilder {
|
||||
sealed interface Command {
|
||||
fun toServerMessage(): ServerMessage
|
||||
|
||||
class NamedCommand(val name: String, val parameters: List<String>) : Command {
|
||||
override fun toServerMessage(): ServerMessage {
|
||||
return when (name.toUpperCasePreservingASCIIRules()) {
|
||||
// PONG :PREFIX COMMAND HOSTNAME :TOKEN
|
||||
"PONG" -> {
|
||||
val tokenIndex = parameters.indexOfLast { it.contains(":") }
|
||||
if (tokenIndex != -1) ServerMessage.Pong(
|
||||
parameters.subList(tokenIndex, parameters.size).joinToString(" ").removePrefix(":")
|
||||
)
|
||||
else ServerMessage.Pong()
|
||||
}
|
||||
|
||||
else -> ServerMessage.UNIMPLEMENTED("$name ${parameters.joinToString(" ")}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NumericCommand(val number: Int, val parameters: List<String>?) : Command {
|
||||
override fun toServerMessage(): ServerMessage {
|
||||
return ServerMessage.UNIMPLEMENTED("$number ${parameters?.joinToString(" ")}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var tags: String? = null
|
||||
private var prefix: String? = null
|
||||
private lateinit var command: Command
|
||||
|
||||
/**
|
||||
* Messages have this format, as rough ABNF:
|
||||
*
|
||||
* message ::= ['@' <tags> SPACE] [':' <source> SPACE] <command> <parameters> <crlf>
|
||||
* SPACE ::= %x20 *( %x20 ) ; space character(s)
|
||||
* crlf ::= %x0D %x0A ; "carriage return" "linefeed"
|
||||
*
|
||||
* The specific parts of an IRC message are:
|
||||
*
|
||||
* tags: Optional metadata on a message, starting with ('@', 0x40).
|
||||
*
|
||||
* source: Optional note of where the message came from, starting with (':', 0x3A).
|
||||
*
|
||||
* command: The specific command this message represents.
|
||||
*
|
||||
* parameters: If it exists, data relevant to this specific command.
|
||||
*
|
||||
* ignoring tags for now ;-;.. still making space for them
|
||||
*/
|
||||
fun build(commandList: List<String>): ServerMessage {
|
||||
val tokens = commandList.toMutableList()
|
||||
tags = if (tokens.first().startsWith("@")) tokens.removeFirst() else null
|
||||
prefix = if (tokens.first().startsWith(":")) tokens.removeFirst() else null
|
||||
val commandStr = tokens.removeFirst()
|
||||
command = if (commandStr.toIntOrNull() != null) Command.NumericCommand(commandStr.toInt(), tokens)
|
||||
else Command.NamedCommand(commandStr, tokens)
|
||||
|
||||
return command.toServerMessage()
|
||||
}
|
||||
|
||||
fun build(command: String) = build(command.split(" "))
|
||||
}
|
||||
|
||||
suspend fun start() {
|
||||
for (msg in incoming) {
|
||||
logger.debug { msg }
|
||||
incomingParsedMessages.send(build(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user