diff --git a/python/openapi/src/com/jetbrains/python/Result.kt b/python/openapi/src/com/jetbrains/python/Result.kt index 045c53f4d8e1..1c2a22a90d27 100644 --- a/python/openapi/src/com/jetbrains/python/Result.kt +++ b/python/openapi/src/com/jetbrains/python/Result.kt @@ -2,8 +2,38 @@ package com.jetbrains.python /** - * Operation result to be used with pattern matching. - * Unlike Kotlin `Result`, [ERR] could be anything (See [LocalizedErrorString]) + * Operation result to be used as `Maybe` instead of checked exceptions. + * Unlike Kotlin `Result`, [ERR] could be anything (See [LocalizedErrorString]). + * + * Typical usages: + * + * ```kotlin + * when(val r = someFun() { + * is Result.Success -> r.result // is ok + * is Result.Failure -> r.error // is error + * } + * ``` + * Get result or throw error (I am 100% sure there is no error): [orThrow]. + * + * Chain several calls, get latest result or first error (all errors are the same): [mapResult]. + * + * When errors are different: [mapResultWithErr] + * + * Fast return: [getOr] + * ```kotlin + * fun foo() { + * val data = getSomeResult().getOr { return } + * } + * ``` + * + * Return from function with same error (see [convertErr]) + * ```kotlin + * fun foo():Result { + * // Returns Result + * getSomeResult().getOr { return it.convertErr()} + * } + * ``` + * See showcase in tests. */ sealed class Result { data class Failure(val error: ERR) : Result() @@ -15,10 +45,24 @@ sealed class Result { is Failure -> Failure(error) } + /*** + * ```kotlin + * val data = someFun().getOr { return } + * ``` + */ + inline fun getOr(onFailure: (err: Failure<*, ERR>) -> Nothing): SUCC { + when (this) { + is Failure -> onFailure(this) + is Success -> return result + } + } + /** * Maps success result to another one with same error * ```kotlin - * findBeer().mapResult{openBeer(it)}.mapResult{drinkIt(it)} + * val drinkResultOrFirstError = findBeer() + * .mapResult{ openBeer(it) } + * .mapResult{ drinkIt(it) } * ``` */ inline fun mapResult(map: (SUCC) -> Result): Result = @@ -27,11 +71,42 @@ sealed class Result { is Failure -> Failure(error) } + /** + * Same as [mapResult] but for different errors + * ```kotlin + * val drinkResultOrFirstError = findBeer() + * .mapResult{ openBeer(it) } + * .mapResultWithErr( + * onSuccess = { drink(it) }, + * onErr = { LocalizedErrorString("Oops, ${it.message}") } + * ) + * ``` + */ + inline fun mapResultWithErr( + onSuccess: (SUCC) -> Result, + onErr: (ERR) -> NEW_ERR, + ): Result = + when (this) { + is Success -> onSuccess(result) + is Failure -> Failure(onErr(error)) + } + val successOrNull: SUCC? get() = if (this is Success) result else null + + + /** + * Like Rust `unwrap`: returns result or throws exception. Use when error is unexpected + */ fun orThrow(onError: (ERR) -> Throwable = { e -> AssertionError(e) }): SUCC { when (this) { is Success -> return result is Failure -> throw onError(this.error) } } -} \ No newline at end of file +} + +/** + * Converts [Result.Failure] to another [Result.Failure] with the same error but different success. + * See class doc for example + */ +fun Result.Failure<*, E>.convertErr(): Result.Failure = Result.Failure(error) \ No newline at end of file