fix: ignore CancellationException
in suspend functions (#1794)
* fix: ignore `CancellationException` in suspend functions Signed-off-by: Aditya Wasan <adityawasan55@gmail.com> * build(coroutine-utils): use `api` instead of `implementation` Co-authored-by: Harsh Shandilya <me@msfjarvis.dev> Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
parent
cf3ae17b84
commit
9c9616d047
4 changed files with 57 additions and 7 deletions
|
@ -10,4 +10,5 @@ plugins {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.kotlin.coroutines.core)
|
implementation(libs.kotlin.coroutines.core)
|
||||||
implementation(libs.dagger.hilt.core)
|
implementation(libs.dagger.hilt.core)
|
||||||
|
api(libs.thirdparty.kotlinResult)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
@file:OptIn(ExperimentalContracts::class)
|
||||||
|
@file:Suppress("RedundantSuspendModifier")
|
||||||
|
|
||||||
|
package dev.msfjarvis.aps.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] 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.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ plugins {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.cryptoCommon)
|
api(projects.cryptoCommon)
|
||||||
|
implementation(projects.coroutineUtils)
|
||||||
implementation(libs.androidx.annotation)
|
implementation(libs.androidx.annotation)
|
||||||
implementation(libs.dagger.hilt.core)
|
implementation(libs.dagger.hilt.core)
|
||||||
implementation(libs.kotlin.coroutines.core)
|
implementation(libs.kotlin.coroutines.core)
|
||||||
|
|
|
@ -8,9 +8,9 @@ package dev.msfjarvis.aps.crypto
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import com.github.michaelbull.result.Result
|
import com.github.michaelbull.result.Result
|
||||||
import com.github.michaelbull.result.runCatching
|
|
||||||
import dev.msfjarvis.aps.crypto.KeyUtils.tryGetId
|
import dev.msfjarvis.aps.crypto.KeyUtils.tryGetId
|
||||||
import dev.msfjarvis.aps.crypto.KeyUtils.tryParseKeyring
|
import dev.msfjarvis.aps.crypto.KeyUtils.tryParseKeyring
|
||||||
|
import dev.msfjarvis.aps.util.coroutines.runSuspendCatching
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
@ -28,7 +28,7 @@ constructor(
|
||||||
|
|
||||||
override suspend fun addKey(key: PGPKey, replace: Boolean): Result<PGPKey, Throwable> =
|
override suspend fun addKey(key: PGPKey, replace: Boolean): Result<PGPKey, Throwable> =
|
||||||
withContext(dispatcher) {
|
withContext(dispatcher) {
|
||||||
runCatching {
|
runSuspendCatching {
|
||||||
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException
|
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException
|
||||||
if (tryParseKeyring(key) == null) throw KeyManagerException.InvalidKeyException
|
if (tryParseKeyring(key) == null) throw KeyManagerException.InvalidKeyException
|
||||||
val keyFile = File(keyDir, "${tryGetId(key)}.$KEY_EXTENSION")
|
val keyFile = File(keyDir, "${tryGetId(key)}.$KEY_EXTENSION")
|
||||||
|
@ -49,7 +49,7 @@ constructor(
|
||||||
|
|
||||||
override suspend fun removeKey(key: PGPKey): Result<PGPKey, Throwable> =
|
override suspend fun removeKey(key: PGPKey): Result<PGPKey, Throwable> =
|
||||||
withContext(dispatcher) {
|
withContext(dispatcher) {
|
||||||
runCatching {
|
runSuspendCatching {
|
||||||
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException
|
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException
|
||||||
if (tryParseKeyring(key) == null) throw KeyManagerException.InvalidKeyException
|
if (tryParseKeyring(key) == null) throw KeyManagerException.InvalidKeyException
|
||||||
val keyFile = File(keyDir, "${tryGetId(key)}.$KEY_EXTENSION")
|
val keyFile = File(keyDir, "${tryGetId(key)}.$KEY_EXTENSION")
|
||||||
|
@ -63,7 +63,7 @@ constructor(
|
||||||
|
|
||||||
override suspend fun getKeyById(id: GpgIdentifier): Result<PGPKey, Throwable> =
|
override suspend fun getKeyById(id: GpgIdentifier): Result<PGPKey, Throwable> =
|
||||||
withContext(dispatcher) {
|
withContext(dispatcher) {
|
||||||
runCatching {
|
runSuspendCatching {
|
||||||
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException
|
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException
|
||||||
val keyFiles = keyDir.listFiles()
|
val keyFiles = keyDir.listFiles()
|
||||||
if (keyFiles.isNullOrEmpty()) throw KeyManagerException.NoKeysAvailableException
|
if (keyFiles.isNullOrEmpty()) throw KeyManagerException.NoKeysAvailableException
|
||||||
|
@ -89,7 +89,7 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchResult != null) {
|
if (matchResult != null) {
|
||||||
return@runCatching matchResult
|
return@runSuspendCatching matchResult
|
||||||
}
|
}
|
||||||
|
|
||||||
throw KeyManagerException.KeyNotFoundException("$id")
|
throw KeyManagerException.KeyNotFoundException("$id")
|
||||||
|
@ -98,10 +98,10 @@ constructor(
|
||||||
|
|
||||||
override suspend fun getAllKeys(): Result<List<PGPKey>, Throwable> =
|
override suspend fun getAllKeys(): Result<List<PGPKey>, Throwable> =
|
||||||
withContext(dispatcher) {
|
withContext(dispatcher) {
|
||||||
runCatching {
|
runSuspendCatching {
|
||||||
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException
|
if (!keyDirExists()) throw KeyManagerException.KeyDirectoryUnavailableException
|
||||||
val keyFiles = keyDir.listFiles()
|
val keyFiles = keyDir.listFiles()
|
||||||
if (keyFiles.isNullOrEmpty()) return@runCatching emptyList()
|
if (keyFiles.isNullOrEmpty()) return@runSuspendCatching emptyList()
|
||||||
keyFiles.map { keyFile -> PGPKey(keyFile.readBytes()) }.toList()
|
keyFiles.map { keyFile -> PGPKey(keyFile.readBytes()) }.toList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue