Use safer runCatching alternative

This commit is contained in:
Rafael Caetano 2023-08-23 01:02:18 +01:00
parent a24a99f05c
commit 8ec196032a
11 changed files with 73 additions and 18 deletions

View File

@ -113,6 +113,7 @@ dependencies {
with(Dependencies.Modules) {
implementation(project(masterSwitchPreference))
implementation(project(rcheevosApi))
implementation(project(common))
}
with(Dependencies.Kotlin) {

View File

@ -5,6 +5,7 @@ import android.net.Uri
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import me.magnum.melonds.MelonDSiNand
import me.magnum.melonds.common.suspendRunCatching
import me.magnum.melonds.domain.model.ConfigurationDirResult
import me.magnum.melonds.domain.model.DSiWareTitle
import me.magnum.melonds.domain.model.dsinand.ImportDSiWareTitleResult
@ -69,7 +70,7 @@ class AndroidDSiNandManager(
return@withContext ImportDSiWareTitleResult.NOT_DSIWARE_TITLE
}
val tmdMetadataResult = runCatching {
val tmdMetadataResult = suspendRunCatching {
dsiWareMetadataRepository.getDSiWareTitleMetadata(categoryId, titleId)
}

View File

@ -10,6 +10,9 @@ import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import me.magnum.melonds.common.suspendMapCatching
import me.magnum.melonds.common.suspendRecoverCatching
import me.magnum.melonds.common.suspendRunCatching
import me.magnum.melonds.common.workers.RetroAchievementsSubmissionWorker
import me.magnum.melonds.database.daos.RAAchievementsDao
import me.magnum.melonds.database.entities.retroachievements.RAGameEntity
@ -111,7 +114,7 @@ class AndroidRetroAchievementsRepository(
}
override suspend fun getAchievement(achievementId: Long): Result<RAAchievement?> {
return runCatching {
return suspendRunCatching {
achievementsDao.getAchievement(achievementId)
}.map {
it?.mapToModel()
@ -184,13 +187,13 @@ class AndroidRetroAchievementsRepository(
.map {
it[gameHash]
}
.recoverCatching {
.suspendRecoverCatching {
achievementsDao.getGameHashEntity(gameHash)?.let {
RAGameId(it.gameId)
}
}
} else {
runCatching {
suspendRunCatching {
achievementsDao.getGameHashEntity(gameHash)?.let {
RAGameId(it.gameId)
}
@ -200,7 +203,7 @@ class AndroidRetroAchievementsRepository(
private suspend fun fetchGameAchievements(gameId: RAGameId, gameSetMetadata: CurrentGameSetMetadata): Result<List<RAAchievement>> {
return if (mustRefreshAchievementSet(gameSetMetadata.currentMetadata)) {
raApi.getGameInfo(gameId).mapCatching { game ->
raApi.getGameInfo(gameId).suspendMapCatching { game ->
val achievementEntities = game.achievements.map {
it.mapToEntity()
}
@ -210,7 +213,7 @@ class AndroidRetroAchievementsRepository(
achievementsDao.updateGameData(gameEntity, achievementEntities)
achievementsDao.updateGameSetMetadata(newMetadata)
game.achievements
}.recoverCatching { exception ->
}.suspendRecoverCatching { exception ->
if (gameSetMetadata.isGameAchievementDataKnown()) {
// Load DB data because we know that it was previously loaded
achievementsDao.getGameAchievements(gameId.id).map { it ->
@ -222,7 +225,7 @@ class AndroidRetroAchievementsRepository(
}
}
} else {
runCatching {
suspendRunCatching {
achievementsDao.getGameAchievements(gameId.id).map {
it.mapToModel()
}
@ -247,7 +250,7 @@ class AndroidRetroAchievementsRepository(
val newMetadata = gameSetMetadata.withNewUserAchievementsUpdate(forHardcoreMode)
achievementsDao.updateGameUserUnlockedAchievements(gameId.id, userAchievementEntities)
achievementsDao.updateGameSetMetadata(newMetadata)
}.recoverCatching { exception ->
}.suspendRecoverCatching { exception ->
if (gameSetMetadata.isUserAchievementDataKnown(forHardcoreMode)) {
// Load DB data because we know that it was previously loaded
achievementsDao.getGameUserUnlockedAchievements(gameId.id, forHardcoreMode).map {
@ -259,7 +262,7 @@ class AndroidRetroAchievementsRepository(
}
}
} else {
runCatching {
suspendRunCatching {
achievementsDao.getGameUserUnlockedAchievements(gameId.id, forHardcoreMode).map {
it.achievementId
}

View File

@ -6,6 +6,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.rx2.await
import kotlinx.coroutines.withContext
import me.magnum.melonds.MelonEmulator
@ -57,7 +58,7 @@ class AndroidEmulatorManager(
}
val loadResult = MelonEmulator.loadRom(romUri, sram, rom.config.mustLoadGbaCart(), rom.config.gbaCartPath, rom.config.gbaSavePath)
if (loadResult.isTerminal) {
if (loadResult.isTerminal || !isActive) {
cameraManager.stopCurrentCameraSource()
RomLaunchResult.LaunchFailed(loadResult)
} else {

View File

@ -20,7 +20,7 @@ object Dependencies {
const val Hilt = "2.47"
const val Junit = "4.12"
const val Kotlin = "1.9.0"
const val KotlinxCoroutinesRx = "1.6.4"
const val KotlinxCoroutines = "1.7.3"
const val Ksp = "1.9.0-1.0.12"
const val LifecycleViewModel = "2.6.1"
const val Material = "1.7.0"
@ -52,6 +52,7 @@ object Dependencies {
object Kotlin {
const val kotlinStdlib = "org.jetbrains.kotlin:kotlin-stdlib:${Versions.Kotlin}"
const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.KotlinxCoroutines}"
}
object AndroidX {
@ -96,7 +97,7 @@ object Dependencies {
const val flexbox = "com.google.android.flexbox:flexbox:${Versions.Flexbox}"
const val gson = "com.google.code.gson:gson:${Versions.Gson}"
const val hilt = "com.google.dagger:hilt-android:${Versions.Hilt}"
const val kotlinxCoroutinesRx = "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:${Versions.KotlinxCoroutinesRx}"
const val kotlinxCoroutinesRx = "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:${Versions.KotlinxCoroutines}"
const val picasso = "com.squareup.picasso:picasso:${Versions.Picasso}"
const val markwon = "io.noties.markwon:core:${Versions.Markwon}"
const val markwonImagePicasso = "io.noties.markwon:image-picasso:${Versions.Markwon}"
@ -123,6 +124,7 @@ object Dependencies {
object Modules {
const val masterSwitchPreference = ":masterswitch"
const val rcheevosApi = ":rcheevos-api"
const val common = ":common"
}
object Testing {

1
common/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

13
common/build.gradle.kts Normal file
View File

@ -0,0 +1,13 @@
plugins {
id("org.jetbrains.kotlin.jvm")
}
kotlin {
jvmToolchain(17)
}
dependencies {
with(Dependencies.Kotlin) {
implementation(coroutines)
}
}

View File

@ -0,0 +1,27 @@
package me.magnum.melonds.common
import kotlinx.coroutines.ensureActive
import kotlin.coroutines.coroutineContext
suspend inline fun <R> suspendRunCatching(block: () -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
coroutineContext.ensureActive()
Result.failure(e)
}
}
suspend inline fun <R, T : R> Result<T>.suspendRecoverCatching(transform: (exception: Throwable) -> R): Result<R> {
return when (val exception = exceptionOrNull()) {
null -> this
else -> suspendRunCatching { transform(exception) }
}
}
suspend inline fun <R, T> Result<T>.suspendMapCatching(transform: (value: T) -> R): Result<R> {
return when {
isSuccess -> suspendRunCatching { transform(getOrThrow()) }
else -> Result.failure(exceptionOrNull()!!)
}
}

View File

@ -7,6 +7,10 @@ kotlin {
}
dependencies {
with(Dependencies.Modules) {
implementation(project(common))
}
with(Dependencies.ThirdParty) {
implementation(gson)
implementation(okHttp)

View File

@ -2,6 +2,8 @@ package me.magnum.rcheevosapi
import com.google.gson.Gson
import com.google.gson.JsonParser
import me.magnum.melonds.common.suspendMapCatching
import me.magnum.melonds.common.suspendRunCatching
import me.magnum.rcheevosapi.dto.*
import me.magnum.rcheevosapi.dto.mapper.mapToModel
import me.magnum.rcheevosapi.exception.UnsuccessfulRequestException
@ -104,7 +106,7 @@ class RAApi(
PARAMETER_TOKEN to userAuth.token,
PARAMETER_GAME_ID to gameId.id.toString(),
)
).mapCatching {
).suspendMapCatching {
it.game.mapToModel()
}
}
@ -171,9 +173,9 @@ class RAApi(
errorHandler: (String?) -> Unit = { throw UnsuccessfulRequestException(it ?: "Unknown reason") }
): Result<T> {
val request = buildGetRequest(parameters)
return runCatching {
return suspendRunCatching {
executeRequest(request)
}.mapCatching { response ->
}.suspendMapCatching { response ->
if (response.isSuccessful) {
val json = JsonParser.parseReader(response.body?.charStream())
val isSuccessful = json.asJsonObject["Success"].asBoolean
@ -201,9 +203,9 @@ class RAApi(
errorHandler: (String?) -> Unit = { throw UnsuccessfulRequestException(it ?: "Unknown reason") }
): Result<T> {
val request = buildPostRequest(parameters)
return runCatching {
return suspendRunCatching {
executeRequest(request)
}.mapCatching { response ->
}.suspendMapCatching { response ->
if (response.isSuccessful) {
val json = JsonParser.parseReader(response.body?.charStream())
val isSuccessful = json.asJsonObject["Success"].asBoolean

View File

@ -1 +1 @@
include ':app', ':masterswitch', ':rcheevos-api'
include ':app', ':masterswitch', ':rcheevos-api', ':common'