diff --git a/fleet/kernel/srcCommonMain/fleet/kernel/rete/Queries.kt b/fleet/kernel/srcCommonMain/fleet/kernel/rete/Queries.kt index 1c13e9ea59af..e3076d92025c 100644 --- a/fleet/kernel/srcCommonMain/fleet/kernel/rete/Queries.kt +++ b/fleet/kernel/srcCommonMain/fleet/kernel/rete/Queries.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.first import kotlin.jvm.JvmName /** - * There is always a match in Single, run with it. If the match is invalidate while the body is running, cancel everything. + * There is always a match in Single, run with it. If the match is invalidate while the body is running, cancel everything. */ suspend fun StateQuery.withCurrentMatch(f: suspend CoroutineScope.(T) -> R): WithMatchResult { return matchesFlow().first().withMatch(f) @@ -63,18 +63,20 @@ fun Query.orNull(): StateQuery = @Suppress("UNCHECKED_CAST") -fun Query.singleOrNone(): Query = checkMatchesCount(0..1) as Query - -@Suppress("UNCHECKED_CAST") -fun Query.single(): StateQuery = checkMatchesCount(1..1) as Query - -private fun Query<*, T>.checkMatchesCount(range: IntRange): Query<*, T> = - run { - var count = 0 - transform { token, emit -> - count += if (token.added) 1 else -1 - check(count in range) { "Query produced $count matches while it was supposed to have only $range" } - emit(token) +fun Query.singleOrNone(msg: ((List) -> String)? = null): Query = + let { source -> + Query { + val none = Any() + var value: Any? = none + source.producer().transform { token, emit -> + when { + token.added && value != none -> error("More than one match, ${listOf(value, token.value).let { msg?.invoke(it as List) ?: "values: ${it}" }}") + token.added && value == none -> value = token.value as Any? + !token.added && value == none -> error("Nothing to retract") + !token.added && value != none -> value = none + } + emit(token) + } } }