Skip to content

Commit

Permalink
Add ktlint and format (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
dhoepelman authored Apr 1, 2024
1 parent 4a7541c commit da9f26c
Show file tree
Hide file tree
Showing 13 changed files with 464 additions and 346 deletions.
12 changes: 8 additions & 4 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
max_line_length = off
trim_trailing_whitespace = false
max_line_length = 140
end_of_line = lf

[*.{yml,yaml}]
indent_size = 2

[*.{kt,kts}]
ktlint_code_style = ktlint_official
ij_kotlin_packages_to_use_import_on_demand = unset
ij_kotlin_name_count_to_use_star_import = 99
ij_kotlin_name_count_to_use_star_import_for_members = 99
10 changes: 7 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ plugins {
id("maven-publish")
id("signing")
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
id("org.jlleitschuh.gradle.ktlint") version "12.1.0"
idea
}

Expand Down Expand Up @@ -98,11 +99,14 @@ kotlin {
}
}
}

val javaDocJar = tasks.register<Jar>("stubJavadoc") {
archiveClassifier.set("javadoc")
configure<org.jlleitschuh.gradle.ktlint.KtlintExtension> {
version.set("1.2.1")
}

val javaDocJar =
tasks.register<Jar>("stubJavadoc") {
archiveClassifier.set("javadoc")
}

publishing {
publications {
Expand Down
3 changes: 1 addition & 2 deletions src/commonMain/kotlin/io/konform/validation/Validation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package io.konform.validation
import io.konform.validation.internal.ValidationBuilderImpl

interface Validation<T> {

companion object {
operator fun <T> invoke(init: ValidationBuilder<T>.() -> Unit): Validation<T> {
val builder = ValidationBuilderImpl<T>()
Expand All @@ -12,8 +11,8 @@ interface Validation<T> {
}

fun validate(value: T): ValidationResult<T>

operator fun invoke(value: T) = validate(value)
}


class Constraint<R> internal constructor(val hint: String, val templateValues: List<String>, val test: (R) -> Boolean)
34 changes: 30 additions & 4 deletions src/commonMain/kotlin/io/konform/validation/ValidationBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,47 @@ private annotation class ValidationScope
@ValidationScope
abstract class ValidationBuilder<T> {
abstract fun build(): Validation<T>
abstract fun addConstraint(errorMessage: String, vararg templateValues: String, test: (T) -> Boolean): Constraint<T>

abstract fun addConstraint(
errorMessage: String,
vararg templateValues: String,
test: (T) -> Boolean,
): Constraint<T>

abstract infix fun Constraint<T>.hint(hint: String): Constraint<T>

abstract operator fun <R> KProperty1<T, R>.invoke(init: ValidationBuilder<R>.() -> Unit)
internal abstract fun <R> onEachIterable(prop: KProperty1<T, Iterable<R>>, init: ValidationBuilder<R>.() -> Unit)

internal abstract fun <R> onEachIterable(
prop: KProperty1<T, Iterable<R>>,
init: ValidationBuilder<R>.() -> Unit,
)

@JvmName("onEachIterable")
infix fun <R> KProperty1<T, Iterable<R>>.onEach(init: ValidationBuilder<R>.() -> Unit) = onEachIterable(this, init)
internal abstract fun <R> onEachArray(prop: KProperty1<T, Array<R>>, init: ValidationBuilder<R>.() -> Unit)

internal abstract fun <R> onEachArray(
prop: KProperty1<T, Array<R>>,
init: ValidationBuilder<R>.() -> Unit,
)

@JvmName("onEachArray")
infix fun <R> KProperty1<T, Array<R>>.onEach(init: ValidationBuilder<R>.() -> Unit) = onEachArray(this, init)
internal abstract fun <K, V> onEachMap(prop: KProperty1<T, Map<K, V>>, init: ValidationBuilder<Map.Entry<K, V>>.() -> Unit)

internal abstract fun <K, V> onEachMap(
prop: KProperty1<T, Map<K, V>>,
init: ValidationBuilder<Map.Entry<K, V>>.() -> Unit,
)

@JvmName("onEachMap")
infix fun <K, V> KProperty1<T, Map<K, V>>.onEach(init: ValidationBuilder<Map.Entry<K, V>>.() -> Unit) = onEachMap(this, init)

abstract infix fun <R> KProperty1<T, R?>.ifPresent(init: ValidationBuilder<R>.() -> Unit)

abstract infix fun <R> KProperty1<T, R?>.required(init: ValidationBuilder<R>.() -> Unit)

abstract fun run(validation: Validation<T>)

abstract val <R> KProperty1<T, R>.has: ValidationBuilder<R>
}

Expand Down
19 changes: 12 additions & 7 deletions src/commonMain/kotlin/io/konform/validation/ValidationResult.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface ValidationError {

internal data class PropertyValidationError(
override val dataPath: String,
override val message: String
override val message: String,
) : ValidationError {
override fun toString(): String {
return "ValidationError(dataPath=$dataPath, message=$message)"
Expand All @@ -19,6 +19,7 @@ internal data class PropertyValidationError(
interface ValidationErrors : List<ValidationError>

internal object NoValidationErrors : ValidationErrors, List<ValidationError> by emptyList()

internal class DefaultValidationErrors(private val errors: List<ValidationError>) : ValidationErrors, List<ValidationError> by errors {
override fun toString(): String {
return errors.toString()
Expand All @@ -27,15 +28,17 @@ internal class DefaultValidationErrors(private val errors: List<ValidationError>

sealed class ValidationResult<out T> {
abstract operator fun get(vararg propertyPath: Any): List<String>?

abstract fun <R> map(transform: (T) -> R): ValidationResult<R>

abstract val errors: ValidationErrors
}

data class Invalid<T>(
internal val internalErrors: Map<String, List<String>>) : ValidationResult<T>() {
internal val internalErrors: Map<String, List<String>>,
) : ValidationResult<T>() {
override fun get(vararg propertyPath: Any): List<String>? = internalErrors[propertyPath.joinToString("", transform = ::toPathSegment)]

override fun get(vararg propertyPath: Any): List<String>? =
internalErrors[propertyPath.joinToString("", transform = ::toPathSegment)]
override fun <R> map(transform: (T) -> R): ValidationResult<R> = Invalid(this.internalErrors)

private fun toPathSegment(it: Any): String {
Expand All @@ -48,20 +51,22 @@ data class Invalid<T>(

override val errors: ValidationErrors by lazy {
DefaultValidationErrors(
internalErrors.flatMap { (path, errors ) ->
internalErrors.flatMap { (path, errors) ->
errors.map { PropertyValidationError(path, it) }
}
},
)
}

override fun toString(): String {
return "Invalid(errors=${errors})"
return "Invalid(errors=$errors)"
}
}

data class Valid<T>(val value: T) : ValidationResult<T>() {
override fun get(vararg propertyPath: Any): List<String>? = null

override fun <R> map(transform: (T) -> R): ValidationResult<R> = Valid(transform(this.value))

override val errors: ValidationErrors
get() = DefaultValidationErrors(emptyList())
}
75 changes: 43 additions & 32 deletions src/commonMain/kotlin/io/konform/validation/internal/Validation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,29 @@ import io.konform.validation.Validation
import io.konform.validation.ValidationResult
import kotlin.reflect.KProperty1

internal class OptionalValidation<T: Any>(
private val validation: Validation<T>
internal class OptionalValidation<T : Any>(
private val validation: Validation<T>,
) : Validation<T?> {
override fun validate(value: T?): ValidationResult<T?> {
val nonNullValue = value ?: return Valid(value)
return validation(nonNullValue)
}
}

internal class RequiredValidation<T: Any>(
private val validation: Validation<T>
internal class RequiredValidation<T : Any>(
private val validation: Validation<T>,
) : Validation<T?> {
override fun validate(value: T?): ValidationResult<T?> {
val nonNullValue = value
?: return Invalid(mapOf("" to listOf("is required")))
val nonNullValue =
value
?: return Invalid(mapOf("" to listOf("is required")))
return validation(nonNullValue)
}
}

internal class NonNullPropertyValidation<T, R>(
private val property: KProperty1<T, R>,
private val validation: Validation<R>
private val validation: Validation<R>,
) : Validation<T> {
override fun validate(value: T): ValidationResult<T> {
val propertyValue = property(value)
Expand All @@ -38,7 +39,7 @@ internal class NonNullPropertyValidation<T, R>(

internal class OptionalPropertyValidation<T, R>(
private val property: KProperty1<T, R?>,
private val validation: Validation<R>
private val validation: Validation<R>,
) : Validation<T> {
override fun validate(value: T): ValidationResult<T> {
val propertyValue = property(value) ?: return Valid(value)
Expand All @@ -48,54 +49,52 @@ internal class OptionalPropertyValidation<T, R>(

internal class RequiredPropertyValidation<T, R>(
private val property: KProperty1<T, R?>,
private val validation: Validation<R>
private val validation: Validation<R>,
) : Validation<T> {
override fun validate(value: T): ValidationResult<T> {
val propertyValue = property(value)
?: return Invalid<T>(mapOf(".${property.name}" to listOf("is required")))
return validation(propertyValue).mapError { ".${property.name}${it}" }.map { value }
val propertyValue =
property(value)
?: return Invalid<T>(mapOf(".${property.name}" to listOf("is required")))
return validation(propertyValue).mapError { ".${property.name}$it" }.map { value }
}
}

internal class IterableValidation<T>(
private val validation: Validation<T>
private val validation: Validation<T>,
) : Validation<Iterable<T>> {
override fun validate(value: Iterable<T>): ValidationResult<Iterable<T>> {
return value.foldIndexed(Valid(value)) { index, result: ValidationResult<Iterable<T>>, propertyValue ->
val propertyValidation = validation(propertyValue).mapError { "[$index]$it" }.map { value }
result.combineWith(propertyValidation)
}

}
}

internal class ArrayValidation<T>(
private val validation: Validation<T>
private val validation: Validation<T>,
) : Validation<Array<T>> {
override fun validate(value: Array<T>): ValidationResult<Array<T>> {
return value.foldIndexed(Valid(value)) { index, result: ValidationResult<Array<T>>, propertyValue ->
val propertyValidation = validation(propertyValue).mapError { "[$index]$it" }.map { value }
result.combineWith(propertyValidation)
}

}
}

internal class MapValidation<K, V>(
private val validation: Validation<Map.Entry<K, V>>
private val validation: Validation<Map.Entry<K, V>>,
) : Validation<Map<K, V>> {
override fun validate(value: Map<K, V>): ValidationResult<Map<K, V>> {
return value.asSequence().fold(Valid(value)) { result: ValidationResult<Map<K, V>>, entry ->
val propertyValidation = validation(entry).mapError { ".${entry.key.toString()}${it.removePrefix(".value")}" }.map { value }
val propertyValidation = validation(entry).mapError { ".${entry.key}${it.removePrefix(".value")}" }.map { value }
result.combineWith(propertyValidation)
}

}
}

internal class ValidationNode<T>(
private val constraints: List<Constraint<T>>,
private val subValidations: List<Validation<T>>
private val subValidations: List<Validation<T>>,
) : Validation<T> {
override fun validate(value: T): ValidationResult<T> {
val subValidationResult = applySubValidations(value, keyTransform = { it })
Expand All @@ -116,13 +115,19 @@ internal class ValidationNode<T>(
}
}

private fun constructHint(value: T, it: Constraint<T>): String {
private fun constructHint(
value: T,
it: Constraint<T>,
): String {
val replaceValue = it.hint.replace("{value}", value.toString())
return it.templateValues
.foldIndexed(replaceValue) { index, hint, templateValue -> hint.replace("{$index}", templateValue) }
}

private fun applySubValidations(propertyValue: T, keyTransform: (String) -> String): ValidationResult<T> {
private fun applySubValidations(
propertyValue: T,
keyTransform: (String) -> String,
): ValidationResult<T> {
return subValidations.fold(Valid(propertyValue)) { existingValidation: ValidationResult<T>, validation ->
val newValidation = validation.validate(propertyValue).mapError(keyTransform)
existingValidation.combineWith(newValidation)
Expand All @@ -133,22 +138,28 @@ internal class ValidationNode<T>(
internal fun <R> ValidationResult<R>.mapError(keyTransform: (String) -> String): ValidationResult<R> {
return when (this) {
is Valid -> this
is Invalid -> Invalid(this.internalErrors.mapKeys { (key, _) ->
keyTransform(key)
})
is Invalid ->
Invalid(
this.internalErrors.mapKeys { (key, _) ->
keyTransform(key)
},
)
}
}

internal fun <R> ValidationResult<R>.combineWith(other: ValidationResult<R>): ValidationResult<R> {
return when (this) {
is Valid -> return other
is Invalid -> when (other) {
is Valid -> this
is Invalid -> {
Invalid((this.internalErrors.toList() + other.internalErrors.toList())
.groupBy({ it.first }, { it.second })
.mapValues { (_, values) -> values.flatten() })
is Invalid ->
when (other) {
is Valid -> this
is Invalid -> {
Invalid(
(this.internalErrors.toList() + other.internalErrors.toList())
.groupBy({ it.first }, { it.second })
.mapValues { (_, values) -> values.flatten() },
)
}
}
}
}
}
Loading

0 comments on commit da9f26c

Please sign in to comment.