From da821429411e1dc0ed9b4295a42b34797a281817 Mon Sep 17 00:00:00 2001 From: Nek-12 Date: Thu, 30 May 2024 17:34:38 +0300 Subject: [PATCH] feat!: Refactor ApiResult to be an inline class --- buildSrc/src/main/kotlin/Config.kt | 6 +- .../kotlin/pro/respawn/apiresult/ApiResult.kt | 242 +++++++++--------- .../pro/respawn/apiresult/CollectionResult.kt | 59 +++-- .../pro/respawn/apiresult/SuspendResult.kt | 17 +- 4 files changed, 159 insertions(+), 165 deletions(-) diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt index 56c64bd..f9d8095 100644 --- a/buildSrc/src/main/kotlin/Config.kt +++ b/buildSrc/src/main/kotlin/Config.kt @@ -15,10 +15,10 @@ object Config { const val artifactId = "$group.$artifact" - const val majorRelease = 1 - const val minorRelease = 1 + const val majorRelease = 2 + const val minorRelease = 0 const val patch = 0 - const val postfix = "" + const val postfix = "-alpha01" const val versionName = "$majorRelease.$minorRelease.$patch$postfix" const val url = "https://github.com/respawn-app/ApiResult" const val licenseName = "The Apache Software License, Version 2.0" diff --git a/core/src/commonMain/kotlin/pro/respawn/apiresult/ApiResult.kt b/core/src/commonMain/kotlin/pro/respawn/apiresult/ApiResult.kt index d730160..bee2a40 100644 --- a/core/src/commonMain/kotlin/pro/respawn/apiresult/ApiResult.kt +++ b/core/src/commonMain/kotlin/pro/respawn/apiresult/ApiResult.kt @@ -5,30 +5,36 @@ "unused", "NOTHING_TO_INLINE", "TooManyFunctions", - "ThrowingExceptionsWithoutMessageOrCause", + "ThrowingExceptionsWithoutMessageOrCause", "UNCHECKED_CAST", ) package pro.respawn.apiresult +import pro.respawn.apiresult.ApiResult.Companion.Error +import pro.respawn.apiresult.ApiResult.Companion.Success import pro.respawn.apiresult.ApiResult.Error import pro.respawn.apiresult.ApiResult.Loading -import pro.respawn.apiresult.ApiResult.Success import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.coroutines.cancellation.CancellationException +import kotlin.jvm.JvmField import kotlin.jvm.JvmInline import kotlin.jvm.JvmName /** * A class that represents a result of an operation. * - * This class is **efficient**: no actual objects are created unless dynamic type resolution is required, - * all operations are inlined and no function resolution is performed. + * This class is **efficient**: + * * no actual objects are created, + * * all operations are inlined + * * no function resolution is performed. + * * ApiResult is **not** an Rx-style callback chain - * the operators that are invoked are called **immediately** and in-place. */ -public sealed interface ApiResult { +@JvmInline +public value class ApiResult @PublishedApi internal constructor(@PublishedApi internal val value: Any?) { /** * Get the [Success] component of this result or null @@ -58,47 +64,69 @@ public sealed interface ApiResult { */ public operator fun not(): T = orThrow() - /** - * A value of [ApiResult] for its successful state. - * @param result a successful result value - */ - @JvmInline - public value class Success(public val result: T) : ApiResult { - - override fun toString(): String = "ApiResult.Success: $result" - } - /** * The state of [ApiResult] that represents an error. * @param e wrapped [Exception] */ @JvmInline - public value class Error(public val e: Exception) : ApiResult { + @PublishedApi + internal value class Error(@JvmField val e: Exception) { - override fun toString(): String = "ApiResult.Error: message=$message and cause: $e" + override fun toString(): String = "ApiResult.Error: message=${e.message} and cause: $e" } + @PublishedApi + internal data object Loading + /** * Whether this is [Success] */ - public val isSuccess: Boolean get() = this is Success + public inline val isSuccess: Boolean get() = !isError && !isLoading /** * Whether this is [Error] */ - public val isError: Boolean get() = this is Error + public inline val isError: Boolean get() = value is Error /** * Whether this is [Loading] */ - public val isLoading: Boolean get() = this is Loading + public inline val isLoading: Boolean get() = value is Loading - /** - * A loading state of an [ApiResult] - */ - public data object Loading : ApiResult + override fun toString(): String = when { + value is Error || value is Loading -> value.toString() + else -> "ApiResult.Success: $value" + } public companion object { + + /** + * Create a successful [ApiResult] value + */ + public inline fun Success(value: T): ApiResult = ApiResult(value) + + /** + * Create an error [ApiResult] value + */ + public inline fun Error(value: Exception): ApiResult = ApiResult(Error(e = value)) + + /** + * Create a loading [ApiResult] value + */ + public inline fun Loading(): ApiResult = ApiResult(value = Loading) + + /** + * Create an [ApiResult] instance using the given value. + * When [value] is an Exception, an error result will be created. + * Otherwise, a success result will be created. + * + * If you want to directly create a success value of an [Exception], use [Success] + */ + public inline operator fun invoke(value: T): ApiResult = when (value) { + is Exception -> Error(value = value) + else -> Success(value) + } + /** * Execute [call], catching any exceptions, and wrap it in an [ApiResult]. * @@ -115,19 +143,7 @@ public sealed interface ApiResult { } /** - * * If [T] is an exception, will produce [ApiResult.Error] - * * If [T] is Loading, will produce [ApiResult.Loading] - * * Otherwise [ApiResult.Success]. - * @see asResult - */ - public inline operator fun invoke(value: T): ApiResult = when (value) { - is Loading -> value - is Exception -> Error(value) - else -> Success(value) - } - - /** - * Returns an [Success] (Unit) value. + * Returns an [ApiResult](Unit) value. * Use this for applying operators such as `require` and `mapWrapping` to build chains of operators that should * start with an empty value. */ @@ -138,42 +154,43 @@ public sealed interface ApiResult { /** * [ApiResult.Error.e]'s stack trace as string */ -public inline val Error.stackTrace: String get() = e.stackTraceToString() +public inline val ApiResult<*>.stackTrace: String? get() = exceptionOrNull()?.stackTraceToString() /** * [ApiResult.Error.e]'s cause */ -public inline val Error.cause: Throwable? get() = e.cause +public inline val ApiResult<*>.cause: Throwable? get() = exceptionOrNull()?.cause /** * [ApiResult.Error.e]'s message. */ -public inline val Error.message: String? get() = e.message +public inline val ApiResult<*>.message: String? get() = exceptionOrNull()?.message /** * Execute [block] wrapping it in an [ApiResult] * @see ApiResult.invoke */ -public inline fun T.runResulting(block: T.() -> R): ApiResult = ApiResult { block() } +public inline fun T.runResulting(block: T.() -> R): ApiResult = ApiResult(call = { block() }) /** * Executes [block], wrapping it in an [ApiResult] * @see ApiResult.invoke */ -public inline fun runResulting(block: () -> T): ApiResult = ApiResult { block() } +public inline fun runResulting(block: () -> T): ApiResult = ApiResult(call = { block() }) /** - * Executes [block] if [this] is an [ApiResult.Error], otherwise returns [ApiResult.Success.result] + * Executes [block] if [this] is an [ApiResult.Error], otherwise returns [ApiResult.value] * [Loading] will result in [NotFinishedException] */ +@Suppress("UNCHECKED_CAST") public inline infix fun ApiResult.orElse(block: (e: Exception) -> R): T { contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } - return when (this) { - is Success -> result - is Error -> block(e) + return when (value) { + is Error -> block(value.e) is Loading -> block(NotFinishedException()) + else -> value as T } } @@ -191,17 +208,13 @@ public inline fun ApiResult?.orNull(): T? = this?.or(null) /** * @return exception if [this] is [Error] or null */ -public inline fun ApiResult?.exceptionOrNull(): Exception? = (this as? Error)?.e +public inline fun ApiResult?.exceptionOrNull(): Exception? = (this?.value as? Error)?.e /** * Throws [ApiResult.Error.e], or [NotFinishedException] if the request has not been completed yet. * @see ApiResult.not */ -public inline fun ApiResult.orThrow(): T = when (this) { - is Loading -> throw NotFinishedException() - is Error -> throw e - is Success -> result -} +public inline fun ApiResult.orThrow(): T = orElse { throw it } /** * Throws if [this] result is an [Error] and [Error.e] is of type [T]. Ignores all other exceptions. @@ -214,6 +227,7 @@ public inline fun ApiResult.rethrow(): ApiResult ApiResult.fold( onSuccess: (result: T) -> R, onError: (e: Exception) -> R, @@ -223,10 +237,10 @@ public inline fun ApiResult.fold( callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE) callsInPlace(onError, InvocationKind.AT_MOST_ONCE) } - return when (this) { - is Success -> onSuccess(result) - is Error -> onError(e) + return when (value) { + is Error -> onError(value.e) is Loading -> onLoading?.invoke() ?: onError(NotFinishedException()) + else -> onSuccess(value as T) } } @@ -239,7 +253,7 @@ public inline infix fun ApiResult.onError(block: (Exception) -> Unit): Ap contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } - if (this is Error) block(e) + if (value is Error) block(value.e) return this } @@ -247,12 +261,8 @@ public inline infix fun ApiResult.onError(block: (Exception) -> Unit): Ap * Invoke a given block if [this] is [Error] and it's [Error.e] is of type [E]. */ @JvmName("onErrorTyped") -public inline infix fun ApiResult.onError(block: (E) -> Unit): ApiResult { - contract { - callsInPlace(block, InvocationKind.AT_MOST_ONCE) - } - if (this is Error && e is E) block(e) - return this +public inline infix fun ApiResult.onError(block: (E) -> Unit): ApiResult = onError { + if (it is E) block(it) } /** @@ -264,7 +274,7 @@ public inline infix fun ApiResult.onSuccess(block: (T) -> Unit): ApiResul contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } - if (this is Success) block(result) + if (isSuccess) block(value as T) return this } @@ -277,7 +287,7 @@ public inline infix fun ApiResult.onLoading(block: () -> Unit): ApiResult contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } - if (this is Loading) block() + if (isLoading) block() return this } @@ -302,7 +312,7 @@ public inline fun ApiResult.errorIf( callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) callsInPlace(exception, InvocationKind.AT_MOST_ONCE) } - return if (this is Success && predicate(result)) Error(exception()) else this + return if (isSuccess && predicate(value as T)) Error(value = exception()) else this } /** @@ -313,11 +323,10 @@ public inline fun ApiResult.errorOnLoading( ): ApiResult { contract { callsInPlace(exception, InvocationKind.AT_MOST_ONCE) - returns() implies (this@errorOnLoading !is Loading) } - return when (this) { - is Loading -> Error(exception()) + return when (value) { + is Loading -> Error(value = exception()) else -> this } } @@ -331,7 +340,7 @@ public inline fun ApiResult?.requireNotNull(): ApiResult = erro * Throws if [this] is not [Success] and returns [Success] otherwise. * @see orThrow */ -public inline fun ApiResult.require(): Success = Success(!this) +public inline fun ApiResult.require(): ApiResult = Success(!this) /** * Change the type of the [Success] to [R] without affecting [Error]/[Loading] results @@ -343,11 +352,11 @@ public inline infix fun ApiResult.map(block: (T) -> R): ApiResult { contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } - return when (this) { - is Success -> Success(block(result)) - is Error -> Error(e) - is Loading -> this - } + return fold( + onSuccess = { Success(value = block(it)) }, + onError = { Error(value = it) }, + onLoading = { ApiResult.Loading() } + ) } /** @@ -358,10 +367,10 @@ public inline fun ApiResult.mapOrDefault(default: () -> R, transform: callsInPlace(transform, InvocationKind.AT_MOST_ONCE) callsInPlace(default, InvocationKind.AT_MOST_ONCE) } - return when (this) { - is Success -> transform(result) - else -> default() - } + return fold( + onSuccess = { transform(it) }, + onError = { default() } + ) } /** @@ -370,11 +379,10 @@ public inline fun ApiResult.mapOrDefault(default: () -> R, transform: public inline fun ApiResult.mapEither( success: (T) -> R, error: (Exception) -> Exception, -): ApiResult = when (this) { - is Error -> Error(error(e)) - is Loading -> this - is Success -> Success(success(result)) -} +): ApiResult = fold( + onSuccess = { Success(success(it)) }, + onError = { Error(value = error(it)) } +) /** * Maps [Loading] to a [Success], not affecting other states. @@ -386,10 +394,7 @@ public inline infix fun ApiResult.mapLoading(block: () -> R): ApiR contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } - return when (this) { - is Success, is Error -> this - is Loading -> Success(block()) - } + return if (isLoading) Success(block()) else this } /** @@ -409,7 +414,7 @@ public inline infix fun ApiResult.mapError(block: callsInPlace(block, InvocationKind.AT_MOST_ONCE) } return when { - this is Error && e is R -> Error(block(e)) + value is Error && value.e is R -> Error(value = block(value.e)) else -> this } } @@ -424,8 +429,8 @@ public inline fun ApiResult.mapErrorToCause(): ApiResult = mapError { */ public inline fun ApiResult>.unwrap(): ApiResult = fold( onSuccess = { it }, - onError = { Error(it) }, - onLoading = { Loading } + onError = { Error(value = it) }, + onLoading = { ApiResult.Loading() }, ) /** @@ -435,8 +440,9 @@ public inline fun ApiResult>.unwrap(): ApiResult = fold( * @see mapError * @see mapLoading */ -public inline infix fun ApiResult.tryMap(block: (T) -> R): ApiResult = - map { ApiResult { block(it) } }.unwrap() +public inline infix fun ApiResult.tryMap( + block: (T) -> R +): ApiResult = map { ApiResult(call = { block(it) }) }.unwrap() /** * Make this result an [Error] if [Success] value was null. @@ -450,19 +456,19 @@ public inline fun ApiResult?.errorOnNull( contract { returns() implies (this@errorOnNull != null) } - return this?.errorIf(exception) { it == null }?.map { it!! } ?: Error(exception()) + return when (this?.value) { + is Error -> Error(value = value.e) + is Loading -> ApiResult.Loading() + null -> Error(value = exception()) + else -> ApiResult(value = value) + } } /** * Maps [Error] values to nulls * @see orNull */ -public inline fun ApiResult.nullOnError(): ApiResult { - contract { - returns() implies (this@nullOnError !is Error) - } - return if (this is Error) Success(null) else this -} +public inline fun ApiResult.nullOnError(): ApiResult = if (isError) Success(null) else this /** * Recover from an exception of type [R], else no-op. @@ -475,7 +481,7 @@ public inline fun ApiResult.nullOnError(): ApiResult { public inline infix fun ApiResult.recover( another: (e: T) -> ApiResult ): ApiResult = when { - this is Error && e is T -> another(e) + value is Error && value.e is T -> another(value.e) else -> this } @@ -483,19 +489,16 @@ public inline infix fun ApiResult.recover( * Recover from an exception. Does not affect [Loading] * See also the typed version of this function to recover from a specific exception type */ -public inline infix fun ApiResult.recover(another: (e: Exception) -> ApiResult): ApiResult { - contract { - returns() implies (this@recover !is Error) - } - return recover(another) -} +public inline infix fun ApiResult.recover( + another: (e: Exception) -> ApiResult +): ApiResult = recover(another) /** * calls [recover] catching and wrapping any exceptions thrown inside [block]. */ @JvmName("tryRecoverTyped") public inline infix fun ApiResult.tryRecover(block: (T) -> R): ApiResult = - recover(another = { ApiResult { block(it) } }) + recover(another = { ApiResult(call = { block(it) }) }) /** * Calls [recover] catching and wrapping any exceptions thrown inside [block]. @@ -503,12 +506,7 @@ public inline infix fun ApiResult.tryRecover(block */ public inline infix fun ApiResult.tryRecover( block: (e: Exception) -> T -): ApiResult { - contract { - returns() implies (this@tryRecover !is Error) - } - return tryRecover(block) -} +): ApiResult = tryRecover(block) /** * Recover from an [Error] only if the [condition] is true, else no-op. @@ -518,7 +516,7 @@ public inline infix fun ApiResult.tryRecover( public inline fun ApiResult.tryRecoverIf( condition: (Exception) -> Boolean, block: (Exception) -> T, -): ApiResult = recoverIf(condition) { ApiResult { block(it) } } +): ApiResult = recoverIf(condition) { ApiResult(call = { block(it) }) } /** * Recover from an [Error] only if the [condition] is true, else no-op. @@ -534,8 +532,8 @@ public inline fun ApiResult.recoverIf( callsInPlace(block, InvocationKind.AT_MOST_ONCE) } return when { - this is Error && condition(e) -> block(e) - else -> this + value is Error && condition(value.e) -> block(value.e) + else -> ApiResult(value = value) } } @@ -550,10 +548,7 @@ public inline infix fun ApiResult.chain(another: (T) -> ApiResult<*>): Ap contract { callsInPlace(another, InvocationKind.AT_MOST_ONCE) } - return when (this) { - is Loading, is Error -> this - is Success -> another(result).map { result } - } + return map { outer -> another(outer).map { outer } }.unwrap() } /** @@ -566,8 +561,9 @@ public inline infix fun ApiResult.chain(another: (T) -> ApiResult<*>): Ap * @see [ApiResult.chain] * @see [ApiResult.then] */ -public inline infix fun ApiResult.tryChain(block: (T) -> Unit): ApiResult = - chain(another = { ApiResult { block(it) } }) +public inline infix fun ApiResult.tryChain( + block: (T) -> Unit +): ApiResult = chain(another = { ApiResult(call = { block(it) }) }) /** * Call [another] and if it succeeds, continue with [another]'s result. @@ -628,7 +624,7 @@ public inline infix fun ApiResult.apply(block: T.() -> R): ApiResult ApiResult.requireIs( exception: (T) -> Exception = { value -> - "Result value is of type ${value?.let { it::class.simpleName }} but expected ${R::class}" + "Result value is of type ${value?.let { it::class.simpleName }} but expected ${R::class.simpleName}" .let(::ConditionNotSatisfiedException) }, ): ApiResult = tryMap { value -> diff --git a/core/src/commonMain/kotlin/pro/respawn/apiresult/CollectionResult.kt b/core/src/commonMain/kotlin/pro/respawn/apiresult/CollectionResult.kt index 3e15ef2..d19df1b 100644 --- a/core/src/commonMain/kotlin/pro/respawn/apiresult/CollectionResult.kt +++ b/core/src/commonMain/kotlin/pro/respawn/apiresult/CollectionResult.kt @@ -9,8 +9,6 @@ package pro.respawn.apiresult -import pro.respawn.apiresult.ApiResult.Error -import pro.respawn.apiresult.ApiResult.Success import kotlin.jvm.JvmName /** @@ -126,84 +124,84 @@ public inline infix fun , R> ApiResult.filter( ): ApiResult> = map { it.filter(block) } /** - * Filters only [Error] values + * Returns only error values */ -public inline fun Iterable>.filterErrors(): List = filterIsInstance() +public inline fun Iterable>.filterErrors(): List = mapNotNull { it.exceptionOrNull() } /** - * Filters only [Error] values + * Returns only error values */ -public inline fun Sequence>.filterErrors(): Sequence = filterIsInstance() +public inline fun Sequence>.filterErrors(): Sequence = mapNotNull { it.exceptionOrNull() } /** - * Filters only [Success] values + * Returns only successful values */ -public inline fun Iterable>.filterSuccesses(): List> = filterIsInstance>() +public inline fun Iterable>.filterSuccesses(): List = mapNotNull { it.orNull() } /** - * Filters only [Success] values + * Returns only successful values */ -public inline fun Sequence>.filterSuccesses(): Sequence> = filterIsInstance>() +public inline fun Sequence>.filterSuccesses(): Sequence = mapNotNull { it.orNull() } /** - * Filters all null values of [Success]es + * Filters all null values of results */ public inline fun Iterable>.filterNulls(): List> = - filter { it !is Success || it.result != null }.mapResults { it!! } + filter { !it.isSuccess || it.value != null }.mapResults { it!! } /** - * Filters all null values of [Success]es + * Filters all null values of results */ public inline fun Sequence>.filterNulls(): Sequence> = - filter { it !is Success || it.result != null }.mapResults { it!! } + filter { !it.isSuccess || it.value != null }.mapResults { it!! } /** - * Merges all [Success] results into a single [List], or if any has failed, returns [Error]. + * Merges all results into a single [List], or if any has failed, returns [Error]. */ +@JvmName("merge2") public inline fun Iterable>.merge(): ApiResult> = ApiResult { map { !it } } /** * Merges all [results] into a single [List], or if any has failed, returns [Error]. */ -public inline fun merge(vararg results: ApiResult): ApiResult> = results.asIterable().merge() +public inline fun merge(results: Iterable>): ApiResult> = results.merge() /** * Merges [this] results and all other [results] into a single result of type [T]. */ public inline fun ApiResult.merge( - vararg results: ApiResult + results: Iterable> ): ApiResult> = sequence { yield(this@merge) - yieldAll(results.iterator()) + yieldAll(results) }.asIterable().merge() /** - * Returns a list of only [Success] values, discarding any errors + * Returns a list of only successful values, discarding any errors */ -public inline fun Iterable>.values(): List = asSequence() - .filterSuccesses() - .map { it.result } - .toList() +public inline fun Iterable>.values(): List = filterSuccesses() /** - * Return the first [Success] value, or an [Error] if no success was found + * Return the first successful value, or an [Error] if no success was found + * + * Exception will always be [NoSuchElementException] * - * [Error.e] will always be [NoSuchElementException] * @see firstSuccessOrNull * @see firstSuccessOrThrow */ -public inline fun Iterable>.firstSuccess(): ApiResult = - ApiResult { asSequence().filterIsInstance>().first().result } +public inline fun Iterable>.firstSuccess(): ApiResult = ApiResult { + asSequence().filterSuccesses().first() +} /** - * Return the first [Success] value, or throw if no success was found + * Return the first successful value, or throw if no success was found * @see firstSuccess * @see firstSuccessOrNull */ public inline fun Iterable>.firstSuccessOrThrow(): T = firstSuccess().orThrow() /** - * Return the first [Success] value, or null if no success was found + * Return the first successful value, or null if no success was found * @see firstSuccess * @see firstSuccessOrThrow */ @@ -228,10 +226,11 @@ public inline fun Iterable.mapResulting( * - First is the [ApiResult.Success] results * - Seconds is [ApiResult.Error] or errors produced by [ApiResult.Loading] (see [ApiResult.errorOnLoading] */ +@Suppress("UNCHECKED_CAST") public fun Sequence>.accumulate(): Pair, List> { val (success, other) = partition { it.isSuccess } return Pair( - success.map { (it as Success).result }, + success.map { it.value as T }, other.mapNotNull { it.errorOnLoading().exceptionOrNull() } ) } diff --git a/core/src/commonMain/kotlin/pro/respawn/apiresult/SuspendResult.kt b/core/src/commonMain/kotlin/pro/respawn/apiresult/SuspendResult.kt index b9ad6f5..9aae9a2 100644 --- a/core/src/commonMain/kotlin/pro/respawn/apiresult/SuspendResult.kt +++ b/core/src/commonMain/kotlin/pro/respawn/apiresult/SuspendResult.kt @@ -21,7 +21,6 @@ import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext import pro.respawn.apiresult.ApiResult.Error import pro.respawn.apiresult.ApiResult.Loading -import pro.respawn.apiresult.ApiResult.Success import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.jvm.JvmName @@ -47,7 +46,7 @@ public inline fun Flow.catchExceptions( public suspend inline fun SuspendResult( context: CoroutineContext = EmptyCoroutineContext, noinline block: suspend CoroutineScope.() -> T, -): ApiResult = withContext(context) { ApiResult { supervisorScope(block) } } +): ApiResult = withContext(context) { ApiResult(call = { supervisorScope(block) }) } /** * Emits [ApiResult.Loading], then executes [call] and wraps it. @@ -56,8 +55,8 @@ public suspend inline fun SuspendResult( public inline fun ApiResult.Companion.tryFlow( crossinline call: suspend () -> T ): Flow> = kotlinx.coroutines.flow.flow { - emit(Loading) - emit(ApiResult { call() }) + emit(Loading()) + emit(ApiResult(call = { call() })) } /** @@ -68,23 +67,23 @@ public inline fun ApiResult.Companion.tryFlow( public inline fun ApiResult.Companion.flow( crossinline result: suspend () -> ApiResult, ): Flow> = kotlinx.coroutines.flow.flow { - emit(Loading) + emit(Loading()) emit(result()) } /** * Emits [Loading] before this flow starts to be collected. - * Then maps all values to [Success] and catches [Exception]s and maps them to [Error]s + * Then maps all results to their values, then catches [Exception]s and maps them to [Error]s * @see ApiResult.Companion.flow * @see SuspendResult */ public inline fun Flow.asApiResult(): Flow> = this .map { it.asResult } - .onStart { emit(Loading) } - .catchExceptions { emit(Error(it)) } + .onStart { emit(ApiResult.Loading()) } + .catchExceptions { emit(ApiResult.Error(value = it)) } /** - * Maps each [Success] value of [this] flow using [transform] + * Maps each success value of [this] flow using [transform] */ public inline fun Flow>.mapResults( crossinline transform: suspend (T) -> R