Kuba Petržílka
04/21/2021, 4:43 PMsealed class Result<out T, out E>
class Success<out T>(val value: T) : Result<T, Nothing>()
class Failure<out E>(val error: E) : Result<Nothing, E>()
...
class AccumulatedResultContext<U, E> {
var lastPartialResult: Result<U, E>? = null
var continuation: CancellableContinuation<*>? = null
suspend operator fun <T, F: E> Result<T, F>.not(): T = when(this) {
is Success -> this.value
is Failure -> suspendCancellableCoroutine {
lastPartialResult = this
continuation = it
}
}
}
fun <T, E> accumulateResult(
block: suspend AccumulatedResultContext<T, E>.() -> Result<T, E>
): Result<T, E> =
runBlocking {
val context = AccumulatedResultContext<T, E>()
launch(start = CoroutineStart.UNDISPATCHED) {
context.lastPartialResult = block(context)
}
if (context.continuation != null) context.continuation!!.cancel()
context.lastPartialResult!!
}
...
fun mapToUserRecord(
userSource: UserSoruce
): Result<UserRecord.Persisted, ConversionError> = accumulateResult {
val id = userSource.id
val name = ! validate(userSource.name)
val description = ! validate(userSource.description)
Success(UserRecord.Persisted(id, name, description))
}
Especially, I d like to avoid using the experimental CoroutineStart.UNDISPATCHED
but it is required when I start nesting accumulateResult { }
because the execution order of nested coroutines is not guaranteed
Thanks for any suggestions