From 9ad457fd9cff84f23809a7e8e044b52ba1982371 Mon Sep 17 00:00:00 2001 From: Omar BEN ROMDHANE Date: Fri, 16 Jan 2026 23:39:07 +0100 Subject: [PATCH] implement evaluate five elements --- .../monad/evaluation/EvaluateFiveElements.kt | 648 ++++++++++++++++++ .../evaluation/EvaluateFiveElementsTest.kt | 124 ++++ 2 files changed, 772 insertions(+) create mode 100644 library/src/commonMain/kotlin/com/benromdhane/omar/offroadsoft/monad/evaluation/EvaluateFiveElements.kt create mode 100644 library/src/commonTest/kotlin/com/benromdhane/omar/offroadsoft/monad/evaluation/EvaluateFiveElementsTest.kt diff --git a/library/src/commonMain/kotlin/com/benromdhane/omar/offroadsoft/monad/evaluation/EvaluateFiveElements.kt b/library/src/commonMain/kotlin/com/benromdhane/omar/offroadsoft/monad/evaluation/EvaluateFiveElements.kt new file mode 100644 index 0000000..2dddd59 --- /dev/null +++ b/library/src/commonMain/kotlin/com/benromdhane/omar/offroadsoft/monad/evaluation/EvaluateFiveElements.kt @@ -0,0 +1,648 @@ +package com.benromdhane.omar.offroadsoft.monad.evaluation + +import com.benromdhane.omar.offroadsoft.monad.error.PossibleError +import com.benromdhane.omar.offroadsoft.monad.error.SuccessOrMultipleErrors +import com.benromdhane.omar.offroadsoft.monad.tuple.Quintuple + +class EvaluateFiveElements private constructor( + private val firstElement: FIRST_ELEMENT +) { + + fun addEvaluation( + error: () -> ERROR, + check: (FIRST_ELEMENT) -> Boolean + ) = + FirstLevelPreparedEvaluation + .of( + this.firstElement, + error, + check + ) + + companion object Builder { + + fun first( + firstElement: FIRST_ELEMENT + ) = + EvaluateFiveElements<_, ERROR>( + firstElement + ) + } + + class FirstLevelPreparedEvaluation private constructor( + private val firstElement: FIRST_ELEMENT, + private val firstElementEvaluations: Set> + ) { + + fun addEvaluation( + error: () -> ERROR, + check: (FIRST_ELEMENT) -> Boolean + ) = + FirstLevelPreparedEvaluation( + firstElement, + this.firstElementEvaluations + .plus( + Evaluation + .of( + error, + check + ) + ) + ) + + fun second( + secondElement: SECOND_ELEMENT + ) = + SecondLevelNonPreparedEvaluation + .of( + this.firstElement, + this.firstElementEvaluations, + secondElement + ) + + internal companion object Builder { + + fun of( + firstElement: FIRST_ELEMENT, + error: () -> ERROR, + check: (FIRST_ELEMENT) -> Boolean + ) = + FirstLevelPreparedEvaluation( + firstElement, + setOf( + Evaluation + .of( + error, + check + ) + ) + ) + } + + class SecondLevelNonPreparedEvaluation private constructor( + private val firstElement: FIRST_ELEMENT, + private val firstElementEvaluations: Set>, + private val secondElement: SECOND_ELEMENT + ) { + + fun addEvaluation( + error: () -> ERROR, + check: (SECOND_ELEMENT) -> Boolean + ) = + SecondLevelPreparedEvaluation + .of( + this.firstElement, + this.firstElementEvaluations, + this.secondElement, + Evaluation + .of( + error, + check + ) + ) + + internal companion object Builder { + + internal fun of( + firstElement: FIRST_ELEMENT, + firstElementEvaluations: Set>, + secondElement: SECOND_ELEMENT + ) = + SecondLevelNonPreparedEvaluation( + firstElement, + firstElementEvaluations, + secondElement + ) + } + + class SecondLevelPreparedEvaluation private constructor( + private val firstElement: FIRST_ELEMENT, + private val firstElementEvaluations: Set>, + private val secondElement: SECOND_ELEMENT, + private val secondElementEvaluations: Set> + ) { + + fun addEvaluation( + error: () -> ERROR, + check: (SECOND_ELEMENT) -> Boolean + ) = + SecondLevelPreparedEvaluation( + this.firstElement, + this.firstElementEvaluations, + this.secondElement, + this.secondElementEvaluations + .plus( + Evaluation + .of( + error, + check + ) + ) + ) + + fun third( + thirdElement: THIRD_ELEMENT + ) = + ThirdLevelNonPreparedEvaluation + .of( + this.firstElement, + this.firstElementEvaluations, + this.secondElement, + this.secondElementEvaluations, + thirdElement + ) + + internal companion object Builder { + + internal fun of( + firstElement: FIRST_ELEMENT, + firstElementEvaluations: Set>, + secondElement: SECOND_ELEMENT, + secondElementEvaluation: Evaluation + ) = + SecondLevelPreparedEvaluation( + firstElement, + firstElementEvaluations, + secondElement, + setOf( + secondElementEvaluation + ) + ) + } + + class ThirdLevelNonPreparedEvaluation private constructor( + private val firstElement: FIRST_ELEMENT, + private val firstElementEvaluations: Set>, + private val secondElement: SECOND_ELEMENT, + private val secondElementEvaluations: Set>, + private val thirdElement: THIRD_ELEMENT + ) { + + fun addEvaluation( + error: () -> ERROR, + check: (THIRD_ELEMENT) -> Boolean + ) = + ThirdLevelPreparedEvaluation + .of( + this.firstElement, + this.firstElementEvaluations, + this.secondElement, + this.secondElementEvaluations, + this.thirdElement, + Evaluation + .of( + error, + check + ) + ) + + internal companion object Builder { + + internal fun of( + firstElement: FIRST_ELEMENT, + firstElementEvaluations: Set>, + secondElement: SECOND_ELEMENT, + secondElementEvaluations: Set>, + thirdElement: THIRD_ELEMENT + ) = + ThirdLevelNonPreparedEvaluation( + firstElement, + firstElementEvaluations, + secondElement, + secondElementEvaluations, + thirdElement + ) + } + + class ThirdLevelPreparedEvaluation private constructor( + private val firstElement: FIRST_ELEMENT, + private val firstElementEvaluations: Set>, + private val secondElement: SECOND_ELEMENT, + private val secondElementEvaluations: Set>, + private val thirdElement: THIRD_ELEMENT, + private val thirdElementEvaluations: Set> + ) { + + fun addEvaluation( + error: () -> ERROR, + check: (THIRD_ELEMENT) -> Boolean + ) = + ThirdLevelPreparedEvaluation( + this.firstElement, + this.firstElementEvaluations, + this.secondElement, + this.secondElementEvaluations, + this.thirdElement, + this.thirdElementEvaluations + .plus( + Evaluation + .of( + error, + check + ) + ) + ) + + fun fourth( + fourthElement: FOURTH_ELEMENT + ) = + FourLevelNonPreparedEvaluation + .of( + this.firstElement, + this.firstElementEvaluations, + this.secondElement, + this.secondElementEvaluations, + this.thirdElement, + this.thirdElementEvaluations, + fourthElement + ) + + internal companion object Builder { + + internal fun of( + firstElement: FIRST_ELEMENT, + firstElementEvaluations: Set>, + secondElement: SECOND_ELEMENT, + secondElementEvaluations: Set>, + thirdElement: THIRD_ELEMENT, + thirdElementEvaluation: Evaluation + ) = + ThirdLevelPreparedEvaluation( + firstElement, + firstElementEvaluations, + secondElement, + secondElementEvaluations, + thirdElement, + setOf( + thirdElementEvaluation + ) + ) + } + + class FourLevelNonPreparedEvaluation private constructor( + private val firstElement: FIRST_ELEMENT, + private val firstElementEvaluations: Set>, + private val secondElement: SECOND_ELEMENT, + private val secondElementEvaluations: Set>, + private val thirdElement: THIRD_ELEMENT, + private val thirdElementEvaluations: Set>, + private val fourthElement: FOURTH_ELEMENT + ) { + + fun addEvaluation( + error: () -> ERROR, + check: (FOURTH_ELEMENT) -> Boolean + ) = + FourLevelPreparedEvaluation + .of( + this.firstElement, + this.firstElementEvaluations, + this.secondElement, + this.secondElementEvaluations, + this.thirdElement, + this.thirdElementEvaluations, + this.fourthElement, + Evaluation + .of( + error, + check + ) + ) + + internal companion object Builder { + + internal fun of( + firstElement: FIRST_ELEMENT, + firstElementEvaluations: Set>, + secondElement: SECOND_ELEMENT, + secondElementEvaluations: Set>, + thirdElement: THIRD_ELEMENT, + thirdElementEvaluations: Set>, + fourthElement: FOURTH_ELEMENT + ) = + FourLevelNonPreparedEvaluation( + firstElement, + firstElementEvaluations, + secondElement, + secondElementEvaluations, + thirdElement, + thirdElementEvaluations, + fourthElement + ) + } + + class FourLevelPreparedEvaluation private constructor( + private val firstElement: FIRST_ELEMENT, + private val firstElementEvaluations: Set>, + private val secondElement: SECOND_ELEMENT, + private val secondElementEvaluations: Set>, + private val thirdElement: THIRD_ELEMENT, + private val thirdElementEvaluations: Set>, + private val fourthElement: FOURTH_ELEMENT, + private val fourthElementEvaluations: Set> + ) { + + fun addEvaluation( + error: () -> ERROR, + check: (FOURTH_ELEMENT) -> Boolean + ) = + FourLevelPreparedEvaluation( + this.firstElement, + this.firstElementEvaluations, + this.secondElement, + this.secondElementEvaluations, + this.thirdElement, + this.thirdElementEvaluations, + this.fourthElement, + this.fourthElementEvaluations + .plus( + Evaluation + .of( + error, + check + ) + ) + ) + + fun fifth( + fifthElement: FIFTH_ELEMENT + ) = + FifthLevelNonPreparedEvaluation + .of( + this.firstElement, + this.firstElementEvaluations, + this.secondElement, + this.secondElementEvaluations, + this.thirdElement, + this.thirdElementEvaluations, + this.fourthElement, + this.fourthElementEvaluations, + fifthElement + ) + + internal companion object Builder { + + internal fun of( + firstElement: FIRST_ELEMENT, + firstElementEvaluations: Set>, + secondElement: SECOND_ELEMENT, + secondElementEvaluations: Set>, + thirdElement: THIRD_ELEMENT, + thirdElementEvaluations: Set>, + fourthElement: FOURTH_ELEMENT, + fourthElementEvaluation: Evaluation + ) = + FourLevelPreparedEvaluation( + firstElement, + firstElementEvaluations, + secondElement, + secondElementEvaluations, + thirdElement, + thirdElementEvaluations, + fourthElement, + setOf( + fourthElementEvaluation + ) + ) + } + } + + + class FifthLevelNonPreparedEvaluation private constructor( + private val firstElement: FIRST_ELEMENT, + private val firstElementEvaluations: Set>, + private val secondElement: SECOND_ELEMENT, + private val secondElementEvaluations: Set>, + private val thirdElement: THIRD_ELEMENT, + private val thirdElementEvaluations: Set>, + private val fourthElement: FOURTH_ELEMENT, + private val fourthElementEvaluations: Set>, + private val fifthElement: FIFTH_ELEMENT + ) { + + fun addEvaluation( + error: () -> ERROR, + check: (FIFTH_ELEMENT) -> Boolean + ) = + FifthLevelPreparedEvaluation + .of( + this.firstElement, + this.firstElementEvaluations, + this.secondElement, + this.secondElementEvaluations, + this.thirdElement, + this.thirdElementEvaluations, + this.fourthElement, + this.fourthElementEvaluations, + this.fifthElement, + Evaluation + .of( + error, + check + ) + ) + + internal companion object Builder { + + internal fun of( + firstElement: FIRST_ELEMENT, + firstElementEvaluations: Set>, + secondElement: SECOND_ELEMENT, + secondElementEvaluations: Set>, + thirdElement: THIRD_ELEMENT, + thirdElementEvaluations: Set>, + fourthElement: FOURTH_ELEMENT, + fourthElementEvaluations: Set>, + fifthElement: FIFTH_ELEMENT + ) = + FifthLevelNonPreparedEvaluation( + firstElement, + firstElementEvaluations, + secondElement, + secondElementEvaluations, + thirdElement, + thirdElementEvaluations, + fourthElement, + fourthElementEvaluations, + fifthElement + ) + } + } + + class FifthLevelPreparedEvaluation private constructor( + private val firstElement: FIRST_ELEMENT, + private val firstElementEvaluations: Set>, + private val secondElement: SECOND_ELEMENT, + private val secondElementEvaluations: Set>, + private val thirdElement: THIRD_ELEMENT, + private val thirdElementEvaluations: Set>, + private val fourthElement: FOURTH_ELEMENT, + private val fourthElementEvaluations: Set>, + private val fifthElement: FIFTH_ELEMENT, + private val fifthElementEvaluations: Set> + ) { + + fun addEvaluation( + error: () -> ERROR, + check: (FIFTH_ELEMENT) -> Boolean + ) = + FifthLevelPreparedEvaluation( + this.firstElement, + this.firstElementEvaluations, + this.secondElement, + this.secondElementEvaluations, + this.thirdElement, + this.thirdElementEvaluations, + this.fourthElement, + this.fourthElementEvaluations, + this.fifthElement, + this.fifthElementEvaluations + .plus( + Evaluation + .of( + error, + check + ) + ) + ) + + fun evaluate() = + evaluateFirst() + .toErrors() + .plus( + evaluateSecond().toErrors() + ) + .plus( + evaluateThird().toErrors() + ) + .plus( + evaluateFourth().toErrors() + ) + .plus( + evaluateFifth().toErrors() + ) + .toSet() + .toSuccessOrMultipleErrors( + Quintuple.of( + this.firstElement, + this.secondElement, + this.thirdElement, + this.fourthElement, + this.fifthElement + ) + ) + + private fun evaluateFirst() = + evaluate( + this.firstElementEvaluations, + this.firstElement + ) + + private fun evaluateSecond() = + evaluate( + this.secondElementEvaluations, + this.secondElement + ) + + private fun evaluateThird() = + evaluate( + this.thirdElementEvaluations, + this.thirdElement + ) + + private fun evaluateFourth() = + evaluate( + this.fourthElementEvaluations, + this.fourthElement + ) + + private fun evaluateFifth() = + evaluate( + this.fifthElementEvaluations, + this.fifthElement + ) + + internal companion object Builder { + + internal fun of( + firstElement: FIRST_ELEMENT, + firstElementEvaluations: Set>, + secondElement: SECOND_ELEMENT, + secondElementEvaluations: Set>, + thirdElement: THIRD_ELEMENT, + thirdElementEvaluations: Set>, + fourthElement: FOURTH_ELEMENT, + fourthElementEvaluations: Set>, + fifthElement: FIFTH_ELEMENT, + fifthElementEvaluation: Evaluation + ) = + FifthLevelPreparedEvaluation( + firstElement, + firstElementEvaluations, + secondElement, + secondElementEvaluations, + thirdElement, + thirdElementEvaluations, + fourthElement, + fourthElementEvaluations, + fifthElement, + setOf( + fifthElementEvaluation + ) + ) + } + } + } + } + } + } + } + } + + @ConsistentCopyVisibility + internal data class Evaluation private constructor( + val error: () -> ERROR, + val check: (ELEMENT) -> Boolean + ) { + internal fun evaluate( + element: ELEMENT + ) = + if (this.check(element)) + PossibleError.Success.of() + else + PossibleError.Error.of(this.error()) + + internal companion object Builder { + + internal fun of( + error: () -> ERROR, + check: (ELEMENT) -> Boolean + ) = + Evaluation( + error, + check + ) + } + } +} + +private fun evaluate( + evaluations: Set>, + element: ELEMENT +) = + evaluations + .map { it.evaluate(element) } + .filter { it.error() } + .map { it.toMaybeError() } + .filter { it.present() } + .map { it.orNull() } + .map { it!! } + .toSuccessOrMultipleErrors(element) + +private fun Collection.toSuccessOrMultipleErrors(element: ELEMENT) = + if (this.isEmpty()) + SuccessOrMultipleErrors.Success.of(element) + else + SuccessOrMultipleErrors.Error + .of(this.first()) + .addErrors(this.drop(1)) \ No newline at end of file diff --git a/library/src/commonTest/kotlin/com/benromdhane/omar/offroadsoft/monad/evaluation/EvaluateFiveElementsTest.kt b/library/src/commonTest/kotlin/com/benromdhane/omar/offroadsoft/monad/evaluation/EvaluateFiveElementsTest.kt new file mode 100644 index 0000000..fffb385 --- /dev/null +++ b/library/src/commonTest/kotlin/com/benromdhane/omar/offroadsoft/monad/evaluation/EvaluateFiveElementsTest.kt @@ -0,0 +1,124 @@ +package com.benromdhane.omar.offroadsoft.monad.evaluation + +import io.kotest.assertions.assertSoftly +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertContains +import kotlin.test.assertEquals +import kotlin.uuid.ExperimentalUuidApi +import kotlin.uuid.Uuid + +@OptIn(ExperimentalUuidApi::class) +class EvaluateFiveElementsTest { + + @Test + fun `evaluate must return success success or multiple errors if one valid evaluation for first element and one valid evaluation for second and one valid evaluation for third and one valid evaluation for fourth and one valid evaluation for fifth was provided`() { + val firstElementToEvaluate = Uuid.random().toString() + val secondElementToEvaluate = Random.nextInt() + val thirdElementToEvaluate = Random.nextLong() + val fourthElementToEvaluate = Random.nextDouble() + val fifthElementToEvaluate = Random.nextFloat() + val result = + EvaluateFiveElements + .first<_, Throwable>(firstElementToEvaluate) + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .second(secondElementToEvaluate) + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .third(thirdElementToEvaluate) + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .fourth(fourthElementToEvaluate) + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .fifth(fifthElementToEvaluate) + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .evaluate() + .toMaybeSuccess() + .orNull()!! + + assertSoftly { + assertEquals(firstElementToEvaluate, result.firstElement) + assertEquals(secondElementToEvaluate, result.secondElement) + assertEquals(thirdElementToEvaluate, result.thirdElement) + assertEquals(fourthElementToEvaluate, result.fourthElement) + assertEquals(fifthElementToEvaluate, result.fifthElement) + } + } + + @Test + fun `evaluate must return success success or multiple errors if multiple valid evaluations for first element and multiple valid evaluations for second and multiple valid evaluations for third and multiple valid evaluations for fourth and multiple valid evaluations for fifth was provided`() { + val firstElementToEvaluate = Uuid.random().toString() + val secondElementToEvaluate = Random.nextInt() + val thirdElementToEvaluate = Random.nextLong() + val fourthElementToEvaluate = Random.nextDouble() + val fifthElementToEvaluate = Random.nextFloat() + val result = + EvaluateFiveElements + .first<_, Throwable>(firstElementToEvaluate) + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .second(secondElementToEvaluate) + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .third(thirdElementToEvaluate) + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .fourth(fourthElementToEvaluate) + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .fifth(fifthElementToEvaluate) + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .addEvaluation({ Exception(Uuid.random().toString()) }) { true } + .evaluate() + .toMaybeSuccess() + .orNull()!! + + assertSoftly { + assertEquals(firstElementToEvaluate, result.firstElement) + assertEquals(secondElementToEvaluate, result.secondElement) + assertEquals(thirdElementToEvaluate, result.thirdElement) + assertEquals(fourthElementToEvaluate, result.fourthElement) + assertEquals(fifthElementToEvaluate, result.fifthElement) + } + } + + @Test + fun `evaluate must return failure success or multiple errors if one of multiple evaluations for first element is invalid and one of multiple evaluations for second is invalid and one of multiple evaluations for third and one of multiple evaluations for fourth is invalid and one of multiple evaluations for fifth is invalid was provided`() { + val firstElementError1 = Exception(Uuid.random().toString()) + val firstElementError2 = Exception(Uuid.random().toString()) + val secondElementError1 = Exception(Uuid.random().toString()) + val secondElementError2 = Exception(Uuid.random().toString()) + val thirdElementError1 = Exception(Uuid.random().toString()) + val thirdElementError2 = Exception(Uuid.random().toString()) + val fourthElementError1 = Exception(Uuid.random().toString()) + val fourthElementError2 = Exception(Uuid.random().toString()) + val fifthElementError1 = Exception(Uuid.random().toString()) + val fifthElementError2 = Exception(Uuid.random().toString()) + val result = + EvaluateFiveElements + .first<_, Throwable>(Uuid.random().toString()) + .addEvaluation({ firstElementError1 }) { true } + .addEvaluation({ firstElementError2 }) { false } + .second(Random.nextInt()) + .addEvaluation({ secondElementError1 }) { false } + .addEvaluation({ secondElementError2 }) { true } + .third(Random.nextLong()) + .addEvaluation({ thirdElementError1 }) { true } + .addEvaluation({ thirdElementError2 }) { false } + .fourth(Random.nextDouble()) + .addEvaluation({ fourthElementError1 }) { false } + .addEvaluation({ fourthElementError2 }) { true } + .fifth(Random.nextFloat()) + .addEvaluation({ fifthElementError1 }) { true } + .addEvaluation({ fifthElementError2 }) { false } + .evaluate() + .toErrors() + + assertSoftly { + assertEquals(5, result.size) + assertContains(result, firstElementError2) + assertContains(result, secondElementError1) + assertContains(result, thirdElementError2) + assertContains(result, fourthElementError1) + assertContains(result, fifthElementError2) + } + } +} \ No newline at end of file