Skip to content
Snippets Groups Projects
Verified Commit 6787f348 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Continue invoker implementation

parent 2dcd077e
No related branches found
No related tags found
No related merge requests found
Showing
with 643 additions and 64 deletions
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.client
import de.justjanne.libquassel.annotations.ProtocolSide
import de.justjanne.libquassel.protocol.exceptions.RpcInvocationFailedException
import de.justjanne.libquassel.protocol.models.Message
import de.justjanne.libquassel.protocol.models.SignalProxyMessage
import de.justjanne.libquassel.protocol.models.StatusMessage
import de.justjanne.libquassel.protocol.models.ids.IdentityId
import de.justjanne.libquassel.protocol.models.ids.NetworkId
import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
import de.justjanne.libquassel.protocol.syncables.HeartBeatHandler
import de.justjanne.libquassel.protocol.syncables.ObjectRepository
import de.justjanne.libquassel.protocol.syncables.Session
import de.justjanne.libquassel.protocol.syncables.SyncableStub
import de.justjanne.libquassel.protocol.syncables.common.AliasManager
import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
import de.justjanne.libquassel.protocol.syncables.common.BufferSyncer
import de.justjanne.libquassel.protocol.syncables.common.BufferViewManager
import de.justjanne.libquassel.protocol.syncables.common.CoreInfo
import de.justjanne.libquassel.protocol.syncables.common.DccConfig
import de.justjanne.libquassel.protocol.syncables.common.HighlightRuleManager
import de.justjanne.libquassel.protocol.syncables.common.Identity
import de.justjanne.libquassel.protocol.syncables.common.IgnoreListManager
import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
import de.justjanne.libquassel.protocol.syncables.common.Network
import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig
import de.justjanne.libquassel.protocol.syncables.common.RpcHandler
import de.justjanne.libquassel.protocol.syncables.invoker.Invokers
import de.justjanne.libquassel.protocol.syncables.state.NetworkState
import de.justjanne.libquassel.protocol.util.update
import de.justjanne.libquassel.protocol.variant.QVariantMap
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.nio.ByteBuffer
class ClientSession : Session {
override val protocolSide = ProtocolSide.CLIENT
override val objectRepository = ObjectRepository()
override val rpcHandler = ClientRpcHandler(this)
val heartBeatHandler = HeartBeatHandler()
override fun network(id: NetworkId) = state().networks[id]
override fun addNetwork(id: NetworkId) {
val network = Network(this, state = NetworkState(id))
synchronize(network)
state.update {
copy(networks = networks + Pair(id, network))
}
}
override fun removeNetwork(id: NetworkId) {
val network = network(id) ?: return
stopSynchronize(network)
state.update {
copy(networks = networks - id)
}
}
override fun identity(id: IdentityId) = state().identities[id]
override fun addIdentity(properties: QVariantMap) {
val identity = Identity(this)
identity.fromVariantMap(properties)
synchronize(identity)
state.update {
copy(identities = identities + Pair(identity.id(), identity))
}
}
override fun removeIdentity(id: IdentityId) {
val identity = identity(id) ?: return
stopSynchronize(identity)
state.update {
copy(identities = identities - id)
}
}
override val aliasManager get() = state().aliasManager
override val backlogManager get() = state().backlogManager
override val bufferSyncer get() = state().bufferSyncer
override val bufferViewManager get() = state().bufferViewManager
override val highlightRuleManager get() = state().highlightRuleManager
override val ignoreListManager get() = state().ignoreListManager
override val ircListHelper get() = state().ircListHelper
override val coreInfo get() = state().coreInfo
override val dccConfig get() = state().dccConfig
override val networkConfig get() = state().networkConfig
override fun synchronize(syncable: SyncableStub) {
if (objectRepository.add(syncable)) {
dispatch(SignalProxyMessage.InitRequest(syncable.className, syncable.objectName))
}
}
override fun stopSynchronize(syncable: SyncableStub) {
objectRepository.remove(syncable)
}
override fun emit(message: SignalProxyMessage) {
TODO("Not yet implemented")
}
override fun dispatch(message: SignalProxyMessage) {
when (message) {
is SignalProxyMessage.HeartBeat -> emit(SignalProxyMessage.HeartBeatReply(message.timestamp))
is SignalProxyMessage.HeartBeatReply -> heartBeatHandler.recomputeLatency(message.timestamp, force = true)
is SignalProxyMessage.InitData -> objectRepository.init(
objectRepository.find(message.className, message.objectName) ?: return,
message.initData
)
is SignalProxyMessage.InitRequest -> {
// Ignore incoming requests, we’re a client, we shouldn’t ever receive these
}
is SignalProxyMessage.Rpc -> {
val invoker = Invokers.get(ProtocolSide.CLIENT, "RpcHandler")
?: throw RpcInvocationFailedException.InvokerNotFoundException("RpcHandler")
invoker.invoke(rpcHandler, message.slotName, message.params)
}
is SignalProxyMessage.Sync -> {
val invoker = Invokers.get(ProtocolSide.CLIENT, message.className)
?: throw RpcInvocationFailedException.InvokerNotFoundException(message.className)
val syncable = objectRepository.find(message.className, message.objectName)
?: throw RpcInvocationFailedException.SyncableNotFoundException(message.className, message.objectName)
invoker.invoke(syncable, message.slotName, message.params)
}
}
}
@Suppress("NOTHING_TO_INLINE")
inline fun state(): ClientSessionState = state.value
@Suppress("NOTHING_TO_INLINE")
inline fun flow(): Flow<ClientSessionState> = state
@PublishedApi
internal val state = MutableStateFlow(
ClientSessionState(
networks = mapOf(),
identities = mapOf(),
aliasManager = AliasManager(this),
backlogManager = BacklogManager(this),
bufferSyncer = BufferSyncer(this),
bufferViewManager = BufferViewManager(this),
highlightRuleManager = HighlightRuleManager(this),
ignoreListManager = IgnoreListManager(this),
ircListHelper = IrcListHelper(this),
coreInfo = CoreInfo(this),
dccConfig = DccConfig(this),
networkConfig = NetworkConfig(this)
)
)
class ClientRpcHandler(session: Session) : RpcHandler(session) {
override fun objectRenamed(classname: ByteBuffer, newName: String?, oldName: String?) {
val objectRepository = session?.objectRepository ?: return
val className = StringSerializerUtf8.deserializeRaw(classname)
val syncable = objectRepository.find(className, oldName ?: return)
?: return
objectRepository.rename(syncable, newName ?: return)
}
override fun displayMsg(message: Message) {
messages.tryEmit(message)
}
override fun displayStatusMsg(net: String?, msg: String?) {
statusMessage.tryEmit(StatusMessage(net, msg ?: return))
}
@Suppress("NOTHING_TO_INLINE")
inline fun messages(): Flow<Message> = messages
@PublishedApi
internal val messages = MutableSharedFlow<Message>()
@Suppress("NOTHING_TO_INLINE")
inline fun statusMessage(): StateFlow<StatusMessage?> = statusMessage
@PublishedApi
internal val statusMessage = MutableStateFlow<StatusMessage?>(null)
}
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.client
import de.justjanne.libquassel.protocol.models.ids.IdentityId
import de.justjanne.libquassel.protocol.models.ids.NetworkId
import de.justjanne.libquassel.protocol.syncables.common.AliasManager
import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
import de.justjanne.libquassel.protocol.syncables.common.BufferSyncer
import de.justjanne.libquassel.protocol.syncables.common.BufferViewManager
import de.justjanne.libquassel.protocol.syncables.common.CoreInfo
import de.justjanne.libquassel.protocol.syncables.common.DccConfig
import de.justjanne.libquassel.protocol.syncables.common.HighlightRuleManager
import de.justjanne.libquassel.protocol.syncables.common.Identity
import de.justjanne.libquassel.protocol.syncables.common.IgnoreListManager
import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
import de.justjanne.libquassel.protocol.syncables.common.Network
import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig
data class ClientSessionState(
val networks: Map<NetworkId, Network>,
val identities: Map<IdentityId, Identity>,
val aliasManager: AliasManager,
val backlogManager: BacklogManager,
val bufferSyncer: BufferSyncer,
val bufferViewManager: BufferViewManager,
val ignoreListManager: IgnoreListManager,
val highlightRuleManager: HighlightRuleManager,
val ircListHelper: IrcListHelper,
val coreInfo: CoreInfo,
val dccConfig: DccConfig,
val networkConfig: NetworkConfig
)
......@@ -16,8 +16,8 @@ import de.justjanne.libquassel.protocol.models.flags.MessageType
import de.justjanne.libquassel.protocol.models.flags.MessageTypes
import de.justjanne.libquassel.protocol.models.ids.BufferId
import de.justjanne.libquassel.protocol.models.ids.MsgId
import de.justjanne.libquassel.protocol.syncables.BacklogManager
import de.justjanne.libquassel.protocol.syncables.Session
import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
import de.justjanne.libquassel.protocol.variant.QVariantList
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
......
......@@ -12,8 +12,8 @@ package de.justjanne.libquassel.client.syncables
import de.justjanne.libquassel.client.exceptions.IrcListException
import de.justjanne.libquassel.protocol.models.QStringList
import de.justjanne.libquassel.protocol.models.ids.NetworkId
import de.justjanne.libquassel.protocol.syncables.IrcListHelper
import de.justjanne.libquassel.protocol.syncables.Session
import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
import de.justjanne.libquassel.protocol.variant.QVariantList
import de.justjanne.libquassel.protocol.variant.into
import kotlin.coroutines.Continuation
......
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.generator
import com.squareup.kotlinpoet.ANY
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.MAP
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.STRING
import de.justjanne.libquassel.annotations.ProtocolSide
import de.justjanne.libquassel.generator.rpcmodel.RpcModel
object Constants {
fun invokerName(model: RpcModel.ObjectModel, side: ProtocolSide) = ClassName(
TYPENAME_INVOKER.packageName,
"${model.rpcName}${side.name.toLowerCase().capitalize()}Invoker"
)
val TYPENAME_ANY = ANY.copy(nullable = true)
val TYPENAME_SYNCABLESTUB = ClassName(
"de.justjanne.libquassel.protocol.syncables",
"SyncableStub"
)
val TYPENAME_INVOKER = ClassName(
"de.justjanne.libquassel.protocol.syncables.invoker",
"Invoker"
)
val TYPENAME_INVOKERREGISTRY = ClassName(
"de.justjanne.libquassel.protocol.syncables.invoker",
"InvokerRegistry"
)
val TYPENAME_INVOKERMAP = MAP.parameterizedBy(STRING, TYPENAME_INVOKER)
val TYPENAME_UNKNOWN_METHOD_EXCEPTION = ClassName(
"de.justjanne.libquassel.protocol.exceptions",
"RpcInvocationFailedException", "UnknownMethodException"
)
val TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION = ClassName(
"de.justjanne.libquassel.protocol.exceptions",
"RpcInvocationFailedException", "WrongObjectTypeException"
)
val TYPENAME_QVARIANTLIST = ClassName(
"de.justjanne.libquassel.protocol.variant",
"QVariantList"
)
val TYPENAME_QVARIANT_INTOORTHROW = ClassName(
"de.justjanne.libquassel.protocol.variant",
"intoOrThrow"
)
val TYPENAME_GENERATED = ClassName(
"de.justjanne.libquassel.annotations",
"Generated"
)
init {
System.setProperty("idea.io.use.nio2", "true")
}
}
......@@ -19,6 +19,7 @@ import de.justjanne.libquassel.annotations.SyncedObject
import de.justjanne.libquassel.generator.visitors.KSDeclarationParser
import de.justjanne.libquassel.generator.visitors.KotlinSaver
import de.justjanne.libquassel.generator.visitors.RpcModelProcessor
import de.justjanne.libquassel.generator.visitors.RpcObjectCollector
class InvokerProcessor : SymbolProcessor {
lateinit var codeGenerator: CodeGenerator
......@@ -35,15 +36,21 @@ class InvokerProcessor : SymbolProcessor {
}
override fun process(resolver: Resolver): List<KSAnnotated> {
resolver.getSymbolsWithAnnotation(SyncedObject::class.java.canonicalName)
.mapNotNull { it.accept(KSDeclarationParser(resolver, logger), Unit) }
.flatMap {
val annotationModels = resolver.getSymbolsWithAnnotation(SyncedObject::class.java.canonicalName)
val rpcModels = annotationModels.mapNotNull { it.accept(KSDeclarationParser(resolver, logger), Unit) }
val invokerFiles = rpcModels.flatMap {
listOfNotNull(
it.accept(RpcModelProcessor(), ProtocolSide.CLIENT),
it.accept(RpcModelProcessor(), ProtocolSide.CORE),
) + InvokerRegistryGenerator.generateRegistry(
RpcObjectCollector().apply {
rpcModels.forEach { it.accept(this, Unit) }
}.objects
)
}
.map { it.accept(KotlinSaver(), codeGenerator) }
invokerFiles.forEach {
it.accept(KotlinSaver(), codeGenerator)
}
return emptyList()
}
......
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.generator
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.buildCodeBlock
import de.justjanne.libquassel.annotations.ProtocolSide
import de.justjanne.libquassel.generator.kotlinmodel.KotlinModel
import de.justjanne.libquassel.generator.rpcmodel.RpcModel
import de.justjanne.libquassel.generator.util.kotlinpoet.withIndent
object InvokerRegistryGenerator {
private fun generateCodeBlock(
objects: List<RpcModel.ObjectModel>,
side: ProtocolSide
) = buildCodeBlock {
add("mapOf(\n")
withIndent {
for (syncable in objects) {
addStatement("%S to %T,", syncable.rpcName, Constants.invokerName(syncable, side))
}
}
if (objects.isEmpty()) {
add("\n")
}
add(")")
}
fun generateRegistry(objects: List<RpcModel.ObjectModel>): KotlinModel.FileModel {
val name = ClassName(
Constants.TYPENAME_INVOKER.packageName,
"GeneratedInvokerRegistry"
)
return KotlinModel.FileModel(
objects.map(RpcModel.ObjectModel::source),
FileSpec.builder(name.packageName, name.simpleName)
.addType(
TypeSpec.objectBuilder("GeneratedInvokerRegistry")
.addSuperinterface(Constants.TYPENAME_INVOKERREGISTRY)
.addProperty(
PropertySpec.builder("clientInvokers", Constants.TYPENAME_INVOKERMAP)
.addModifiers(KModifier.OVERRIDE)
.initializer(generateCodeBlock(objects, ProtocolSide.CLIENT))
.build()
)
.addProperty(
PropertySpec.builder("coreInvokers", Constants.TYPENAME_INVOKERMAP)
.addModifiers(KModifier.OVERRIDE)
.initializer(generateCodeBlock(objects, ProtocolSide.CORE))
.build()
)
.build()
).build()
)
}
}
......@@ -16,7 +16,7 @@ import com.squareup.kotlinpoet.FileSpec
sealed class KotlinModel {
data class FileModel(
val source: KSClassDeclaration,
val source: List<KSClassDeclaration>,
val data: FileSpec
) : KotlinModel() {
override fun <D, R> accept(visitor: KotlinModelVisitor<D, R>, data: D) =
......
......@@ -11,17 +11,32 @@ package de.justjanne.libquassel.generator.visitors
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.symbol.KSClassDeclaration
import de.justjanne.libquassel.generator.kotlinmodel.KotlinModel
import de.justjanne.libquassel.generator.kotlinmodel.KotlinModelVisitor
import java.io.IOException
class KotlinSaver : KotlinModelVisitor<CodeGenerator, Unit> {
private fun generateDependencies(sources: List<KSClassDeclaration>): Dependencies {
val sourceFiles = sources.mapNotNull(KSClassDeclaration::containingFile)
return Dependencies(sourceFiles.size > 1, *sourceFiles.toTypedArray())
}
override fun visitFileModel(model: KotlinModel.FileModel, data: CodeGenerator) {
data.createNewFile(
Dependencies(false, model.source.containingFile!!),
require(model.source.isNotEmpty()) {
"Source may not be empty. Sources was empty for $model"
}
val writer = data.createNewFile(
generateDependencies(model.source),
model.data.packageName,
model.data.name
).bufferedWriter(Charsets.UTF_8).use {
model.data.writeTo(it)
).bufferedWriter(Charsets.UTF_8)
model.data.writeTo(writer)
try {
writer.close()
} catch (_: IOException) {
// Ignored
}
}
......
......@@ -9,18 +9,23 @@
package de.justjanne.libquassel.generator.visitors
import com.squareup.kotlinpoet.ANY
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asTypeName
import com.squareup.kotlinpoet.buildCodeBlock
import de.justjanne.libquassel.annotations.ProtocolSide
import de.justjanne.libquassel.generator.Constants.TYPENAME_GENERATED
import de.justjanne.libquassel.generator.Constants.TYPENAME_INVOKER
import de.justjanne.libquassel.generator.Constants.TYPENAME_QVARIANTLIST
import de.justjanne.libquassel.generator.Constants.TYPENAME_QVARIANT_INTOORTHROW
import de.justjanne.libquassel.generator.Constants.TYPENAME_SYNCABLESTUB
import de.justjanne.libquassel.generator.Constants.TYPENAME_UNKNOWN_METHOD_EXCEPTION
import de.justjanne.libquassel.generator.Constants.TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION
import de.justjanne.libquassel.generator.kotlinmodel.KotlinModel
import de.justjanne.libquassel.generator.rpcmodel.RpcModel
import de.justjanne.libquassel.generator.rpcmodel.RpcModelVisitor
......@@ -35,7 +40,7 @@ class RpcModelProcessor : RpcModelVisitor<ProtocolSide, KotlinModel?> {
"${model.rpcName}${data.name.toLowerCase().capitalize()}Invoker"
)
return KotlinModel.FileModel(
model.source,
listOf(model.source),
FileSpec.builder(name.packageName, name.simpleName)
.addImport(
TYPENAME_QVARIANT_INTOORTHROW.packageName,
......@@ -44,7 +49,7 @@ class RpcModelProcessor : RpcModelVisitor<ProtocolSide, KotlinModel?> {
.addAnnotation(TYPENAME_GENERATED)
.addType(
TypeSpec.objectBuilder(name.simpleName)
.addSuperinterface(TYPENAME_INVOKER.parameterizedBy(model.name))
.addSuperinterface(TYPENAME_INVOKER)
.addAnnotation(TYPENAME_GENERATED)
.addProperty(
PropertySpec.builder(
......@@ -63,7 +68,7 @@ class RpcModelProcessor : RpcModelVisitor<ProtocolSide, KotlinModel?> {
.addParameter(
ParameterSpec.builder(
"on",
TYPENAME_ANY
TYPENAME_SYNCABLESTUB
).build()
).addParameter(
ParameterSpec.builder(
......@@ -127,36 +132,4 @@ class RpcModelProcessor : RpcModelVisitor<ProtocolSide, KotlinModel?> {
)
override fun visitParameterModel(model: RpcModel.ParameterModel, data: ProtocolSide): KotlinModel? = null
companion object {
private val TYPENAME_INVOKER = ClassName(
"de.justjanne.libquassel.protocol.syncables.invoker",
"Invoker"
)
private val TYPENAME_UNKNOWN_METHOD_EXCEPTION = ClassName(
"de.justjanne.libquassel.protocol.exceptions",
"UnknownMethodException"
)
private val TYPENAME_WRONG_OBJECT_TYPE_EXCEPTION = ClassName(
"de.justjanne.libquassel.protocol.exceptions",
"WrongObjectTypeException"
)
private val TYPENAME_QVARIANTLIST = ClassName(
"de.justjanne.libquassel.protocol.variant",
"QVariantList"
)
private val TYPENAME_QVARIANT_INTOORTHROW = ClassName(
"de.justjanne.libquassel.protocol.variant",
"intoOrThrow"
)
private val TYPENAME_GENERATED = ClassName(
"de.justjanne.libquassel.annotations",
"Generated"
)
private val TYPENAME_ANY = ANY.copy(nullable = true)
init {
System.setProperty("idea.io.use.nio2", "true")
}
}
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.generator.visitors
import de.justjanne.libquassel.generator.rpcmodel.RpcModel
import de.justjanne.libquassel.generator.rpcmodel.RpcModelVisitor
class RpcObjectCollector : RpcModelVisitor<Unit, Unit> {
val objects = mutableListOf<RpcModel.ObjectModel>()
override fun visitObjectModel(model: RpcModel.ObjectModel, data: Unit) {
objects.add(model)
}
override fun visitFunctionModel(model: RpcModel.FunctionModel, data: Unit) = Unit
override fun visitParameterModel(model: RpcModel.ParameterModel, data: Unit) = Unit
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.protocol.exceptions
import java.lang.Exception
sealed class RpcInvocationFailedException(message: String) : Exception(message) {
data class InvokerNotFoundException(
val className: String
) : RpcInvocationFailedException("Could not find invoker for $className")
data class SyncableNotFoundException(
val className: String,
val objectName: String
) : RpcInvocationFailedException("Could not find syncable $objectName for type $className")
data class UnknownMethodException(
val className: String,
val methodName: String
) : RpcInvocationFailedException("Could not find method $methodName for type $className")
data class WrongObjectTypeException(
val obj: Any?,
val type: String
) : RpcInvocationFailedException("Wrong type for invoker, expected $type but got $obj")
}
......@@ -7,9 +7,9 @@
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.protocol.exceptions
package de.justjanne.libquassel.protocol.models
data class WrongObjectTypeException(
val obj: Any?,
val type: String
) : Exception()
data class StatusMessage(
val network: String?,
val message: String
)
......@@ -10,6 +10,13 @@
package de.justjanne.libquassel.protocol.models.alias
data class Alias(
val name: String?,
val expansion: String?
val name: String,
val expansion: String
) {
companion object {
fun of(name: String?, expansion: String?) = Alias(
name ?: "",
expansion ?: ""
)
}
}
......@@ -13,7 +13,7 @@ import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
import de.justjanne.libquassel.protocol.serializers.qt.QVariantMapSerializer
import de.justjanne.libquassel.protocol.syncables.Identity
import de.justjanne.libquassel.protocol.syncables.common.Identity
import de.justjanne.libquassel.protocol.variant.QVariantMap
import java.nio.ByteBuffer
......
......@@ -13,7 +13,7 @@ import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
import de.justjanne.libquassel.protocol.serializers.qt.QVariantMapSerializer
import de.justjanne.libquassel.protocol.syncables.IrcChannel
import de.justjanne.libquassel.protocol.syncables.common.IrcChannel
import de.justjanne.libquassel.protocol.variant.QVariantMap
import java.nio.ByteBuffer
......
......@@ -13,7 +13,7 @@ import de.justjanne.libquassel.protocol.features.FeatureSet
import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
import de.justjanne.libquassel.protocol.serializers.qt.QVariantMapSerializer
import de.justjanne.libquassel.protocol.syncables.IrcUser
import de.justjanne.libquassel.protocol.syncables.common.IrcUser
import de.justjanne.libquassel.protocol.variant.QVariantMap
import java.nio.ByteBuffer
......
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.protocol.syncables
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import org.threeten.bp.Instant
class HeartBeatHandler {
private var lastReceived: Instant? = null
/**
* Utility function to recompute the latency value,
* usually should be called by a timer.
*/
fun recomputeLatency() {
recomputeLatency(Instant.now(), force = false)
}
/**
* Utility function to recompute the latency value with a given heartbeat value
*/
fun recomputeLatency(current: Instant, force: Boolean) {
val last = lastReceived?.toEpochMilli() ?: return
val roundtripLatency = current.toEpochMilli() - last
if (force || roundtripLatency > this.roundtripLatency.value ?: return) {
this.roundtripLatency.value = roundtripLatency
}
}
@Suppress("NOTHING_TO_INLINE")
inline fun flow(): Flow<Long?> = roundtripLatency
@PublishedApi
internal val roundtripLatency = MutableStateFlow<Long?>(null)
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.protocol.syncables
data class ObjectIdentifier(
val className: String,
val objectName: String
) {
constructor(syncable: SyncableStub) : this(syncable.className, syncable.objectName)
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.protocol.syncables
import de.justjanne.libquassel.protocol.util.update
import de.justjanne.libquassel.protocol.variant.QVariantMap
import kotlinx.coroutines.flow.MutableStateFlow
class ObjectRepository {
fun add(syncable: SyncableStub): Boolean {
val identifier = ObjectIdentifier(syncable)
if (syncable is StatefulSyncableStub) {
state.update {
copy(
syncables = syncables + Pair(
identifier,
syncable
),
waiting = waiting + syncable
)
}
return true
} else {
state.update {
copy(
syncables = syncables + Pair(
identifier,
syncable
)
)
}
return false
}
}
fun init(syncable: SyncableStub, properties: QVariantMap) {
if (syncable is StatefulSyncableStub) {
syncable.fromVariantMap(properties)
syncable.initialized = true
state.update {
copy(waiting = waiting - syncable)
}
}
}
fun rename(syncable: SyncableStub, newName: String) {
val identifier = ObjectIdentifier(syncable)
state.update {
copy(
syncables = syncables - identifier + Pair(
identifier.copy(objectName = newName),
syncable
)
)
}
}
fun remove(syncable: SyncableStub) {
val identifier = ObjectIdentifier(syncable)
syncable.initialized = false
state.update {
copy(syncables = syncables - identifier)
}
}
fun find(className: String, objectName: String): SyncableStub? {
return state().syncables[ObjectIdentifier(objectName, className)]
}
inline fun <reified T : SyncableStub> find(objectName: String): T? {
return find(T::class.java.simpleName, objectName) as? T
}
@Suppress("NOTHING_TO_INLINE")
inline fun state() = flow().value
@Suppress("NOTHING_TO_INLINE")
inline fun flow() = state
@PublishedApi
internal val state = MutableStateFlow(ObjectRepositoryState())
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment