From edb97215ef9c0336905da40a5315531bbed7d589 Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Thu, 20 Mar 2025 17:28:28 +0600 Subject: [PATCH] add TransactionsDSL --- CHANGELOG.md | 2 + settings.gradle | 1 + transactions/build.gradle | 7 ++ .../src/commonMain/kotlin/TransactionsDSL.kt | 82 +++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 transactions/build.gradle create mode 100644 transactions/src/commonMain/kotlin/TransactionsDSL.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index bf7fc5ca09d..2072fc62925 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * `Coroutines`: * Add extensions `SmartRWLocker.alsoWithUnlockingOnSuccessAsync` and `SmartRWLocker.alsoWithUnlockingOnSuccess` +* `Transactions`: + * Add `TransactionsDSL` ## 0.25.2 diff --git a/settings.gradle b/settings.gradle index a8e6a646598..99d4168788a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,6 +3,7 @@ rootProject.name='micro_utils' String[] includes = [ ":common", ":common:compose", + ":transactions", ":matrix", ":safe_wrapper", ":crypto", diff --git a/transactions/build.gradle b/transactions/build.gradle new file mode 100644 index 00000000000..9e169e84113 --- /dev/null +++ b/transactions/build.gradle @@ -0,0 +1,7 @@ +plugins { + id "org.jetbrains.kotlin.multiplatform" + id "org.jetbrains.kotlin.plugin.serialization" + id "com.android.library" +} + +apply from: "$mppJvmJsAndroidLinuxMingwLinuxArm64Project" diff --git a/transactions/src/commonMain/kotlin/TransactionsDSL.kt b/transactions/src/commonMain/kotlin/TransactionsDSL.kt new file mode 100644 index 00000000000..c268891b5b5 --- /dev/null +++ b/transactions/src/commonMain/kotlin/TransactionsDSL.kt @@ -0,0 +1,82 @@ +package dev.inmo.micro_utils.transactions + +typealias TransactionDSLRollbackLambda = suspend (Throwable) -> Unit +class TransactionsDSL internal constructor() { + internal val rollbackActions = LinkedHashSet() + + internal fun addRollbackAction(rollbackAction: TransactionDSLRollbackLambda) { + rollbackActions.add(rollbackAction) + } +} +class RollbackContext internal constructor ( + val actionResult: T, + val error: Throwable +) + +/** + * Calls [action] and, if it succeeded - saving [rollback] action for future usage for cases when some other + * action or even main one throwing an error + * + * @param rollback Will be called if + */ +suspend fun TransactionsDSL.rollbackableOperation( + rollback: suspend RollbackContext.() -> Unit, + action: suspend () -> T +): T { + return runCatching { action() } + .onSuccess { + addRollbackAction { e -> + val context = RollbackContext(it, e) + context.rollback() + } + } + .getOrThrow() +} + +/** + * Starts transaction with opportunity to add actions [rollbackableOperation]. How to use: + * + * ```kotlin + * doSuspendTransaction { + * println("start of action") + * + * withRollback({ // it - result of action + * // some rollback action + * }) { + * // Some action with rollback + * } + * + * withRollback({ + * repository.delete(it) // it - result of createSomething, if it completes successfully + * }) { + * repository.createSomething() + * } + * + * withRollback({ + * // will not be triggered due to error in action + * }) { + * error("It is just a simple error") // Will trigger rolling back previously successfully completed actions + * } + * } + * ``` + * + * @param onRollbackStepError Will be called if rollback action throwing some error + */ +suspend fun doSuspendTransaction( + onRollbackStepError: suspend (TransactionDSLRollbackLambda, Throwable) -> Unit = { _, _ -> }, + block: suspend TransactionsDSL.() -> T +): Result { + val transactionsDSL = TransactionsDSL() + + return runCatching { + transactionsDSL.block() + }.onFailure { e -> + transactionsDSL.rollbackActions.forEach { + runCatching { + it.invoke(e) + }.onFailure { ee -> + onRollbackStepError(it, ee) + } + } + } +}