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

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