commonMain.kotlin.util.Result.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-stdlib Show documentation
Show all versions of kotlin-stdlib Show documentation
Kotlin Standard Library for JVM
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("UNCHECKED_CAST", "RedundantVisibilityModifier")
package kotlin
import kotlin.contracts.*
import kotlin.internal.InlineOnly
import kotlin.jvm.JvmField
import kotlin.jvm.JvmInline
import kotlin.jvm.JvmName
/**
* A discriminated union that encapsulates a successful outcome with a value of type [T]
* or a failure with an arbitrary [Throwable] exception.
*/
@SinceKotlin("1.3")
@JvmInline
public value class Result @PublishedApi internal constructor(
@PublishedApi
internal val value: Any?
) : Serializable {
// discovery
/**
* Returns `true` if this instance represents a successful outcome.
* In this case [isFailure] returns `false`.
*/
public val isSuccess: Boolean get() = value !is Failure
/**
* Returns `true` if this instance represents a failed outcome.
* In this case [isSuccess] returns `false`.
*/
public val isFailure: Boolean get() = value is Failure
// value & exception retrieval
/**
* Returns the encapsulated value if this instance represents [success][Result.isSuccess] or `null`
* if it is [failure][Result.isFailure].
*
* This function is a shorthand for `getOrElse { null }` (see [getOrElse]) or
* `fold(onSuccess = { it }, onFailure = { null })` (see [fold]).
*/
@InlineOnly
public inline fun getOrNull(): T? =
when {
isFailure -> null
else -> value as T
}
/**
* Returns the encapsulated [Throwable] exception if this instance represents [failure][isFailure] or `null`
* if it is [success][isSuccess].
*
* This function is a shorthand for `fold(onSuccess = { null }, onFailure = { it })` (see [fold]).
*/
public fun exceptionOrNull(): Throwable? =
when (value) {
is Failure -> value.exception
else -> null
}
/**
* Returns a string `Success(v)` if this instance represents [success][Result.isSuccess]
* where `v` is a string representation of the value or a string `Failure(x)` if
* it is [failure][isFailure] where `x` is a string representation of the exception.
*/
public override fun toString(): String =
when (value) {
is Failure -> value.toString() // "Failure($exception)"
else -> "Success($value)"
}
// companion with constructors
/**
* Companion object for [Result] class that contains its constructor functions
* [success] and [failure].
*/
public companion object {
/**
* Returns an instance that encapsulates the given [value] as successful value.
*/
@Suppress("INAPPLICABLE_JVM_NAME")
@InlineOnly
@JvmName("success")
public inline fun success(value: T): Result =
Result(value)
/**
* Returns an instance that encapsulates the given [Throwable] [exception] as failure.
*/
@Suppress("INAPPLICABLE_JVM_NAME")
@InlineOnly
@JvmName("failure")
public inline fun failure(exception: Throwable): Result =
Result(createFailure(exception))
}
internal class Failure(
@JvmField
val exception: Throwable
) : Serializable {
override fun equals(other: Any?): Boolean = other is Failure && exception == other.exception
override fun hashCode(): Int = exception.hashCode()
override fun toString(): String = "Failure($exception)"
}
}
/**
* Creates an instance of internal marker [Result.Failure] class to
* make sure that this class is not exposed in ABI.
*/
@PublishedApi
@SinceKotlin("1.3")
internal fun createFailure(exception: Throwable): Any =
Result.Failure(exception)
/**
* Throws exception if the result is failure. This internal function minimizes
* inlined bytecode for [getOrThrow] and makes sure that in the future we can
* add some exception-augmenting logic here (if needed).
*/
@PublishedApi
@SinceKotlin("1.3")
internal fun Result<*>.throwOnFailure() {
if (value is Result.Failure) throw value.exception
}
/**
* Calls the specified function [block] and returns its encapsulated result if invocation was successful,
* catching any [Throwable] exception that was thrown from the [block] function execution and encapsulating it as a failure.
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun runCatching(block: () -> R): Result {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
/**
* Calls the specified function [block] with `this` value as its receiver and returns its encapsulated result if invocation was successful,
* catching any [Throwable] exception that was thrown from the [block] function execution and encapsulating it as a failure.
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun T.runCatching(block: T.() -> R): Result {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
// -- extensions ---
/**
* Returns the encapsulated value if this instance represents [success][Result.isSuccess] or throws the encapsulated [Throwable] exception
* if it is [failure][Result.isFailure].
*
* This function is a shorthand for `getOrElse { throw it }` (see [getOrElse]).
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun Result.getOrThrow(): T {
throwOnFailure()
return value as T
}
/**
* Returns the encapsulated value if this instance represents [success][Result.isSuccess] or the
* result of [onFailure] function for the encapsulated [Throwable] exception if it is [failure][Result.isFailure].
*
* Note, that this function rethrows any [Throwable] exception thrown by [onFailure] function.
*
* This function is a shorthand for `fold(onSuccess = { it }, onFailure = onFailure)` (see [fold]).
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun Result.getOrElse(onFailure: (exception: Throwable) -> R): R {
contract {
callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
}
return when (val exception = exceptionOrNull()) {
null -> value as T
else -> onFailure(exception)
}
}
/**
* Returns the encapsulated value if this instance represents [success][Result.isSuccess] or the
* [defaultValue] if it is [failure][Result.isFailure].
*
* This function is a shorthand for `getOrElse { defaultValue }` (see [getOrElse]).
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun Result.getOrDefault(defaultValue: R): R {
if (isFailure) return defaultValue
return value as T
}
/**
* Returns the result of [onSuccess] for the encapsulated value if this instance represents [success][Result.isSuccess]
* or the result of [onFailure] function for the encapsulated [Throwable] exception if it is [failure][Result.isFailure].
*
* Note, that this function rethrows any [Throwable] exception thrown by [onSuccess] or by [onFailure] function.
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun Result.fold(
onSuccess: (value: T) -> R,
onFailure: (exception: Throwable) -> R
): R {
contract {
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
}
return when (val exception = exceptionOrNull()) {
null -> onSuccess(value as T)
else -> onFailure(exception)
}
}
// transformation
/**
* Returns the encapsulated result of the given [transform] function applied to the encapsulated value
* if this instance represents [success][Result.isSuccess] or the
* original encapsulated [Throwable] exception if it is [failure][Result.isFailure].
*
* Note, that this function rethrows any [Throwable] exception thrown by [transform] function.
* See [mapCatching] for an alternative that encapsulates exceptions.
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun Result.map(transform: (value: T) -> R): Result {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when {
isSuccess -> Result.success(transform(value as T))
else -> Result(value)
}
}
/**
* Returns the encapsulated result of the given [transform] function applied to the encapsulated value
* if this instance represents [success][Result.isSuccess] or the
* original encapsulated [Throwable] exception if it is [failure][Result.isFailure].
*
* This function catches any [Throwable] exception thrown by [transform] function and encapsulates it as a failure.
* See [map] for an alternative that rethrows exceptions from `transform` function.
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun Result.mapCatching(transform: (value: T) -> R): Result {
return when {
isSuccess -> runCatching { transform(value as T) }
else -> Result(value)
}
}
/**
* Returns the encapsulated result of the given [transform] function applied to the encapsulated [Throwable] exception
* if this instance represents [failure][Result.isFailure] or the
* original encapsulated value if it is [success][Result.isSuccess].
*
* Note, that this function rethrows any [Throwable] exception thrown by [transform] function.
* See [recoverCatching] for an alternative that encapsulates exceptions.
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun Result.recover(transform: (exception: Throwable) -> R): Result {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (val exception = exceptionOrNull()) {
null -> this
else -> Result.success(transform(exception))
}
}
/**
* Returns the encapsulated result of the given [transform] function applied to the encapsulated [Throwable] exception
* if this instance represents [failure][Result.isFailure] or the
* original encapsulated value if it is [success][Result.isSuccess].
*
* This function catches any [Throwable] exception thrown by [transform] function and encapsulates it as a failure.
* See [recover] for an alternative that rethrows exceptions.
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun Result.recoverCatching(transform: (exception: Throwable) -> R): Result {
return when (val exception = exceptionOrNull()) {
null -> this
else -> runCatching { transform(exception) }
}
}
// "peek" onto value/exception and pipe
/**
* Performs the given [action] on the encapsulated [Throwable] exception if this instance represents [failure][Result.isFailure].
* Returns the original `Result` unchanged.
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun Result.onFailure(action: (exception: Throwable) -> Unit): Result {
contract {
callsInPlace(action, InvocationKind.AT_MOST_ONCE)
}
exceptionOrNull()?.let { action(it) }
return this
}
/**
* Performs the given [action] on the encapsulated value if this instance represents [success][Result.isSuccess].
* Returns the original `Result` unchanged.
*/
@InlineOnly
@SinceKotlin("1.3")
public inline fun Result.onSuccess(action: (value: T) -> Unit): Result {
contract {
callsInPlace(action, InvocationKind.AT_MOST_ONCE)
}
if (isSuccess) action(value as T)
return this
}
// -------------------