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
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls the specified function [block] and returns its encapsulated result if
|
* Calls the specified function [block] and returns its encapsulated result if invocation was
|
||||||
* invocation was successful, catching and encapsulating any thrown exception
|
* successful, catching any [Throwable] exception that was thrown from the [block] function
|
||||||
* as a failure.
|
* execution and encapsulating it as a failure.
|
||||||
*/
|
*/
|
||||||
public inline fun <V> runCatching(block: () -> V): Result<V, Throwable> {
|
public inline fun <V> runCatching(block: () -> V): Result<V, Throwable> {
|
||||||
contract {
|
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
|
* Calls the specified function [block] with [this] value as its receiver and returns its
|
||||||
* returns its encapsulated result if invocation was successful, catching and
|
* encapsulated result if invocation was successful, catching any [Throwable] exception that was
|
||||||
* encapsulating any thrown exception as a failure.
|
* 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> {
|
public inline infix fun <T, V> T.runCatching(block: T.() -> V): Result<V, Throwable> {
|
||||||
contract {
|
contract {
|
||||||
|
Loading…
Reference in New Issue
Block a user