
commonMain.com.skydoves.sandwich.ResponseTransformer.kt Maven / Gradle / Ivy
/*
* Designed and developed by 2020 skydoves (Jaewoong Eum)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("unused", "RedundantVisibilityModifier")
@file:JvmName("ResponseTransformer")
@file:JvmMultifileClass
package com.skydoves.sandwich
import com.skydoves.sandwich.mappers.ApiErrorModelMapper
import com.skydoves.sandwich.mappers.ApiResponseMapper
import com.skydoves.sandwich.mappers.ApiSuccessModelMapper
import com.skydoves.sandwich.operators.ApiResponseOperator
import com.skydoves.sandwich.operators.ApiResponseSuspendOperator
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
/**
* @author skydoves (Jaewoong Eum)
*
* Returns the encapsulated data if this instance represents [ApiResponse.Success] or
* returns null if it is [ApiResponse.Failure.Error] or [ApiResponse.Failure.Exception].
*
* @return The encapsulated data or null.
*/
public fun ApiResponse.getOrNull(): T? {
return when (this) {
is ApiResponse.Success -> data
is ApiResponse.Failure -> null
}
}
/**
* @author skydoves (Jaewoong Eum)
*
* Returns the encapsulated data if this instance represents [ApiResponse.Success] or
* returns the [defaultValue] if it is [ApiResponse.Failure.Error] or [ApiResponse.Failure.Exception].
*
* @return The encapsulated data or [defaultValue].
*/
public fun ApiResponse.getOrElse(defaultValue: T): T {
return when (this) {
is ApiResponse.Success -> data
is ApiResponse.Failure -> defaultValue
}
}
/**
* @author skydoves (Jaewoong Eum)
*
* Returns the encapsulated data if this instance represents [ApiResponse.Success] or
* invokes the lambda [defaultValue] that returns [T] if it is [ApiResponse.Failure.Error] or [ApiResponse.Failure.Exception].
*
* @return The encapsulated data or [defaultValue].
*/
public inline fun ApiResponse.getOrElse(defaultValue: () -> T): T {
return when (this) {
is ApiResponse.Success -> data
is ApiResponse.Failure -> defaultValue()
}
}
/**
* @author skydoves (Jaewoong Eum)
*
* Returns the encapsulated data if this instance represents [ApiResponse.Success] or
* throws the encapsulated Throwable exception if it is [ApiResponse.Failure.Error] or [ApiResponse.Failure.Exception].
*
* @throws RuntimeException if it is [ApiResponse.Failure.Error] or
* the encapsulated Throwable exception if it is [ApiResponse.Failure.Exception.throwable]
*
* @return The encapsulated data.
*/
public fun ApiResponse.getOrThrow(): T {
when (this) {
is ApiResponse.Success -> return data
is ApiResponse.Failure.Error -> throw RuntimeException(message())
is ApiResponse.Failure.Exception -> throw throwable
}
}
/**
* @author skydoves (Jaewoong Eum)
*
* A scope function that would be executed for handling successful responses if the request succeeds.
*
* @param onResult The receiver function that receiving [ApiResponse.Success] if the request succeeds.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
public inline fun ApiResponse.onSuccess(
crossinline onResult: ApiResponse.Success.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Success) {
onResult(this)
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A scope function that would be executed for handling successful responses if the request succeeds with a [ApiSuccessModelMapper].
*
* @param mapper The [ApiSuccessModelMapper] for mapping [ApiResponse.Success] response as a custom [V] instance model.
* @param onResult The receiver function that receiving [ApiResponse.Success] if the request succeeds.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
public inline fun ApiResponse.onSuccess(
mapper: ApiSuccessModelMapper,
crossinline onResult: V.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Success) {
onResult(map(mapper))
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A suspension scope function that would be executed for handling successful responses if the request succeeds.
*
* @param onResult The receiver function that receiving [ApiResponse.Success] if the request succeeds.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.suspendOnSuccess(
crossinline onResult: suspend ApiResponse.Success.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Success) {
onResult(this)
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A suspension scope function that would be executed for handling successful responses if the request succeeds with a [ApiSuccessModelMapper].
*
* @param mapper The [ApiSuccessModelMapper] for mapping [ApiResponse.Success] response as a custom [V] instance model.
* @param onResult The receiver function that receiving [ApiResponse.Success] if the request succeeds.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.suspendOnSuccess(
mapper: ApiSuccessModelMapper,
crossinline onResult: suspend V.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Success) {
onResult(map(mapper))
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A function that would be executed for handling error responses if the request failed or get an exception.
*
* @param onResult The receiver function that receiving [ApiResponse.Failure] if the request failed or get an exception.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
public inline fun ApiResponse.onFailure(
crossinline onResult: ApiResponse.Failure.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Failure) {
onResult(this)
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A suspension function that would be executed for handling error responses if the request failed or get an exception.
*
* @param onResult The receiver function that receiving [ApiResponse.Failure] if the request failed or get an exception.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.suspendOnFailure(
crossinline onResult: suspend ApiResponse.Failure.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Failure) {
onResult(this)
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A scope function that would be executed for handling error responses if the request failed.
*
* @param onResult The receiver function that receiving [ApiResponse.Failure.Exception] if the request failed.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
public inline fun ApiResponse.onError(
crossinline onResult: ApiResponse.Failure.Error.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Failure.Error) {
onResult(this)
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A scope function that would be executed for handling error responses if the request failed with a [ApiErrorModelMapper].
* This function receives a [ApiErrorModelMapper] and returns the mapped result into the scope.
*
* @param mapper The [ApiErrorModelMapper] for mapping [ApiResponse.Failure.Error] response as a custom [V] instance model.
* @param onResult The receiver function that receiving [ApiResponse.Failure.Exception] if the request failed.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
public inline fun ApiResponse.onError(
mapper: ApiErrorModelMapper,
crossinline onResult: V.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Failure.Error) {
onResult(map(mapper))
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A suspension scope function that would be executed for handling error responses if the request failed.
*
* @param onResult The receiver function that receiving [ApiResponse.Failure.Exception] if the request failed.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.suspendOnError(
crossinline onResult: suspend ApiResponse.Failure.Error.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Failure.Error) {
onResult(this)
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A suspension scope function that would be executed for handling error responses if the request failed with a [ApiErrorModelMapper].
* This function receives a [ApiErrorModelMapper] and returns the mapped result into the scope.
*
* @param mapper The [ApiErrorModelMapper] for mapping [ApiResponse.Failure.Error] response as a custom [V] instance model.
* @param onResult The receiver function that receiving [ApiResponse.Failure.Exception] if the request failed.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.suspendOnError(
mapper: ApiErrorModelMapper,
crossinline onResult: suspend V.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Failure.Error) {
onResult(map(mapper))
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A scope function that would be executed for handling exception responses if the request get an exception.
*
* @param onResult The receiver function that receiving [ApiResponse.Failure.Exception] if the request get an exception.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
public inline fun ApiResponse.onException(
crossinline onResult: ApiResponse.Failure.Exception.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Failure.Exception) {
onResult(this)
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A suspension scope function that would be executed for handling exception responses if the request get an exception.
*
* @param onResult The receiver function that receiving [ApiResponse.Failure.Exception] if the request get an exception.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.suspendOnException(
crossinline onResult: suspend ApiResponse.Failure.Exception.() -> Unit,
): ApiResponse {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Failure.Exception) {
onResult(this)
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A scope function that will be executed for handling successful, error, exception responses.
* This function receives and handles [ApiResponse.onSuccess], [ApiResponse.onError],
* and [ApiResponse.onException] in one scope.
*
* @param onSuccess A scope function that would be executed for handling successful responses if the request succeeds.
* @param onError A scope function that would be executed for handling error responses if the request failed.
* @param onException A scope function that would be executed for handling exception responses if the request get an exception.
*
* @return The original [ApiResponse].
*/
@JvmSynthetic
public inline fun ApiResponse.onProcedure(
crossinline onSuccess: ApiResponse.Success.() -> Unit,
crossinline onError: ApiResponse.Failure.Error.() -> Unit,
crossinline onException: ApiResponse.Failure.Exception.() -> Unit,
): ApiResponse {
contract {
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
callsInPlace(onError, InvocationKind.AT_MOST_ONCE)
callsInPlace(onException, InvocationKind.AT_MOST_ONCE)
}
this.onSuccess(onSuccess)
this.onError(onError)
this.onException(onException)
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* A suspension scope function that will be executed for handling successful, error, exception responses.
* This function receives and handles [ApiResponse.onSuccess], [ApiResponse.onError],
* and [ApiResponse.onException] in one scope.
*
* @param onSuccess A suspension scope function that would be executed for handling successful responses if the request succeeds.
* @param onError A suspension scope function that would be executed for handling error responses if the request failed.
* @param onException A suspension scope function that would be executed for handling exception responses if the request get an exception.
* @return The original [ApiResponse].
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.suspendOnProcedure(
crossinline onSuccess: suspend ApiResponse.Success.() -> Unit,
crossinline onError: suspend ApiResponse.Failure.Error.() -> Unit,
crossinline onException: suspend ApiResponse.Failure.Exception.() -> Unit,
): ApiResponse {
contract {
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
callsInPlace(onError, InvocationKind.AT_MOST_ONCE)
callsInPlace(onException, InvocationKind.AT_MOST_ONCE)
}
this.suspendOnSuccess(onSuccess)
this.suspendOnError(onError)
this.suspendOnException(onException)
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps a [T] type of the [ApiResponse] to a [V] type of the [ApiResponse].
*
* @param transformer A transformer that receives [T] and returns [V].
*
* @return A [V] type of the [ApiResponse].
*/
public inline fun ApiResponse.flatMap(
crossinline transformer: ApiResponse.() -> ApiResponse,
): ApiResponse {
return transformer.invoke(this)
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps a [T] type of the [ApiResponse] to a [V] type of the [ApiResponse].
*
* @param transformer A transformer that receives [T] and returns [V].
*
* @return A [V] type of the [ApiResponse].
*/
public suspend inline fun ApiResponse.suspendFlatMap(
crossinline transformer: suspend ApiResponse.() -> ApiResponse,
): ApiResponse {
return transformer.invoke(this)
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps a [T] type of the [ApiResponse] to a [V] type of the [ApiResponse].
*
* @param mapper A transformer that receives [T] and returns [V].
*
* @return A [V] type of the [ApiResponse].
*/
public inline fun ApiResponse.flatmap(
mapper: ApiResponseMapper,
): ApiResponse {
return mapper.map(this)
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps a [T] type of the [ApiResponse] to a [V] type of the [ApiResponse] if the [ApiResponse] is [ApiResponse.Success].
*
* @param transformer A transformer that receives [T] and returns [V].
*
* @return A [V] type of the [ApiResponse].
*/
@Suppress("UNCHECKED_CAST")
public inline fun ApiResponse.mapSuccess(
crossinline transformer: T.() -> V,
): ApiResponse {
contract { callsInPlace(transformer, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Success) {
return ApiResponse.of(tag = tag) { transformer(data) }
}
return this as ApiResponse
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps a [T] type of the [ApiResponse] to a [V] type of the [ApiResponse] if the [ApiResponse] is [ApiResponse.Success].
*
* @param transformer A suspend transformer that receives [T] and returns [V].
*
* @return A [V] type of the [ApiResponse].
*/
@JvmSynthetic
@SuspensionFunction
@Suppress("UNCHECKED_CAST")
public suspend inline fun ApiResponse.suspendMapSuccess(
crossinline transformer: suspend T.() -> V,
): ApiResponse {
if (this is ApiResponse.Success) {
val invoke = transformer(data)
return ApiResponse.of(tag = tag) { invoke }
}
return this as ApiResponse
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [Any] type of the [ApiResponse.Failure.Error.payload] to another Any type.
*
* @param transformer A transformer that receives [Any] and returns [Any].
*
* @return A [T] type of the [ApiResponse].
*/
public fun ApiResponse.mapFailure(transformer: Any?.() -> Any?): ApiResponse {
contract { callsInPlace(transformer, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Failure.Error) {
return ApiResponse.Failure.Error(payload = transformer.invoke(payload))
} else if (this is ApiResponse.Failure.Exception) {
return ApiResponse.exception(ex = (transformer.invoke(throwable) as? Throwable) ?: throwable)
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [Any] type of the [ApiResponse.Failure.Error.payload] to another Any type.
*
* @param transformer A transformer that receives [Any] and returns [Any].
*
* @return A [T] type of the [ApiResponse].
*/
@JvmSynthetic
@SuspensionFunction
public suspend fun ApiResponse.suspendMapFailure(
transformer: suspend Any?.() -> Any?,
): ApiResponse {
contract { callsInPlace(transformer, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Failure.Error) {
return ApiResponse.Failure.Error(payload = transformer.invoke(payload))
} else if (this is ApiResponse.Failure.Exception) {
return ApiResponse.exception(ex = (transformer.invoke(throwable) as? Throwable) ?: throwable)
}
return this
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [ApiResponse.Success] to a customized success response model.
*
* @param mapper A mapper interface for mapping [ApiResponse.Success] response as a custom [V] instance model.
*
* @return A mapped custom [V] error response model.
*/
public fun ApiResponse.Success.map(mapper: ApiSuccessModelMapper): V {
return mapper.map(this)
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [ApiResponse.Success] to a customized success response model.
*
* @param mapper An executable lambda for mapping [ApiResponse.Success] response as a custom [V] instance model.
*
* @return A mapped custom [V] error response model.
*/
public fun ApiResponse.Success.map(mapper: (ApiResponse.Success) -> V): V {
contract { callsInPlace(mapper, InvocationKind.AT_MOST_ONCE) }
return mapper(this)
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [ApiResponse.Success] to a customized error response model with a receiver scope lambda.
*
* @param mapper A mapper interface for mapping [ApiResponse.Success] response as a custom [V] instance model.
* @param onResult A receiver scope lambda of the mapped custom [V] success response model.
*
* @return A mapped custom [V] success response model.
*/
@JvmSynthetic
public inline fun ApiResponse.Success.map(
mapper: ApiSuccessModelMapper,
crossinline onResult: V.() -> Unit,
) {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
onResult(mapper.map(this))
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [ApiResponse.Success] to a customized error response model with a suspension receiver scope lambda.
*
* @param mapper A mapper interface for mapping [ApiResponse.Success] response as a custom [V] instance model.
* @param onResult A suspension receiver scope lambda of the mapped custom [V] success response model.
*
* @return A mapped custom [V] success response model.
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.Success.suspendMap(
mapper: ApiSuccessModelMapper,
crossinline onResult: suspend V.() -> Unit,
) {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
onResult(mapper.map(this))
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [ApiResponse.Success] to a customized error response model with a suspension receiver scope lambda.
*
* @param mapper An executable lambda for mapping [ApiResponse.Success] response as a custom [V] instance model.
*
* @return A mapped custom [V] success response model.
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.Success.suspendMap(
crossinline mapper: suspend (ApiResponse.Success) -> V,
): V {
contract { callsInPlace(mapper, InvocationKind.AT_MOST_ONCE) }
return mapper(this)
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [ApiResponse.Failure.Error] to a customized error response model.
*
* @param mapper A mapper interface for mapping [ApiResponse.Failure.Error] response as a custom [T] instance model.
*
* @return A mapped custom [T] error response model.
*/
public fun ApiResponse.Failure.Error.map(mapper: ApiErrorModelMapper): T {
return mapper.map(this)
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [ApiResponse.Failure.Error] to a customized error response model.
*
* @param mapper An executable lambda for mapping [ApiResponse.Failure.Error] response as a custom [T] instance model.
*
* @return A mapped custom [T] error response model.
*/
public fun ApiResponse.Failure.Error.map(mapper: (ApiResponse.Failure.Error) -> T): T {
contract { callsInPlace(mapper, InvocationKind.AT_MOST_ONCE) }
return mapper(this)
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [ApiResponse.Failure.Error] to a customized error response model with a receiver scope lambda.
*
* @param mapper A mapper interface for mapping [ApiResponse.Failure.Error] response as a custom [T] instance model.
* @param onResult A receiver scope lambda of the mapped custom [T] error response model.
*
* @return A mapped custom [T] error response model.
*/
@JvmSynthetic
public inline fun ApiResponse.Failure.Error.map(
mapper: ApiErrorModelMapper,
crossinline onResult: T.() -> Unit,
) {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
onResult(mapper.map(this))
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [ApiResponse.Failure.Error] to a customized error response model with a suspension receiver scope lambda.
*
* @param mapper A mapper interface for mapping [ApiResponse.Failure.Error] response as a custom [T] instance model.
* @param onResult A suspension receiver scope lambda of the mapped custom [T] error response model.
*
* @return A mapped custom [T] error response model.
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.Failure.Error.suspendMap(
mapper: ApiErrorModelMapper,
crossinline onResult: suspend T.() -> Unit,
) {
contract { callsInPlace(onResult, InvocationKind.AT_MOST_ONCE) }
onResult(mapper.map(this))
}
/**
* @author skydoves (Jaewoong Eum)
*
* Maps [ApiResponse.Failure.Error] to a customized error response model with a suspension receiver scope lambda.
*
* @param mapper A mapper interface for mapping [ApiResponse.Failure.Error] response as a custom [T] instance model.
*
* @return A mapped custom [T] error response model.
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.Failure.Error.suspendMap(
crossinline mapper: suspend (ApiResponse.Failure.Error) -> T,
): T {
return mapper(this)
}
/**
* @author skydoves (Jaewoong Eum)
*
* Returns the tag value if this instance represents [ApiResponse.Success].
*
* @return The encapsulated data.
*/
public fun ApiResponse.tagOrNull(): Any? {
return if (this is ApiResponse.Success) {
tag
} else {
null
}
}
/**
* Returns an error message from the [ApiResponse.Failure] that consists of the localized message.
*
* @return An error message from the [ApiResponse.Failure].
*/
public fun ApiResponse.Failure.message(): String {
return when (this) {
is ApiResponse.Failure.Error -> message()
is ApiResponse.Failure.Exception -> message()
}
}
/**
* @author skydoves (Jaewoong Eum)
*
* Returns an error message from the [ApiResponse.Failure.Error] that consists of the status and error response.
*
* @return An error message from the [ApiResponse.Failure.Error].
*/
public fun ApiResponse.Failure.Error.message(): String = toString()
/**
* Returns an error message from the [ApiResponse.Failure.Exception] that consists of the localized message.
*
* @return An error message from the [ApiResponse.Failure.Exception].
*/
public fun ApiResponse.Failure.Exception.message(): String = toString()
/**
* @author skydoves (Jaewoong Eum)
*
* Operates on an [ApiResponse] and return an [ApiResponse].
* This allows you to handle success and error response instead of the [ApiResponse.onSuccess],
* [ApiResponse.onError], [ApiResponse.onException] transformers.
*/
@JvmSynthetic
public fun > ApiResponse.operator(
apiResponseOperator: V,
): ApiResponse = apply {
when (this) {
is ApiResponse.Success -> apiResponseOperator.onSuccess(this)
is ApiResponse.Failure.Error -> apiResponseOperator.onError(this)
is ApiResponse.Failure.Exception -> apiResponseOperator.onException(this)
}
}
/**
* @author skydoves (Jaewoong Eum)
*
* Operates on an [ApiResponse] and return an [ApiResponse] which should be handled in the suspension scope.
* This allows you to handle success and error response instead of the [ApiResponse.suspendOnSuccess],
* [ApiResponse.suspendOnError], [ApiResponse.suspendOnException] transformers.
*/
@JvmSynthetic
@SuspensionFunction
public suspend fun > ApiResponse.suspendOperator(
apiResponseOperator: V,
): ApiResponse = apply {
when (this) {
is ApiResponse.Success -> apiResponseOperator.onSuccess(this)
is ApiResponse.Failure.Error -> apiResponseOperator.onError(this)
is ApiResponse.Failure.Exception -> apiResponseOperator.onException(this)
}
}
/**
* @author skydoves (Jaewoong Eum)
*
* Merges multiple [ApiResponse]s as one [ApiResponse] depending on the policy, [ApiResponseMergePolicy].
* The default policy is [ApiResponseMergePolicy.IGNORE_FAILURE].
*
* @param responses Responses for merging as one [ApiResponse].
* @param mergePolicy A policy for merging response data depend on the success or not.
*
* @return [ApiResponse] that depends on the [ApiResponseMergePolicy].
*/
@JvmSynthetic
public fun ApiResponse>.merge(
vararg responses: ApiResponse>,
mergePolicy: ApiResponseMergePolicy = ApiResponseMergePolicy.IGNORE_FAILURE,
): ApiResponse> {
val apiResponses = responses.toMutableList()
apiResponses.add(0, this)
var apiResponse: ApiResponse> = ApiResponse.of(tag = tagOrNull()) { mutableListOf() }
val data: MutableList = mutableListOf()
for (response in apiResponses) {
if (response is ApiResponse.Success) {
data.addAll(response.data)
apiResponse = ApiResponse.Success(data = data, tag = response.tag)
} else if (mergePolicy === ApiResponseMergePolicy.PREFERRED_FAILURE) {
return response
}
}
return apiResponse
}
/**
* @author skydoves (Jaewoong Eum)
*
* Returns a [Flow] which emits successful data if the response is a [ApiResponse.Success] and the data is not null.
*
* @return A coroutines [Flow] which emits successful data.
*/
@JvmSynthetic
public fun ApiResponse.toFlow(): Flow {
return if (this is ApiResponse.Success) {
flowOf(data)
} else {
emptyFlow()
}
}
/**
* @author skydoves (Jaewoong Eum)
*
* Returns a [Flow] which contains transformed data using successful data
* if the response is a [ApiResponse.Success] and the data is not null.
*
* @param transformer A transformer lambda receives successful data and returns anything.
*
* @return A coroutines [Flow] which emits successful data.
*/
@JvmSynthetic
public inline fun ApiResponse.toFlow(crossinline transformer: T.() -> R): Flow {
contract { callsInPlace(transformer, InvocationKind.AT_MOST_ONCE) }
return if (this is ApiResponse.Success) {
flowOf(data.transformer())
} else {
emptyFlow()
}
}
/**
* @author skydoves (Jaewoong Eum)
*
* Returns a [Flow] which contains transformed data using successful data
* if the response is a [ApiResponse.Success] and the data is not null.
*
* @param transformer A suspension transformer lambda receives successful data and returns anything.
*
* @return A coroutines [Flow] which emits successful data.
*/
@JvmSynthetic
@SuspensionFunction
public suspend inline fun ApiResponse.toSuspendFlow(
crossinline transformer: suspend T.() -> R,
): Flow {
contract { callsInPlace(transformer, InvocationKind.AT_MOST_ONCE) }
return if (this is ApiResponse.Success) {
flowOf(data.transformer())
} else {
emptyFlow()
}
}
/**
* @author skydoves (Jaewoong Eum)
*
* Composition the [ApiResponse] with a given [ApiResponse] from the [transformer].
*
* If the give [ApiResponse] success, execute [transformer] for executing the next request.
* If the give [ApiResponse] failed, it returns the first [ApiResponse.Failure] response.
*
* @param transformer A transformer lambda receives successful data and returns anything.
*
* @return A mapped custom [V] success response or failed response depending on the given [ApiResponse].
*/
@Suppress("UNCHECKED_CAST")
@JvmSynthetic
public inline infix fun ApiResponse.then(
transformer: (T) -> ApiResponse,
): ApiResponse {
contract { callsInPlace(transformer, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Success) {
return transformer(this.data)
}
return this as ApiResponse
}
/**
* @author skydoves (Jaewoong Eum)
*
* Composition the [ApiResponse] with a given [ApiResponse] from the [transformer].
*
* If the give [ApiResponse] success, execute [transformer] for executing the next request.
* If the give [ApiResponse] failed, it returns the first [ApiResponse.Failure] response.
*
* @param transformer A transformer lambda receives successful data and returns anything.
*
* @return A mapped custom [V] success response or failed response depending on the given [ApiResponse].
*/
@Suppress("UNCHECKED_CAST")
@JvmSynthetic
public suspend inline infix fun ApiResponse.suspendThen(
crossinline transformer: suspend (T) -> ApiResponse,
): ApiResponse {
contract { callsInPlace(transformer, InvocationKind.AT_MOST_ONCE) }
if (this is ApiResponse.Success) {
return transformer(this.data)
}
return this as ApiResponse
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy