Updated on 2026-08-14

This commit is contained in:
Tangem 2023-04-19 19:05:58 +03:00
parent c3d8722f57
commit 4589332282
10 changed files with 233 additions and 13 deletions

View file

@ -1,7 +1,19 @@
package com.tangem.domain.core.chain
import arrow.core.Either
import arrow.core.EitherNel
/**
* A chain in the [ChainProcessor] class for processing a chain of operations with the ability to handle errors.
* @param E the type of error
* @param R the type of result
*/
interface Chain<E, R> {
suspend operator fun invoke(previousChainResult: Either<E, R>): Either<E, R>
/**
* Invokes the chain with the previous chain result as input and returns an [Either] result.
* @param previousChainResult the previous chain result as an [EitherNel]
* @return the result of the chain processing as an [Either]
*/
suspend operator fun invoke(previousChainResult: EitherNel<E, R>): Either<E, R>
}

View file

@ -1,28 +1,63 @@
package com.tangem.domain.core.chain
import arrow.core.Either
import arrow.core.EitherNel
import arrow.core.left
import arrow.core.nel
import arrow.core.right
import arrow.core.toEitherNel
/**
* A class for processing a chain of operations with the ability to handle errors.
* @param E the type of error
* @param R the type of result
*/
class ChainProcessor<E, R> {
/**
* The list of chains to be executed in order.
*/
private val chains: MutableList<Chain<E, R>> = mutableListOf()
/**
* Adds chains to the existing list of chains to be executed.
* @param chains the chains to be added to the list
*/
fun addChains(vararg chains: Chain<E, R>) {
this.chains.addAll(chains)
}
suspend fun launchChains(initial: R, returnOnFirstError: Boolean = true): Either<E, R> {
return launchChains(initial = Either.Right(initial), returnOnFirstError)
}
private suspend fun launchChains(initial: Either<E, R>, returnOnFirstError: Boolean = true): Either<E, R> {
var result = initial
chains.forEach { chain ->
result = chain(result)
.onRight {
if (returnOnFirstError) return result
/**
* Launches the chains with the given initial value as input.
* @param initial the initial value to start the chain processing.
* @param accumulateExceptions determines whether to accumulate exceptions or break on first exception.
* Defaults to true.
* @return the result of the chain processing as an EitherNel, which is a disjunction that may contain multiple errors.
*/
private suspend fun launchChains(initial: Either<E, R>, accumulateExceptions: Boolean = true): EitherNel<E, R> {
return chains.fold(initial.toEitherNel()) { previousChainResult, chain ->
chain.invoke(previousChainResult)
.mapLeft { e ->
previousChainResult.leftOrNull()?.plus(e)
?: e.nel()
}
.onLeft { e ->
if (!accumulateExceptions) {
return e.left()
}
}
}
}
return result
/**
* Launches the chains with the given initial value as input.
* This function is a convenience function that assumes the initial value does not contain any errors.
* @param initial the initial value to start the chain processing.
* @param accumulateExceptions determines whether to accumulate exceptions or break on first exception.
* Defaults to true.
* @return the result of the chain processing as an EitherNel, which is a disjunction that may contain multiple errors.
*/
suspend fun launchChains(initial: R, accumulateExceptions: Boolean = true): EitherNel<E, R> {
return launchChains(initial.right(), accumulateExceptions)
}
}