diff --git a/build.gradle.kts b/build.gradle.kts index c8b02b807705e7e1b99554417aff87c3a7890a81..63be78db726b0bface5384110b9cdbca6cc753cd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,4 +15,4 @@ plugins { } group = "de.justjanne.libquassel" -version = "0.7.0" +version = "0.8.0" diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandler.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandler.kt index 1b143193068dd9c08704a4798ed5b955c43a8885..90dadc84583cc6236996aba871bdf8936ee14ef7 100644 --- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandler.kt +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/BaseInitHandler.kt @@ -12,13 +12,14 @@ package de.justjanne.libquassel.client.session import de.justjanne.libquassel.client.util.CoroutineQueue import de.justjanne.libquassel.protocol.syncables.ObjectIdentifier import de.justjanne.libquassel.protocol.syncables.SyncableStub +import de.justjanne.libquassel.protocol.util.StateHolder import de.justjanne.libquassel.protocol.util.update import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow class BaseInitHandler( private val session: ClientSession -) { +) : StateHolder<BaseInitHandlerState> { private val coroutineQueue = CoroutineQueue<Unit>() fun sync(stub: SyncableStub) { @@ -45,12 +46,7 @@ class BaseInitHandler( coroutineQueue.wait() } else Unit - @Suppress("NOTHING_TO_INLINE") - inline fun state(): BaseInitHandlerState = state.value - - @Suppress("NOTHING_TO_INLINE") - inline fun flow(): Flow<BaseInitHandlerState> = state - - @PublishedApi - internal val state = MutableStateFlow(BaseInitHandlerState()) + override fun state(): BaseInitHandlerState = state.value + override fun flow(): Flow<BaseInitHandlerState> = state + private val state = MutableStateFlow(BaseInitHandlerState()) } diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSession.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSession.kt index f9d4448fd780b4f05f777ba59fda28dba524d0a5..58d4c6b1b80ea556408ce7499e63c3ca6351d7f3 100644 --- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSession.kt +++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/session/ClientSession.kt @@ -38,6 +38,7 @@ import de.justjanne.libquassel.protocol.syncables.common.Network import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig import de.justjanne.libquassel.protocol.syncables.state.CertManagerState import de.justjanne.libquassel.protocol.syncables.state.NetworkState +import de.justjanne.libquassel.protocol.util.StateHolder import de.justjanne.libquassel.protocol.util.log.info import de.justjanne.libquassel.protocol.util.update import de.justjanne.libquassel.protocol.variant.QVariantMap @@ -51,7 +52,7 @@ class ClientSession( protocolFeatures: ProtocolFeatures, protocols: List<ProtocolMeta>, sslContext: SSLContext -) : Session { +) : Session, StateHolder<ClientSessionState> { override val side = ProtocolSide.CLIENT override val rpcHandler = ClientRpcHandler(this) @@ -202,14 +203,9 @@ class ClientSession( override val networkConfig get() = state().networkConfig - @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( + override fun state(): ClientSessionState = state.value + override fun flow(): Flow<ClientSessionState> = state + private val state = MutableStateFlow( ClientSessionState( networks = mapOf(), identities = mapOf(), diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/CoroutineChannel.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/CoroutineChannel.kt index 9ac728996772ad3d79f83d6c612a03d78448ab92..22e3de9e52967d03a77fed3500a2d0baa5d17a60 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/CoroutineChannel.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/io/CoroutineChannel.kt @@ -9,6 +9,7 @@ package de.justjanne.libquassel.protocol.io +import de.justjanne.libquassel.protocol.util.StateHolder import de.justjanne.libquassel.protocol.util.update import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asCoroutineDispatcher @@ -21,7 +22,7 @@ import java.nio.ByteBuffer import java.util.concurrent.Executors import javax.net.ssl.SSLContext -class CoroutineChannel { +class CoroutineChannel : StateHolder<CoroutineChannelState> { private lateinit var channel: StreamChannel private val writeContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher() private val readContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher() @@ -81,12 +82,7 @@ class CoroutineChannel { } } - @Suppress("NOTHING_TO_INLINE") - inline fun state(): CoroutineChannelState = state.value - - @Suppress("NOTHING_TO_INLINE") - inline fun flow(): Flow<CoroutineChannelState> = state - - @PublishedApi - internal val state = MutableStateFlow(CoroutineChannelState()) + override fun state(): CoroutineChannelState = state.value + override fun flow(): Flow<CoroutineChannelState> = state + private val state = MutableStateFlow(CoroutineChannelState()) } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/HeartBeatHandler.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/HeartBeatHandler.kt index 5435687b09edfe2570ba9ea004f88c8285b8581b..268c0c5058847c9b3be62a32472b9bb711db0d35 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/HeartBeatHandler.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/HeartBeatHandler.kt @@ -9,11 +9,12 @@ package de.justjanne.libquassel.protocol.syncables +import de.justjanne.libquassel.protocol.util.StateHolder import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import org.threeten.bp.Instant -class HeartBeatHandler { +class HeartBeatHandler : StateHolder<Long?> { private var lastReceived: Instant? = null /** @@ -30,14 +31,12 @@ class HeartBeatHandler { fun recomputeLatency(current: Instant, force: Boolean) { val last = lastReceived?.toEpochMilli() ?: return val roundtripLatency = current.toEpochMilli() - last - if (force || roundtripLatency > this.roundtripLatency.value ?: return) { + 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) + override fun flow(): Flow<Long?> = roundtripLatency + override fun state(): Long? = roundtripLatency.value + private val roundtripLatency = MutableStateFlow<Long?>(null) } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/ObjectRepository.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/ObjectRepository.kt index eeab61c0f5e5050585370f7dcbd7790997511611..a783468e48c57d5b0d9f80d4ec4e25aae8057507 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/ObjectRepository.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/ObjectRepository.kt @@ -9,11 +9,12 @@ package de.justjanne.libquassel.protocol.syncables +import de.justjanne.libquassel.protocol.util.StateHolder import de.justjanne.libquassel.protocol.util.update import de.justjanne.libquassel.protocol.variant.QVariantMap import kotlinx.coroutines.flow.MutableStateFlow -class ObjectRepository { +class ObjectRepository : StateHolder<ObjectRepositoryState> { fun add(syncable: SyncableStub): Boolean { val identifier = ObjectIdentifier(syncable) if (syncable is StatefulSyncableStub) { @@ -78,12 +79,7 @@ class ObjectRepository { 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()) + override fun state() = flow().value + override fun flow() = state + private val state = MutableStateFlow(ObjectRepositoryState()) } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt index 0b1067121421a8956a50bca35578e0ebf30ecaee..e3260e3ce920e2bbc24a458c4027d7fc3f713d9d 100644 --- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt @@ -10,6 +10,7 @@ package de.justjanne.libquassel.protocol.syncables import de.justjanne.libquassel.protocol.session.Session +import de.justjanne.libquassel.protocol.util.StateHolder import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -17,7 +18,7 @@ abstract class StatefulSyncableObject<T>( session: Session?, className: String, state: T -) : SyncableObject(session, className), StatefulSyncableStub { +) : SyncableObject(session, className), StatefulSyncableStub, StateHolder<T> { override fun toString(): String { return "$className(objectName=$objectName, state=${state()})" } @@ -38,12 +39,7 @@ abstract class StatefulSyncableObject<T>( return result } - @Suppress("NOTHING_TO_INLINE") - inline fun state(): T = state.value - - @Suppress("NOTHING_TO_INLINE") - inline fun flow(): Flow<T> = state - - @PublishedApi - internal val state = MutableStateFlow(state) + override fun state(): T = state.value + override fun flow(): Flow<T> = state + protected val state = MutableStateFlow(state) } diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/StateHolder.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/StateHolder.kt new file mode 100644 index 0000000000000000000000000000000000000000..03284a967610ea8be93d5315132b6d7e0359b3f3 --- /dev/null +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/StateHolder.kt @@ -0,0 +1,17 @@ +/* + * libquassel + * Copyright (c) 2022 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.util + +import kotlinx.coroutines.flow.Flow + +interface StateHolder<T> { + fun state(): T + fun flow(): Flow<T> +} diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/StateHolderExtensions.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/StateHolderExtensions.kt new file mode 100644 index 0000000000000000000000000000000000000000..780c6d21428b808c0a0761982e0c7f74bdf1c1f8 --- /dev/null +++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/util/StateHolderExtensions.kt @@ -0,0 +1,25 @@ +/* + * libquassel + * Copyright (c) 2022 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.util + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest + +@ExperimentalCoroutinesApi +@Suppress("NOTHING_TO_INLINE") +inline fun <T> Flow<StateHolder<T>?>.flatMap(): Flow<T?> = + flatMapLatest { it?.flow() ?: emptyFlow() } + +@ExperimentalCoroutinesApi +inline fun <reified T> Flow<Iterable<StateHolder<T>>?>.combineLatest(): Flow<List<T>> = + flatMapLatest { combine(it?.map(StateHolder<T>::flow).orEmpty(), ::listOf) }