ViewProteinStructuresCommandHandler.kt
package de.pflugradts.passbird.application.commandhandling.handler.protein
import de.pflugradts.kotlinextensions.MutableOption.Companion.emptyOption
import de.pflugradts.kotlinextensions.MutableOption.Companion.optionOf
import de.pflugradts.kotlinextensions.Option
import de.pflugradts.passbird.application.UserInterfaceAdapterPort
import de.pflugradts.passbird.application.commandhandling.CommandExecutionTracker
import de.pflugradts.passbird.application.commandhandling.capabilities.CanPrintInfo
import de.pflugradts.passbird.application.commandhandling.command.ViewProteinStructuresCommand
import de.pflugradts.passbird.application.commandhandling.handler.TypedCommandHandler
import de.pflugradts.passbird.domain.model.shell.Shell
import de.pflugradts.passbird.domain.model.shell.ShellPair
import de.pflugradts.passbird.domain.model.transfer.Output
import de.pflugradts.passbird.domain.service.password.PasswordService
class ViewProteinStructuresCommandHandler constructor(
private val canPrintInfo: CanPrintInfo,
private val passwordService: PasswordService,
private val userInterfaceAdapterPort: UserInterfaceAdapterPort,
private val commandExecutionTracker: CommandExecutionTracker,
) : TypedCommandHandler<ViewProteinStructuresCommand>(ViewProteinStructuresCommand::class.java) {
override fun handleCommand(command: ViewProteinStructuresCommand) {
passwordService.viewProteinTypes(command.argument).orNull()?.also { types ->
try {
passwordService.viewProteinStructures(command.argument).orNull()?.also { structures ->
try {
userInterfaceAdapterPort.send(*outputsOfHeader())
types.zip(structures).forEachIndexed { index, proteinPair ->
userInterfaceAdapterPort.send(*outputsOf(index, proteinPair.toShellPairOption()))
}
} finally {
structures.scramblePresentShells()
}
} ?: commandExecutionTracker.markFailure()
} finally {
types.scramblePresentShells()
}
} ?: commandExecutionTracker.markFailure()
command.invalidateInput()
userInterfaceAdapterPort.sendLineBreak()
}
private fun List<Option<Shell>>.scramblePresentShells() {
forEach { it.ifPresent(Shell::scramble) }
}
private fun outputsOfHeader(): Array<Output> = with(canPrintInfo) {
arrayOf(out("\n"), outBold(SLOT_HEADER), outBold("Type".padded()), outBold(SEP), outBold("Structure".padded()))
}
private fun outputsOf(index: Int, shellPairOption: Option<ShellPair>): Array<Output> = with(canPrintInfo) {
arrayOf(
out(padded(index)),
out(shellPairOption.map { it.first.asString() }.orElse("---").padded()),
out(SEP),
out(shellPairOption.map { it.second.asString() }.orElse("---").padded()),
)
}
}
private const val SEP = " | "
private const val SLOT_HEADER = "Slot "
private fun String.padded() = padEnd(20, ' ')
private fun padded(index: Int) = "$index:".padEnd(SLOT_HEADER.length, ' ')
private fun Pair<Option<Shell>, Option<Shell>>.toShellPairOption(): Option<ShellPair> =
first.map { second.map { optionOf(first.get() to second.get()) }.orElse(emptyOption()) }.orElse(emptyOption())