fuck it
This commit is contained in:
@@ -23,7 +23,11 @@ shadowJar {
|
||||
dependencies {
|
||||
testImplementation 'org.jetbrains.kotlin:kotlin-test'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0'
|
||||
implementation 'io.github.microutils:kotlin-logging-jvm:3.0.5'
|
||||
implementation "org.jline:jline:3.30.6"
|
||||
implementation "io.github.oshai:kotlin-logging-jvm:7.0.3"
|
||||
implementation "org.slf4j:slf4j-api:2.0.13"
|
||||
implementation 'ch.qos.logback:logback-classic:1.5.13'
|
||||
implementation project(':common')
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import ch.qos.logback.core.net.server.Client
|
||||
import commands.ExecuteScriptCommand
|
||||
import commands.ExitCommand
|
||||
import commands.HelpCommand
|
||||
import commands.SyncCommand
|
||||
import commands.UndefinedCommand
|
||||
import network.NetworkManager
|
||||
import input.HumanBeingBuilder
|
||||
import input.IOManager
|
||||
import network.Request
|
||||
import runner.CommandValidator
|
||||
import runner.CommandInvoker
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val host = if (args.isNotEmpty()) args[0] else "localhost"
|
||||
@@ -10,7 +15,7 @@ fun main(args: Array<String>) {
|
||||
|
||||
val io = IOManager() // Name by Boris Bosenko
|
||||
val client = NetworkManager(host, port)
|
||||
val validator = CommandValidator()
|
||||
val invoker = CommandInvoker(io)
|
||||
|
||||
// Shutdown hook
|
||||
Runtime.getRuntime().addShutdownHook(Thread {
|
||||
@@ -23,70 +28,32 @@ fun main(args: Array<String>) {
|
||||
|════════════════════════════════════════════
|
||||
""".trimMargin())
|
||||
|
||||
runClientRepl(io, client, validator)
|
||||
registerBasicCommands(invoker, client, io)
|
||||
runClientRepl(io, invoker)
|
||||
}
|
||||
|
||||
fun runClientRepl(io: IOManager, client: NetworkManager, validator: CommandValidator) {
|
||||
fun registerBasicCommands(invoker: CommandInvoker,client: NetworkManager, io: IOManager) {
|
||||
listOf(
|
||||
HelpCommand(invoker),
|
||||
ExecuteScriptCommand(io),
|
||||
SyncCommand(invoker, client)
|
||||
).forEach { invoker.register(it)}
|
||||
}
|
||||
|
||||
val initialRequest = Request(
|
||||
commandName = "getCommandsCommand",
|
||||
humanBeing = null
|
||||
)
|
||||
fun runClientRepl(io: IOManager, invoker : CommandInvoker) {
|
||||
// sync commands w server
|
||||
invoker.execute("sync")
|
||||
|
||||
if (client.sendRequest(initialRequest) == null) {
|
||||
io.print("[!] Сервер недоступен. Попробуйте позже.")
|
||||
} else {
|
||||
io.print("|Введите 'help' для справки.")
|
||||
}
|
||||
var isRunning = true
|
||||
invoker.register(ExitCommand {isRunning = false})
|
||||
|
||||
while (true) {
|
||||
while (isRunning) {
|
||||
val line = io.readLine(" ☭ ") ?: break
|
||||
if (line.isBlank()) continue
|
||||
|
||||
val parts = line.trim().split(" ")
|
||||
val commandName = parts[0].lowercase()
|
||||
val args = parts.drop(1)
|
||||
|
||||
val (isValidCommand, message) = validator.validate(commandName, args)
|
||||
|
||||
if (!isValidCommand) {
|
||||
if (message != null) io.print(message)
|
||||
continue
|
||||
}
|
||||
|
||||
if (commandName == "exit") {
|
||||
io.print("До свидания!")
|
||||
break
|
||||
}
|
||||
|
||||
if (commandName == "execute_script") {
|
||||
io.addScriptScanner(args[0])
|
||||
continue
|
||||
}
|
||||
|
||||
val humanBeing = if (validator.isBuildsHumanBeing(commandName)) {
|
||||
try {
|
||||
HumanBeingBuilder(io).build()
|
||||
} catch (e: IllegalStateException) {
|
||||
io.print("[Ошибка] Ввод прерван: ${e.message}")
|
||||
continue
|
||||
}
|
||||
} else null
|
||||
|
||||
// Формируем запрос и отправляем
|
||||
val request = Request(
|
||||
commandName = commandName,
|
||||
args = args,
|
||||
humanBeing = humanBeing
|
||||
)
|
||||
|
||||
val response = client.sendRequest(request)
|
||||
|
||||
// Обрабатываем ответ
|
||||
if (response == null) {
|
||||
io.print("[!] Сервер недоступен. Попробуйте позже.")
|
||||
} else {
|
||||
io.print(response.message)
|
||||
}
|
||||
invoker.execute(line)
|
||||
}
|
||||
|
||||
// and here we exit that crazy circus of code and tears
|
||||
io.print("До свидания!")
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package commands
|
||||
|
||||
import models.HumanBeing
|
||||
|
||||
interface ClientCommand {
|
||||
val name: String
|
||||
val description: String
|
||||
val args: List<CommandArgType>
|
||||
val type: CommandType
|
||||
|
||||
fun execute(args: List<String>, humanBeing: HumanBeing? = null): String
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package commands
|
||||
|
||||
import input.IOManager
|
||||
import models.HumanBeing
|
||||
|
||||
class ExecuteScriptCommand(private val io: IOManager) : ClientCommand {
|
||||
override val name = "execute_script"
|
||||
override val description = "Выполняет скрипт из файла"
|
||||
override val args = listOf(CommandArgType.STRING)
|
||||
override val type = CommandType.SIMPLE
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
||||
io.addScriptScanner(args[0])
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package commands
|
||||
|
||||
import models.HumanBeing
|
||||
|
||||
class ExitCommand(private val onExit: () -> Unit) : ClientCommand {
|
||||
override val name = "exit"
|
||||
override val description = "завершить клиент"
|
||||
override val args: List<CommandArgType> = listOf(CommandArgType.NONE)
|
||||
override val type = CommandType.SIMPLE
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
||||
onExit()
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package commands
|
||||
|
||||
import models.HumanBeing
|
||||
import runner.CommandInvoker
|
||||
|
||||
class HelpCommand(private val invoker: CommandInvoker) : ClientCommand {
|
||||
override val name = "help"
|
||||
override val description = "вывести справку по доступным командам"
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
override val type = CommandType.SIMPLE
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String =
|
||||
invoker.getCommands().values
|
||||
.sortedBy { it.name }
|
||||
.joinToString("\n") { " ${it.name.padEnd(40)} — ${it.description}" }
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package commands
|
||||
|
||||
import input.IOManager
|
||||
import models.HumanBeing
|
||||
import network.NetworkManager
|
||||
import network.Request
|
||||
import runner.CommandInvoker
|
||||
|
||||
class SyncCommand (
|
||||
private val invoker: CommandInvoker,
|
||||
private val client: NetworkManager,
|
||||
) : ClientCommand {
|
||||
override val name = "sync"
|
||||
override val description = "получить команды с сервера"
|
||||
override val args = listOf(CommandArgType.NONE)
|
||||
override val type = CommandType.SIMPLE
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
||||
val initialRequest = Request(
|
||||
commandName = "sync",
|
||||
humanBeing = null
|
||||
)
|
||||
val res = client.sendRequest(initialRequest)
|
||||
|
||||
if (res == null) {
|
||||
return ("[!] Сервер недоступен. Попробуйте позже.")
|
||||
} else {
|
||||
res.commands?.forEach { fuckingCommand ->
|
||||
invoker.register(
|
||||
UndefinedCommand( // ser
|
||||
name = fuckingCommand.name,
|
||||
description = fuckingCommand.description,
|
||||
type = fuckingCommand.type,
|
||||
args = fuckingCommand.args,
|
||||
networkManager = client
|
||||
)
|
||||
)
|
||||
}
|
||||
return ("| Подключен. | Введите 'help' для справки.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package commands
|
||||
|
||||
import input.HumanBeingBuilder
|
||||
import models.HumanBeing
|
||||
import network.Request
|
||||
import network.NetworkManager
|
||||
|
||||
class UndefinedCommand(
|
||||
override var name: String,
|
||||
override var description: String,
|
||||
override var args: List<CommandArgType>,
|
||||
override var type: CommandType,
|
||||
private val networkManager: NetworkManager,
|
||||
): ClientCommand {
|
||||
|
||||
override fun execute(args: List<String>, humanBeing: HumanBeing?): String {
|
||||
val request = Request(
|
||||
commandName = this.name,
|
||||
args = args,
|
||||
humanBeing = humanBeing
|
||||
)
|
||||
val response = networkManager.sendRequest(request)
|
||||
return response?.message ?: "[!] Сервер не ответил"
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import models.WeaponType
|
||||
*
|
||||
* @property inputManager источник ввода
|
||||
*/
|
||||
|
||||
class HumanBeingBuilder(private val inputManager: IOManager) {
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,16 +6,13 @@ import org.jline.reader.LineReaderBuilder
|
||||
import org.jline.reader.UserInterruptException
|
||||
import org.jline.reader.impl.history.DefaultHistory
|
||||
import org.jline.terminal.TerminalBuilder
|
||||
import org.jline.reader.impl.completer.StringsCompleter
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.util.ArrayDeque
|
||||
import java.util.Scanner
|
||||
import runner.CommandValidator
|
||||
|
||||
class IOManager {
|
||||
val isInteractive: Boolean = true
|
||||
val commandValidator: CommandValidator = CommandValidator()
|
||||
|
||||
private val terminal = TerminalBuilder.builder()
|
||||
.system(true)
|
||||
@@ -26,7 +23,7 @@ class IOManager {
|
||||
private val lineReader: LineReader = LineReaderBuilder.builder()
|
||||
.terminal(terminal)
|
||||
.history(history)
|
||||
.completer(StringsCompleter(commandValidator.getKnownCommands()))
|
||||
// .completer(StringsCompleter(invoker.getCommandNames()))
|
||||
.option(LineReader.Option.HISTORY_IGNORE_DUPS, true)
|
||||
.option(LineReader.Option.DISABLE_EVENT_EXPANSION, true)
|
||||
.build()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package network
|
||||
|
||||
import kotlinx.serialization.SerializationException
|
||||
import lab6.prog.network.AppJson
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.PortUnreachableException
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package runner
|
||||
|
||||
import commands.ClientCommand
|
||||
import commands.CommandType
|
||||
import input.HumanBeingBuilder
|
||||
import input.IOManager
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
class CommandInvoker(private val io: IOManager) {
|
||||
private val commands = mutableMapOf<String, ClientCommand>()
|
||||
private val history = ArrayDeque<String>(12)
|
||||
|
||||
fun register(command: ClientCommand) {
|
||||
commands[command.name] = command
|
||||
}
|
||||
|
||||
fun execute(input: String) {
|
||||
val parts = input.trim().split(" ")
|
||||
val name = parts[0].lowercase().trim()
|
||||
val args = parts.drop(1)
|
||||
|
||||
val command = commands[name]
|
||||
if (command == null) {
|
||||
logger.info { "Неизвестная команда: '$name'. Введите 'help' для справки."}
|
||||
return
|
||||
}
|
||||
|
||||
addToHistory(name)
|
||||
try {
|
||||
if (command.type == CommandType.MODEL) {
|
||||
val model = HumanBeingBuilder(io).build()
|
||||
val res = command.execute(args, model)
|
||||
io.print(res)
|
||||
} else {
|
||||
val res = command.execute(args)
|
||||
io.print(res)
|
||||
}
|
||||
} catch (e: IllegalStateException) {
|
||||
logger.info {"[Ошибка выполнения] ${e.message}"}
|
||||
}
|
||||
}
|
||||
|
||||
fun getHistory(): List<String> = history.toList()
|
||||
|
||||
fun getCommands(): Map<String, ClientCommand> = commands.toMap()
|
||||
|
||||
private fun addToHistory(name: String) {
|
||||
if (history.size >= 12) history.removeFirst()
|
||||
history.addLast(name)
|
||||
}
|
||||
|
||||
fun getCommandNames(): List<String> = commands.keys.toList()
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package runner
|
||||
|
||||
class CommandValidator {
|
||||
|
||||
// Команды, которые требуют ввода HumanBeing
|
||||
private val humanBeingCommands = setOf("add", "add_if_max", "add_if_min")
|
||||
|
||||
// Команды с аргументом
|
||||
private val commandsWArgs = setOf("update", "remove_by_id", "execute_script")
|
||||
|
||||
// Все команды, доступные клиенту
|
||||
private val knownCommands = setOf(
|
||||
"help", "info", "show", "add", "update",
|
||||
"remove_by_id", "clear", "execute_script",
|
||||
"add_if_max", "add_if_min", "history",
|
||||
"sum_of_minutes_of_waiting", "min_by_name",
|
||||
"print_field_descending_minutes_of_waiting",
|
||||
"exit"
|
||||
)
|
||||
|
||||
fun getKnownCommands(): Set<String> {return knownCommands}
|
||||
|
||||
fun validate(command: String, args: List<String>?): Pair<Boolean, String?> {
|
||||
if (command !in knownCommands){
|
||||
return Pair(false, "Неизвестная команда: '$command'. Введите 'help' для справки.")
|
||||
}
|
||||
|
||||
if (command in commandsWArgs && args?.isEmpty() == true) {
|
||||
return if (command == "execute_script") {
|
||||
Pair(false, "[Ошибка] Укажите путь к файлу. Пример: execute_script script.txt")
|
||||
} else {
|
||||
// maybe create another set w commands w file args and another for other args
|
||||
Pair(false, "[Ошибка] Укажите id элемента. Пример: $command <uuid>")
|
||||
}
|
||||
}
|
||||
return Pair(true, null)
|
||||
}
|
||||
|
||||
fun isBuildsHumanBeing(command: String): Boolean {
|
||||
return command in humanBeingCommands
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package runner
|
||||
|
||||
class ModelValidator {
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user