/* * 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")
packagekotlinimportkotlin.contracts.*importkotlin.internal.InlineOnlyimportkotlin.jvm.JvmFieldimportkotlin.jvm.JvmInlineimportkotlin.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
publicvalue classResult<outT> @PublishedApi internal constructor(
@PublishedApi
internalvalvalue:Any?
) : Serializable {
// discovery/** * Returns `true` if this instance represents a successful outcome. * In this case [isFailure] returns `false`.*/publicval isSuccess:Boolean get() = value !isFailure/** * Returns `true` if this instance represents a failed outcome. * In this case [isSuccess] returns `false`.*/publicval isFailure:Boolean get() = value isFailure// 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
publicinlinefungetOrNull(): T?=when {
isFailure ->nullelse-> value asT
}
/** * 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]).*/publicfunexceptionOrNull(): Throwable?=when (value) {
isFailure-> 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.*/publicoverridefuntoString(): String=when (value) {
isFailure-> value.toString() // "Failure($exception)"else->"Success($value)"
}
// companion with constructors/** * Companion object for [Result] class that contains its constructor functions * [success] and [failure].*/publiccompanionobject {
/** * Returns an instance that encapsulates the given [value] as successful value.*/
@Suppress("INAPPLICABLE_JVM_NAME")
@InlineOnly
@JvmName("success")
publicinlinefun <T> success(value:T): Result<T> =Result(value)
/** * Returns an instance that encapsulates the given [Throwable] [exception] as failure.*/
@Suppress("INAPPLICABLE_JVM_NAME")
@InlineOnly
@JvmName("failure")
publicinlinefun <T> failure(exception:Throwable): Result<T> =Result(createFailure(exception))
}
internalclassFailure(
@JvmField
valexception:Throwable
) : Serializable {
overridefunequals(other:Any?): Boolean= other isFailure&& exception == other.exception
overridefunhashCode(): Int= exception.hashCode()
overridefuntoString(): 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")
internalfuncreateFailure(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")
internalfunResult<*>.throwOnFailure() {
if (value isResult.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")
publicinlinefun <R> runCatching(block: () ->R): Result<R> {
returntry {
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")
publicinlinefun <T, R> T.runCatching(block:T.() ->R): Result<R> {
returntry {
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")
publicinlinefun <T> Result<T>.getOrThrow(): T {
throwOnFailure()
return value asT
}
/** * 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")
publicinlinefun <R, T:R> Result<T>.getOrElse(onFailure: (exception: Throwable) ->R): R {
contract {
callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
}
returnwhen (val exception = exceptionOrNull()) {
null-> value asTelse-> 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")
publicinlinefun <R, T:R> Result<T>.getOrDefault(defaultValue:R): R {
if (isFailure) return defaultValue
return value asT
}
/** * 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")
publicinlinefun <R, T> Result<T>.fold(
onSuccess: (value: T) ->R,
onFailure: (exception: Throwable) ->R
): R {
contract {
callsInPlace(onSuccess, InvocationKind.AT_MOST_ONCE)
callsInPlace(onFailure, InvocationKind.AT_MOST_ONCE)
}
returnwhen (val exception = exceptionOrNull()) {
null-> onSuccess(value asT)
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")
publicinlinefun <R, T> Result<T>.map(transform: (value: T) ->R): Result<R> {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
returnwhen {
isSuccess ->Result.success(transform(value asT))
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")
publicinlinefun <R, T> Result<T>.mapCatching(transform: (value: T) ->R): Result<R> {
returnwhen {
isSuccess -> runCatching { transform(value asT) }
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")
publicinlinefun <R, T:R> Result<T>.recover(transform: (exception: Throwable) ->R): Result<R> {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
returnwhen (val exception = exceptionOrNull()) {
null->thiselse->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")
publicinlinefun <R, T:R> Result<T>.recoverCatching(transform: (exception: Throwable) ->R): Result<R> {
returnwhen (val exception = exceptionOrNull()) {
null->thiselse-> 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")
publicinlinefun <T> Result<T>.onFailure(action: (exception: Throwable) ->Unit): Result<T> {
contract {
callsInPlace(action, InvocationKind.AT_MOST_ONCE)
}
exceptionOrNull()?.let { action(it) }
returnthis
}
/** * Performs the given [action] on the encapsulated value if this instance represents [success][Result.isSuccess]. * Returns the original `Result` unchanged.*/
@InlineOnly
@SinceKotlin("1.3")
publicinlinefun <T> Result<T>.onSuccess(action: (value: T) ->Unit): Result<T> {
contract {
callsInPlace(action, InvocationKind.AT_MOST_ONCE)
}
if (isSuccess) action(value asT)
returnthis
}
// -------------------