aboutsummaryrefslogtreecommitdiffstats
path: root/coroutine-utils/src/main/kotlin/app
diff options
context:
space:
mode:
Diffstat (limited to 'coroutine-utils/src/main/kotlin/app')
-rw-r--r--coroutine-utils/src/main/kotlin/app/passwordstore/util/coroutines/DispatcherProvider.kt22
-rw-r--r--coroutine-utils/src/main/kotlin/app/passwordstore/util/coroutines/RunSuspendCatching.kt49
2 files changed, 71 insertions, 0 deletions
diff --git a/coroutine-utils/src/main/kotlin/app/passwordstore/util/coroutines/DispatcherProvider.kt b/coroutine-utils/src/main/kotlin/app/passwordstore/util/coroutines/DispatcherProvider.kt
new file mode 100644
index 00000000..c8edd654
--- /dev/null
+++ b/coroutine-utils/src/main/kotlin/app/passwordstore/util/coroutines/DispatcherProvider.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+package app.passwordstore.util.coroutines
+
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+
+/** Interface to allow abstracting individual [CoroutineDispatcher]s out of class dependencies. */
+public interface DispatcherProvider {
+
+ public fun main(): CoroutineDispatcher = Dispatchers.Main
+ public fun default(): CoroutineDispatcher = Dispatchers.Default
+ public fun io(): CoroutineDispatcher = Dispatchers.IO
+ public fun unconfined(): CoroutineDispatcher = Dispatchers.Unconfined
+}
+
+/** Concrete type for [DispatcherProvider] with all the defaults from the class. */
+public class DefaultDispatcherProvider @Inject constructor() : DispatcherProvider
diff --git a/coroutine-utils/src/main/kotlin/app/passwordstore/util/coroutines/RunSuspendCatching.kt b/coroutine-utils/src/main/kotlin/app/passwordstore/util/coroutines/RunSuspendCatching.kt
new file mode 100644
index 00000000..c650c14b
--- /dev/null
+++ b/coroutine-utils/src/main/kotlin/app/passwordstore/util/coroutines/RunSuspendCatching.kt
@@ -0,0 +1,49 @@
+@file:OptIn(ExperimentalContracts::class)
+@file:Suppress("RedundantSuspendModifier")
+
+package app.passwordstore.util.coroutines
+
+import com.github.michaelbull.result.Err
+import com.github.michaelbull.result.Ok
+import com.github.michaelbull.result.Result
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.InvocationKind
+import kotlin.contracts.contract
+import kotlinx.coroutines.CancellationException
+
+/**
+ * Calls the specified function [block] and returns its encapsulated result if invocation was
+ * successful, catching any [Throwable] except [CancellationException] that was thrown from the
+ * [block] function execution and encapsulating it as a failure.
+ */
+@OptIn(ExperimentalContracts::class)
+public suspend inline fun <V> runSuspendCatching(block: () -> V): Result<V, Throwable> {
+ contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
+
+ return try {
+ Ok(block())
+ } catch (e: Throwable) {
+ if (e is CancellationException) throw e
+ Err(e)
+ }
+}
+
+/**
+ * Calls the specified function [block] with [this] value as its receiver and returns its
+ * encapsulated result if invocation was successful, catching any [Throwable] except
+ * [CancellationException] that was thrown from the [block] function execution and encapsulating it
+ * as a failure.
+ */
+@OptIn(ExperimentalContracts::class)
+public suspend inline infix fun <T, V> T.runSuspendCatching(
+ block: T.() -> V
+): Result<V, Throwable> {
+ contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
+
+ return try {
+ Ok(block())
+ } catch (e: Throwable) {
+ if (e is CancellationException) throw e
+ Err(e)
+ }
+}