All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.konform.validation.internal.Validation.kt Maven / Gradle / Ivy

package io.konform.validation.internal

import io.konform.validation.Constraint
import io.konform.validation.Invalid
import io.konform.validation.Valid
import io.konform.validation.Validation
import io.konform.validation.ValidationResult
import kotlin.reflect.KProperty1

internal class NonNullPropertyValidation(
    private val property: KProperty1,
    private val validation: Validation
) : Validation {
    override fun validate(value: T): ValidationResult {
        val propertyValue = property(value)
        return validation(propertyValue).mapError { ".${property.name}$it" }.map { value }
    }
}

internal class OptionalPropertyValidation(
    private val property: KProperty1,
    private val validation: Validation
) : Validation {
    override fun validate(value: T): ValidationResult {
        val propertyValue = property(value) ?: return Valid(value)
        return validation(propertyValue).mapError { ".${property.name}$it" }.map { value }
    }
}

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

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

    }
}

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

    }
}

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

    }
}

internal class ValidationNode(
    private val constraints: List>,
    private val subValidations: List>
) : Validation {
    override fun validate(value: T): ValidationResult {
        val subValidationResult = applySubValidations(value, keyTransform = { it })
        val localValidationResult = localValidation(value)
        return localValidationResult.combineWith(subValidationResult)
    }

    private fun localValidation(value: T): ValidationResult {
        return constraints
            .filter { !it.test(value) }
            .map { constructHint(value, it) }
            .let { errors ->
                if (errors.isEmpty()) {
                    Valid(value)
                } else {
                    Invalid(mapOf("" to errors))
                }
            }
    }

    private fun constructHint(value: T, it: Constraint): 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 {
        return subValidations.fold(Valid(propertyValue)) { existingValidation: ValidationResult, validation ->
            val newValidation = validation.validate(propertyValue).mapError(keyTransform)
            existingValidation.combineWith(newValidation)
        }
    }
}

internal fun  ValidationResult.mapError(keyTransform: (String) -> String): ValidationResult {
    return when (this) {
        is Valid -> this
        is Invalid -> Invalid(this.internalErrors.mapKeys { (key, _) ->
            keyTransform(key)
        })
    }
}

internal fun  ValidationResult.combineWith(other: ValidationResult): ValidationResult {
    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() })
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy