parent
623a360e21
commit
2667273015
@ -0,0 +1,42 @@
|
||||
package com.github.michaelbull.result.coroutines
|
||||
|
||||
import com.github.michaelbull.result.Result
|
||||
import com.github.michaelbull.result.runCatching
|
||||
import com.github.michaelbull.result.throwIf
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* 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. If the encapsulated failure is a
|
||||
* [CancellationException], the exception is thrown to indicate _normal_ cancellation of a
|
||||
* coroutine.
|
||||
*/
|
||||
public inline fun <V> runSuspendCatching(block: () -> V): Result<V, Throwable> {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
|
||||
return runCatching(block).throwIf {
|
||||
it is CancellationException
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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. If the
|
||||
* encapsulated failure is a [CancellationException], the exception is thrown to indicate _normal_
|
||||
* cancellation of a coroutine.
|
||||
*/
|
||||
public inline infix fun <T, V> T.runSuspendCatching(block: T.() -> V): Result<V, Throwable> {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
|
||||
return runCatching(block).throwIf {
|
||||
it is CancellationException
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.github.michaelbull.result.coroutines
|
||||
|
||||
import com.github.michaelbull.result.Err
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.onSuccess
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class RunSuspendCatchingTest {
|
||||
|
||||
@Test
|
||||
fun propagatesCoroutineCancellation() = runBlockingTest {
|
||||
var value: String? = null
|
||||
|
||||
launch(CoroutineName("outer scope")) {
|
||||
launch(CoroutineName("inner scope")) {
|
||||
val result = runSuspendCatching {
|
||||
delay(4_000)
|
||||
"value"
|
||||
}
|
||||
|
||||
// The coroutine should be cancelled before reaching here
|
||||
result.onSuccess { value = it }
|
||||
}
|
||||
|
||||
advanceTimeBy(2_000)
|
||||
|
||||
// Cancel outer scope, which should cancel inner scope
|
||||
cancel()
|
||||
}
|
||||
|
||||
assertNull(value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun returnsOkIfInvocationSuccessful() = runBlockingTest {
|
||||
val block = { "example" }
|
||||
val result = runSuspendCatching(block)
|
||||
|
||||
assertEquals(
|
||||
expected = Ok("example"),
|
||||
actual = result
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun returnsErrIfInvocationFailsWithAnythingOtherThanCancellationException() = runBlockingTest {
|
||||
val exception = IllegalArgumentException("throw me")
|
||||
val block = { throw exception }
|
||||
val result = runSuspendCatching(block)
|
||||
|
||||
assertEquals(
|
||||
expected = Err(exception),
|
||||
actual = result
|
||||
)
|
||||
}
|
||||
}
|
@ -4,9 +4,9 @@ import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* Calls the specified function [block] and returns its encapsulated result if
|
||||
* invocation was successful, catching and encapsulating any thrown exception
|
||||
* as a failure.
|
||||
* 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.
|
||||
*/
|
||||
public inline fun <V> runCatching(block: () -> V): Result<V, Throwable> {
|
||||
contract {
|
||||
@ -21,9 +21,9 @@ public inline fun <V> runCatching(block: () -> V): Result<V, Throwable> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the specified function [block] with [this] value as its receiver and
|
||||
* returns its encapsulated result if invocation was successful, catching and
|
||||
* encapsulating any thrown exception as a failure.
|
||||
* 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.
|
||||
*/
|
||||
public inline infix fun <T, V> T.runCatching(block: T.() -> V): Result<V, Throwable> {
|
||||
contract {
|
||||
|
Loading…
Reference in New Issue
Block a user