Skip to content

Commit d787111

Browse files
authored
Adds validNel and invalidNel extension functions (#123)
Backports validNel and invalidNel extension functions which were originally available in Validated, but recently removed in Arrow 2.x. Adds `shouldBeValid` and `shouldBeInvalid` matchers.
1 parent 3846858 commit d787111

File tree

5 files changed

+59
-0
lines changed

5 files changed

+59
-0
lines changed

lib/api/lib.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,10 @@ public final class app/cash/quiver/extensions/SuspendedFunctionKt {
234234
public final class app/cash/quiver/extensions/ValidatedKt {
235235
public static final fun attemptValidated (Larrow/core/Either;)Larrow/core/Either;
236236
public static final fun concatMap (Larrow/core/Either;Lkotlin/jvm/functions/Function1;)Larrow/core/Either;
237+
public static final fun invalidNel (Ljava/lang/Object;)Larrow/core/Either;
237238
public static final fun takeLeft (Larrow/core/Either;Larrow/core/Either;)Larrow/core/Either;
238239
public static final fun takeRight (Larrow/core/Either;Larrow/core/Either;)Larrow/core/Either;
240+
public static final fun validNel (Ljava/lang/Object;)Larrow/core/Either;
239241
public static final fun validate (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Larrow/core/Either;
240242
public static final fun validateEither (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Larrow/core/Either;
241243
public static final fun validateMap (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Larrow/core/Either;

lib/src/main/kotlin/app/cash/quiver/extensions/Validated.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,17 @@ inline fun <ERR, A, B> A.validateMap(
8787
*/
8888
inline fun <ERR, A, B> ValidatedNel<ERR, A>.concatMap(f: (A) -> ValidatedNel<ERR, B>): ValidatedNel<ERR, B> =
8989
this.flatMap { f(it) }
90+
91+
/**
92+
* An extension method for constructing a valid ValidatedNel.
93+
*/
94+
fun <A> A.validNel(): ValidatedNel<Nothing, A> =
95+
this.right()
96+
97+
98+
/**
99+
* An extension method for constructing an invalid ValidatedNel.
100+
*/
101+
fun <E> E.invalidNel(): ValidatedNel<E, Nothing> =
102+
nonEmptyListOf(this).left()
103+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package app.cash.quiver.extensions
2+
3+
import app.cash.quiver.matchers.shouldBeInvalid
4+
import app.cash.quiver.matchers.shouldBeValid
5+
import io.kotest.core.spec.style.StringSpec
6+
import io.kotest.matchers.shouldBe
7+
8+
class ValidatedTest : StringSpec({
9+
"validNel creates an Either.Right<A>" {
10+
"hello world".validNel().shouldBeValid() shouldBe "hello world"
11+
}
12+
13+
"invalidNel creates an Either.Left<NonEmptyList<E>>" {
14+
"uh oh!".invalidNel().shouldBeInvalid() shouldBe setOf("uh oh!")
15+
}
16+
})

testing-lib/api/testing-lib.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public final class app/cash/quiver/matchers/MatchersKt {
99
public static final fun shouldBeAbsent (Lapp/cash/quiver/Outcome;Lkotlin/jvm/functions/Function1;)V
1010
public static synthetic fun shouldBeAbsent$default (Lapp/cash/quiver/Outcome;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
1111
public static final fun shouldBeFailure (Lapp/cash/quiver/Outcome;)Ljava/lang/Object;
12+
public static final fun shouldBeInvalid (Larrow/core/Either;)Ljava/util/Set;
1213
public static final fun shouldBePresent (Lapp/cash/quiver/Outcome;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
1314
public static synthetic fun shouldBePresent$default (Lapp/cash/quiver/Outcome;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ljava/lang/Object;
1415
}

testing-lib/src/main/kotlin/app/cash/quiver/matchers/Matchers.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import app.cash.quiver.Absent
44
import app.cash.quiver.Failure
55
import app.cash.quiver.Outcome
66
import app.cash.quiver.Present
7+
import app.cash.quiver.extensions.ValidatedNel
8+
import arrow.core.Either
9+
import arrow.core.NonEmptyList
710
import kotlin.contracts.ExperimentalContracts
811
import kotlin.contracts.contract
912

@@ -42,3 +45,26 @@ fun <A, B> Outcome<A, B>.shouldBeFailure(): A {
4245
is Present -> throw AssertionError("Expected Outcome.Failure but got Present($value)")
4346
}
4447
}
48+
49+
@OptIn(ExperimentalContracts::class)
50+
inline fun <E, reified A> ValidatedNel<E, A>.shouldBeValid(): A {
51+
contract {
52+
returns() implies (this@shouldBeValid is A)
53+
}
54+
55+
return when(this) {
56+
is Either.Left -> throw AssertionError("Expected Right (Valid), but found $this")
57+
is Either.Right -> value
58+
}
59+
}
60+
@OptIn(ExperimentalContracts::class)
61+
fun <E, A> ValidatedNel<E, A>.shouldBeInvalid(): Set<E> {
62+
contract {
63+
returns() implies (this@shouldBeInvalid is Set<*>)
64+
}
65+
66+
return when(this) {
67+
is Either.Left -> value.toSet()
68+
is Either.Right -> throw AssertionError("Expected Left (Invalid), but found $this")
69+
}
70+
}

0 commit comments

Comments
 (0)