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

io.getstream.result.Result.kt Maven / Gradle / Ivy

There is a newer version: 1.3.2
Show newest version
/*
 * Copyright (c) 2014-2022 Stream.io Inc. All rights reserved.
 *
 * 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.
 */
package io.getstream.result

/**
 *  A class which encapsulates a successful outcome with a value of type [A] or a failure with [Error].
 */
public sealed class Result {

  /**
   * Checks if the result is a [Success].
   */
  public val isSuccess: Boolean
    inline get() = this is Success

  /**
   * Check if the result is a [Failure].
   */
  public val isFailure: Boolean
    inline get() = this is Failure

  /**
   * Returns the encapsulated value if this instance represents [Success] [Result.isSuccess] or `null`
   * if it is [Failure] [Result.isFailure].
   */
  public fun getOrNull(): A? = when (this) {
    is Success -> value
    is Failure -> null
  }

  /**
   * Returns the encapsulated value if this instance represents [Success] [Result.isSuccess]
   * or throws the [IllegalStateException] exception if it is [Failure] [Result.isFailure].
   */
  @Throws(IllegalStateException::class)
  public fun getOrThrow(): A = when (this) {
    is Success -> value
    is Failure -> throw IllegalStateException("The Success::value cannot be accessed as the Result is a Failure.")
  }

  /**
   * Returns the encapsulated [Error] if this instance represents [Failure] [isFailure] or `null`
   * if it is [Success] [isSuccess].
   */
  public fun errorOrNull(): Error? = when (this) {
    is Success -> null
    is Failure -> value
  }

  /**
   * Represents successful result.
   *
   * @param value The [A] data associated with the result.
   */
  public data class Success(val value: A) : Result()

  /**
   * Represents failed result.
   *
   * @param value The [Error] associated with the result.
   */
  public data class Failure(val value: Error) : Result()

  /**
   * Returns a transformed [Result] of applying the given [f] function if the [Result]
   * contains a successful data payload.
   * Returns an original [Result] if the [Result] contains an error payload.
   *
   * @param f A lambda for mapping [Result] of [A] to [Result] of [C].
   *
   * @return A transformed instance of the [Result] or the original instance of the [Result].
   */
  public inline fun  map(f: (A) -> C): Result = flatMap { Success(f(it)) }

  /**
   * Returns a transformed [Result] of applying the given suspending [f] function if the [Result]
   * contains a successful data payload.
   * Returns an original [Result] if the [Result] contains an error payload.
   *
   * @param f A suspending lambda for mapping [Result] of [A] to [Result] of [C].
   *
   * @return A transformed instance of the [Result] or the original instance of the [Result].
   */
  @JvmSynthetic
  public suspend inline fun  mapSuspend(crossinline f: suspend (A) -> C): Result =
    flatMap { Success(f(it)) }

  /**
   * Returns a [Result] of [Unit] from any type of a [Result].
   *
   * @return [Result] of [Unit].
   */
  public fun toUnitResult(): Result = map {}

  /**
   * Runs the [successSideEffect] lambda function if the [Result] contains a successful data payload.
   *
   * @param successSideEffect A lambda that receives the successful data payload.
   *
   * @return The original instance of the [Result].
   */
  public inline fun onSuccess(
    crossinline successSideEffect: (A) -> Unit
  ): Result =
    also {
      when (it) {
        is Success -> successSideEffect(it.value)
        is Failure -> Unit
      }
    }

  /**
   * Runs the [errorSideEffect] lambda function if the [Result] contains an error payload.
   *
   * @param errorSideEffect A lambda that receives the [Error] payload.
   *
   * @return The original instance of the [Result].
   */
  public inline fun onError(
    crossinline errorSideEffect: (Error) -> Unit
  ): Result =
    also {
      when (it) {
        is Success -> Unit
        is Failure -> errorSideEffect(it.value)
      }
    }
}

/**
 * Returns a transformed [Result] from results of the [f] if the [Result] contains a successful data payload.
 * Returns an original [Result] if the [Result] contains an error payload.
 *
 * @param f A lambda that returns [Result] of [C].
 *
 * @return A transformed instance of the [Result] or the original instance of the [Result].
 */
public inline fun  Result.flatMap(f: (A) -> Result): Result {
  return when (this) {
    is Result.Success -> f(this.value)
    is Result.Failure -> this
  }
}

/**
 * Returns a transformed [Result] from results of the suspending [f] if the [Result] contains a successful data
 * payload.
 * Returns an original [Result] if the [Result] contains an error payload.
 *
 * @param f A suspending lambda that returns [Result] of [C].
 *
 * @return A transformed instance of the [Result] or the original instance of the [Result].
 */
@JvmSynthetic
public suspend inline fun  Result.flatMapSuspend(
  crossinline f: suspend (A) -> Result
): Result {
  return when (this) {
    is Result.Success -> f(this.value)
    is Result.Failure -> this
  }
}

/**
 * Runs the suspending [successSideEffect] lambda function if the [Result] contains a successful data payload.
 *
 * @param successSideEffect A suspending lambda that receives the successful data payload.
 *
 * @return The original instance of the [Result].
 */
@JvmSynthetic
public suspend inline fun  Result.onSuccessSuspend(
  crossinline successSideEffect: suspend (A) -> Unit
): Result =
  also {
    when (it) {
      is Result.Success -> successSideEffect(it.value)
      is Result.Failure -> Unit
    }
  }

/**
 * Runs the suspending [errorSideEffect] lambda function if the [Result] contains an error payload.
 *
 * @param errorSideEffect A suspending lambda that receives the [Error] payload.
 *
 * @return The original instance of the [Result].
 */
@JvmSynthetic
public suspend inline fun  Result.onErrorSuspend(
  crossinline errorSideEffect: suspend (Error) -> Unit
): Result =
  also {
    when (it) {
      is Result.Success -> Unit
      is Result.Failure -> errorSideEffect(it.value)
    }
  }

/**
 * Recovers the error payload by applying the given [errorMapper] function if the [Result]
 * contains an error payload.
 *
 * @param errorMapper A lambda that receives [Error] and transforms it as a payload [A].
 * @param errorMapper A lambda that receives [Error] and transforms it as a payload [A].
 *
 * @return A transformed instance of the [Result] or the original instance of the [Result].
 */
@JvmSynthetic
public fun  Result.recover(errorMapper: (Error) -> A): Result.Success {
  return when (this) {
    is Result.Success -> this
    is Result.Failure -> Result.Success(errorMapper(value))
  }
}

/**
 * Recovers the error payload by applying the given suspending [errorMapper] function if the [Result]
 * contains an error payload.
 *
 * @param errorMapper A suspending lambda that receives [Error] and transforms it as a payload [A].
 *
 * @return A transformed instance of the [Result] or the original instance of the [Result].
 */
@JvmSynthetic
public suspend inline fun  Result.recoverSuspend(
  crossinline errorMapper: suspend (Error) -> A
): Result.Success {
  return when (this) {
    is Result.Success -> this
    is Result.Failure -> Result.Success(errorMapper(value))
  }
}

/**
 * Composition the [Result] with a given [Result] from the [f].
 */
@JvmSynthetic
public inline infix fun  Result.then(f: (T) -> Result): Result =
  when (this) {
    is Result.Success -> f(this.value)
    is Result.Failure -> Result.Failure(this.value)
  }