All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.org.brightify.hyperdrive.property.impl.AsyncMapDeferredObservableProperty.kt Maven / Gradle / Ivy

There is a newer version: 0.1.159
Show newest version
package org.brightify.hyperdrive.property.impl

import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.first
import org.brightify.hyperdrive.CancellationToken
import org.brightify.hyperdrive.Lifecycle
import org.brightify.hyperdrive.property.DeferredObservableProperty
import org.brightify.hyperdrive.property.ObservableProperty
import org.brightify.hyperdrive.util.AsyncQueue
import org.brightify.hyperdrive.utils.Optional
import org.brightify.hyperdrive.utils.filterSome
import org.brightify.hyperdrive.utils.someOrDefault

internal class AsyncMapDeferredObservableProperty(
    private val source: ObservableProperty,
    private val asyncMap: suspend (T) -> U,
    private val lifecycle: Lifecycle,
    private val equalityPolicy: ObservableProperty.EqualityPolicy,
    private val overflowPolicy: AsyncQueue.OverflowPolicy,
): DeferredObservableProperty, ObservableProperty.Listener {
    override val latestValue: Optional
        get() = storage.value

    private val listeners = ValueChangeListenerHandler(this)
    // FIXME: This won't support identityEqualityPolicy/neverEqualityPolicy!
    private val storage = MutableStateFlow>(Optional.None)

    private val queue = AsyncQueue(overflowPolicy, lifecycle) {
        val newValue = asyncMap(it)
        val oldMappedValue = storage.value
        val shouldSave = oldMappedValue !is Optional.Some || equalityPolicy.isEqual(oldMappedValue.value, newValue)
        if (shouldSave) {
            listeners.runNotifyingListeners(newValue) {
                storage.value = Optional.Some(it)
            }
        }
    }

    init {
        source.addListener(this)
    }

    override fun valueDidChange(oldValue: T, newValue: T) {
        queue.push(newValue)
    }

    override suspend fun await(): U = coroutineScope {
        storage.value.someOrDefault { storage.filterSome().first() }
    }

    override suspend fun nextValue(): U = coroutineScope {
        storage.drop(1).filterSome().first()
    }

    override fun addListener(listener: DeferredObservableProperty.Listener): CancellationToken = listeners.addListener(listener)

    override fun removeListener(listener: DeferredObservableProperty.Listener) = listeners.removeListener(listener)
}