Skip to content

Commit 30ab0e7

Browse files
Add Validate Dsl
1 parent 4c42f30 commit 30ab0e7

File tree

18 files changed

+1387
-86
lines changed

18 files changed

+1387
-86
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Added
66

7+
- Validate Dsl
78
- `Either` data structure
89
- `identity` function
910

kotlin-stdlib/api/android/kotlin-stdlib.api

Lines changed: 169 additions & 29 deletions
Large diffs are not rendered by default.

kotlin-stdlib/api/jvm/kotlin-stdlib.api

Lines changed: 173 additions & 29 deletions
Large diffs are not rendered by default.

kotlin-stdlib/api/kotlin-stdlib.api

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,66 @@ public class com/javiersc/kotlin/stdlib/DirScope : com/javiersc/kotlin/stdlib/Fi
180180
public static synthetic fun dir$default (Lcom/javiersc/kotlin/stdlib/DirScope;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/javiersc/kotlin/stdlib/DirScope;
181181
}
182182

183+
public abstract class com/javiersc/kotlin/stdlib/Either {
184+
public final fun fold (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
185+
public final fun getOrNull ()Ljava/lang/Object;
186+
public final fun isLeft ()Z
187+
public final fun isLeft (Lkotlin/jvm/functions/Function1;)Z
188+
public final fun isRight ()Z
189+
public final fun isRight (Lkotlin/jvm/functions/Function1;)Z
190+
public final fun leftOrNull ()Ljava/lang/Object;
191+
public final fun map (Lkotlin/jvm/functions/Function1;)Lcom/javiersc/kotlin/stdlib/Either;
192+
public final fun mapLeft (Lkotlin/jvm/functions/Function1;)Lcom/javiersc/kotlin/stdlib/Either;
193+
public final fun onLeft (Lkotlin/jvm/functions/Function1;)Lcom/javiersc/kotlin/stdlib/Either;
194+
public final fun onRight (Lkotlin/jvm/functions/Function1;)Lcom/javiersc/kotlin/stdlib/Either;
195+
public final fun swap ()Lcom/javiersc/kotlin/stdlib/Either;
196+
public fun toString ()Ljava/lang/String;
197+
}
198+
199+
public final class com/javiersc/kotlin/stdlib/Either$Left : com/javiersc/kotlin/stdlib/Either {
200+
public static final field Companion Lcom/javiersc/kotlin/stdlib/Either$Left$Companion;
201+
public fun <init> (Ljava/lang/Object;)V
202+
public final fun component1 ()Ljava/lang/Object;
203+
public final fun copy (Ljava/lang/Object;)Lcom/javiersc/kotlin/stdlib/Either$Left;
204+
public static synthetic fun copy$default (Lcom/javiersc/kotlin/stdlib/Either$Left;Ljava/lang/Object;ILjava/lang/Object;)Lcom/javiersc/kotlin/stdlib/Either$Left;
205+
public fun equals (Ljava/lang/Object;)Z
206+
public final fun getValue ()Ljava/lang/Object;
207+
public fun hashCode ()I
208+
public fun toString ()Ljava/lang/String;
209+
}
210+
211+
public final class com/javiersc/kotlin/stdlib/Either$Left$Companion {
212+
}
213+
214+
public final class com/javiersc/kotlin/stdlib/Either$Right : com/javiersc/kotlin/stdlib/Either {
215+
public static final field Companion Lcom/javiersc/kotlin/stdlib/Either$Right$Companion;
216+
public fun <init> (Ljava/lang/Object;)V
217+
public final fun component1 ()Ljava/lang/Object;
218+
public final fun copy (Ljava/lang/Object;)Lcom/javiersc/kotlin/stdlib/Either$Right;
219+
public static synthetic fun copy$default (Lcom/javiersc/kotlin/stdlib/Either$Right;Ljava/lang/Object;ILjava/lang/Object;)Lcom/javiersc/kotlin/stdlib/Either$Right;
220+
public fun equals (Ljava/lang/Object;)Z
221+
public final fun getValue ()Ljava/lang/Object;
222+
public fun hashCode ()I
223+
public fun toString ()Ljava/lang/String;
224+
}
225+
226+
public final class com/javiersc/kotlin/stdlib/Either$Right$Companion {
227+
}
228+
229+
public final class com/javiersc/kotlin/stdlib/EitherKt {
230+
public static final fun combine (Lcom/javiersc/kotlin/stdlib/Either;Lcom/javiersc/kotlin/stdlib/Either;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Lcom/javiersc/kotlin/stdlib/Either;
231+
public static final fun compareTo (Lcom/javiersc/kotlin/stdlib/Either;Lcom/javiersc/kotlin/stdlib/Either;)I
232+
public static final fun flatMap (Lcom/javiersc/kotlin/stdlib/Either;Lkotlin/jvm/functions/Function1;)Lcom/javiersc/kotlin/stdlib/Either;
233+
public static final fun flatten (Lcom/javiersc/kotlin/stdlib/Either;)Lcom/javiersc/kotlin/stdlib/Either;
234+
public static final fun getOrElse (Lcom/javiersc/kotlin/stdlib/Either;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object;
235+
public static final fun handleErrorWith (Lcom/javiersc/kotlin/stdlib/Either;Lkotlin/jvm/functions/Function1;)Lcom/javiersc/kotlin/stdlib/Either;
236+
public static final fun left (Ljava/lang/Object;)Lcom/javiersc/kotlin/stdlib/Either;
237+
public static final fun leftNel (Ljava/lang/Object;)Lcom/javiersc/kotlin/stdlib/Either;
238+
public static final fun merge (Lcom/javiersc/kotlin/stdlib/Either;)Ljava/lang/Object;
239+
public static final fun right (Ljava/lang/Object;)Lcom/javiersc/kotlin/stdlib/Either;
240+
public static final fun toEitherNel (Lcom/javiersc/kotlin/stdlib/Either;)Lcom/javiersc/kotlin/stdlib/Either;
241+
}
242+
183243
public class com/javiersc/kotlin/stdlib/FileScope : java/io/File {
184244
public fun <init> (Ljava/io/File;)V
185245
public final fun file (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lcom/javiersc/kotlin/stdlib/FileScope;
@@ -197,6 +257,10 @@ public final class com/javiersc/kotlin/stdlib/FilesKt {
197257
public static synthetic fun root$default (Ljava/io/File;Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/javiersc/kotlin/stdlib/DirScope;
198258
}
199259

260+
public final class com/javiersc/kotlin/stdlib/IdentityKt {
261+
public static final fun identity (Ljava/lang/Object;)Ljava/lang/Object;
262+
}
263+
200264
public final class com/javiersc/kotlin/stdlib/StringsKt {
201265
public static final fun endWithNewLine (Ljava/lang/String;)Ljava/lang/String;
202266
public static final fun getEmpty (Lkotlin/jvm/internal/StringCompanionObject;)Ljava/lang/String;
@@ -441,3 +505,26 @@ public final class com/javiersc/kotlin/stdlib/tree/TreeNodeIterators : java/lang
441505
public static fun values ()[Lcom/javiersc/kotlin/stdlib/tree/TreeNodeIterators;
442506
}
443507

508+
public final class com/javiersc/kotlin/stdlib/validate/Validator {
509+
public static final field Companion Lcom/javiersc/kotlin/stdlib/validate/Validator$Companion;
510+
public fun <init> (Ljava/lang/String;Lcom/javiersc/kotlin/stdlib/validate/ValidatorScope;)V
511+
public final fun getName ()Ljava/lang/String;
512+
public final fun getValidatorScope ()Lcom/javiersc/kotlin/stdlib/validate/ValidatorScope;
513+
}
514+
515+
public final class com/javiersc/kotlin/stdlib/validate/Validator$Companion {
516+
}
517+
518+
public final class com/javiersc/kotlin/stdlib/validate/ValidatorScope {
519+
public field value Ljava/lang/Object;
520+
public fun <init> ()V
521+
public final fun getErrors ()Ljava/util/List;
522+
public final fun getOnValidate ()Lkotlin/jvm/functions/Function0;
523+
public final fun getValue ()Ljava/lang/Object;
524+
public final fun invalid (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;)V
525+
public final fun invalidIfIsEmpty (Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V
526+
public static synthetic fun invalidIfIsEmpty$default (Lcom/javiersc/kotlin/stdlib/validate/ValidatorScope;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V
527+
public final fun setOnValidate (Lkotlin/jvm/functions/Function0;)V
528+
public final fun setValue (Ljava/lang/Object;)V
529+
}
530+

kotlin-stdlib/common/main/kotlin/com/javiersc/kotlin/stdlib/AnsiColors.kt

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.javiersc.kotlin.stdlib
22

33
public inline fun String.ansiColor(color: AnsiColor): String =
4-
lines().joinToString("\n") { line -> "${color}$line${AnsiColor.Reset}" }
4+
lines().joinToString("\n") { line ->
5+
if (line.isNotBlank()) "${color}$line${AnsiColor.Reset}" else line
6+
}
57

6-
public abstract class AnsiColor {
8+
public sealed class AnsiColor {
79

810
public abstract val value: String
911

@@ -13,102 +15,104 @@ public abstract class AnsiColor {
1315
override val value: String = "\u001B[0m"
1416
}
1517

16-
public object Foreground {
17-
public object Black : AnsiColor() {
18+
public sealed class Foreground : AnsiColor() {
19+
20+
public object Black : Foreground() {
1821
override val value: String = "\u001B[30m"
1922
}
2023

21-
public object Red : AnsiColor() {
24+
public object Red : Foreground() {
2225
override val value: String = "\u001B[31m"
2326
}
2427

25-
public object Green : AnsiColor() {
28+
public object Green : Foreground() {
2629
override val value: String = "\u001B[32m"
2730
}
2831

29-
public object Yellow : AnsiColor() {
32+
public object Yellow : Foreground() {
3033
override val value: String = "\u001B[33m"
3134
}
3235

33-
public object Blue : AnsiColor() {
36+
public object Blue : Foreground() {
3437
override val value: String = "\u001B[34m"
3538
}
3639

37-
public object Purple : AnsiColor() {
40+
public object Purple : Foreground() {
3841
override val value: String = "\u001B[35m"
3942
}
4043

41-
public object Cyan : AnsiColor() {
44+
public object Cyan : Foreground() {
4245
override val value: String = "\u001B[36m"
4346
}
4447

45-
public object White : AnsiColor() {
48+
public object White : Foreground() {
4649
override val value: String = "\u001B[37m"
4750
}
4851

49-
public object BrightBlack : AnsiColor() {
52+
public object BrightBlack : Foreground() {
5053
override val value: String = "\u001B[90m"
5154
}
5255

53-
public object BrightRed : AnsiColor() {
56+
public object BrightRed : Foreground() {
5457
override val value: String = "\u001B[91m"
5558
}
5659

57-
public object BrightGreen : AnsiColor() {
60+
public object BrightGreen : Foreground() {
5861
override val value: String = "\u001B[92m"
5962
}
6063

61-
public object BrightYellow : AnsiColor() {
64+
public object BrightYellow : Foreground() {
6265
override val value: String = "\u001B[93m"
6366
}
6467

65-
public object BrightBlue : AnsiColor() {
68+
public object BrightBlue : Foreground() {
6669
override val value: String = "\u001B[94m"
6770
}
6871

69-
public object BrightPurple : AnsiColor() {
72+
public object BrightPurple : Foreground() {
7073
override val value: String = "\u001B[95m"
7174
}
7275

73-
public object BrightCyan : AnsiColor() {
76+
public object BrightCyan : Foreground() {
7477
override val value: String = "\u001B[96m"
7578
}
7679

77-
public object BrightWhite : AnsiColor() {
80+
public object BrightWhite : Foreground() {
7881
override val value: String = "\u001B[97m"
7982
}
8083
}
8184

82-
public object Background {
83-
public object Black : AnsiColor() {
85+
public sealed class Background : AnsiColor() {
86+
87+
public object Black : Background() {
8488
override val value: String = "\u001B[40m"
8589
}
8690

87-
public object Red : AnsiColor() {
91+
public object Red : Background() {
8892
override val value: String = "\u001B[41m"
8993
}
9094

91-
public object Green : AnsiColor() {
95+
public object Green : Background() {
9296
override val value: String = "\u001B[42m"
9397
}
9498

95-
public object Yellow : AnsiColor() {
99+
public object Yellow : Background() {
96100
override val value: String = "\u001b[43m"
97101
}
98102

99-
public object Blue : AnsiColor() {
103+
public object Blue : Background() {
100104
override val value: String = "\u001B[44m"
101105
}
102106

103-
public object Purple : AnsiColor() {
107+
public object Purple : Background() {
104108
override val value: String = "\u001B[45m"
105109
}
106110

107-
public object Cyan : AnsiColor() {
111+
public object Cyan : Background() {
108112
override val value: String = "\u001B[46m"
109113
}
110114

111-
public object Gray : AnsiColor() {
115+
public object Gray : Background() {
112116
override val value: String = "\u001B[47m"
113117
}
114118
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.javiersc.kotlin.stdlib
2+
3+
internal annotation class UseContextParameters
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.javiersc.kotlin.stdlib.validate
2+
3+
import com.javiersc.kotlin.stdlib.Either
4+
import com.javiersc.kotlin.stdlib.UseContextParameters
5+
6+
public class Validator<V : Any>
7+
@PublishedApi
8+
internal constructor(
9+
@PublishedApi internal val name: String,
10+
@PublishedApi internal val block: ValidatorScope<V>.(V) -> Unit,
11+
) {
12+
13+
public inline fun <reified A : V> validate(value: A): Either<String, A> {
14+
val validatorScope = ValidatorScope<V>()
15+
block(validatorScope, value)
16+
17+
val otherwises: List<String> =
18+
validatorScope.validatables.mapNotNull { if (it.predicate()) it.otherwise() else null }
19+
20+
if (otherwises.isEmpty()) return Either.Right(value)
21+
22+
val sanitizedOtherwises: List<String> = buildList {
23+
addAll(otherwises)
24+
25+
if (otherwises.count { it.contains(InvalidProperty) } > 1) {
26+
removeAll { it.contains(InvalidProperty) }
27+
add(MultipleInvalidProperties)
28+
}
29+
30+
if (otherwises.count { it.contains(EmptyProperty) } > 1) {
31+
removeAll { it.contains(EmptyProperty) }
32+
add(MultipleEmptyProperties)
33+
}
34+
}
35+
36+
val title = "'$name' is invalid due to:\n"
37+
val otherwisesAsText: String =
38+
sanitizedOtherwises.joinToString("\n") { message -> " - $message" }
39+
val errorMessage: String = title + otherwisesAsText
40+
return Either.Left(value = errorMessage)
41+
}
42+
43+
public companion object {
44+
45+
public inline operator fun <reified T : Any> invoke(
46+
name: String = T::class.simpleName ?: "Validator",
47+
@UseContextParameters noinline block: ValidatorScope<T>.(T) -> Unit,
48+
): Lazy<Validator<T>> = lazy { Validator(name = name, block = block) }
49+
}
50+
}
51+
52+
@PublishedApi internal const val InvalidProperty: String = "Invalid property"
53+
@PublishedApi internal const val MultipleInvalidProperties: String = "Multiple invalid properties"
54+
@PublishedApi internal const val EmptyProperty: String = "Empty property"
55+
@PublishedApi internal const val MultipleEmptyProperties: String = "Multiple empty properties"
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.javiersc.kotlin.stdlib.validate
2+
3+
import com.javiersc.kotlin.stdlib.UseContextParameters
4+
5+
public class ValidatorScope<V : Any>
6+
@PublishedApi
7+
internal constructor(private val _validatables: MutableList<Validatable> = mutableListOf()) {
8+
9+
@PublishedApi
10+
internal val validatables: List<Validatable>
11+
get() = _validatables.toList()
12+
13+
public fun invalid(predicate: () -> Boolean, otherwise: () -> String) {
14+
_validatables.add(Validatable(predicate = predicate, otherwise = otherwise))
15+
}
16+
17+
public fun invalidIf(predicate: () -> Boolean) {
18+
invalid(predicate = predicate, otherwise = { InvalidProperty })
19+
}
20+
21+
public fun valid(predicate: () -> Boolean, otherwise: () -> String) {
22+
_validatables.add(Validatable(predicate = { !predicate() }, otherwise = otherwise))
23+
}
24+
25+
public fun validIf(predicate: () -> Boolean) {
26+
valid(predicate = predicate, otherwise = { InvalidProperty })
27+
}
28+
29+
public operator fun <T> T.invoke(block: T.() -> Unit): T {
30+
block()
31+
return this
32+
}
33+
34+
public fun <T> validationOf(vararg values: T, block: T.() -> Unit) {
35+
val validatableList: ValidatableList<T> = ValidatableList(values.toList())
36+
validatableList.invoke(block)
37+
}
38+
39+
public infix fun <T> T.and(value: T): ValidatableList<T> = ValidatableList(listOf(this, value))
40+
41+
public infix fun <T> ValidatableList<T>.and(value: T): ValidatableList<T> =
42+
ValidatableList(values + value)
43+
44+
@UseContextParameters
45+
public fun String.invalidIfIsEmpty(otherwise: () -> String = { EmptyProperty }) {
46+
invalid(predicate = ::isEmpty, otherwise = otherwise)
47+
}
48+
49+
@PublishedApi
50+
internal data class Validatable(val predicate: () -> Boolean, val otherwise: () -> String)
51+
52+
public class ValidatableList<T>(public val values: List<T>) : List<T> by values {
53+
54+
public operator fun invoke(block: T.() -> Unit) {
55+
for (value: T in values) block(value)
56+
}
57+
}
58+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.javiersc.kotlin.stdlib.validate
2+
3+
import com.javiersc.kotlin.stdlib.Either
4+
5+
public inline fun <reified T : Any> T.validateWith(validator: Validator<T>): Either<String, T> {
6+
val validated: Either<String, T> = validator.validate(this)
7+
return validated
8+
}

0 commit comments

Comments
 (0)